Multiple buttons on a page - order and subscription

benmacleod
Contributor
Contributor

We're a not-for-profit offering our donors the ability to donate via PayPal. Currently we're using PayPal buttons with a server-side integration with the old Ruby SDK. If the donor selects a once-off donation, we create a payment, if they select a monthly donation, we create a billing agreement.  Either way, the process is triggered by the same `createOrder` callback on the PayPal button.

 

We're trying to upgrade to the latest version of the PayPal buttons integration where the transaction is completed on the client side. We think that this means we'll need to use the basic order integration for once off donations with `createOrder` - https://developer.paypal.com/docs/checkout/integrate/#4-set-up-the-transaction and the subscription integration for monthly donations with `createSubscription` - https://developer.paypal.com/docs/subscriptions/integrate/#4-create-a-subscription.

 

We'd like to be able to include both these options on one page, switching between the two based on what the user selects, but it doesn't seem to be possible, because each option requires us to load the JS SDK with different parameters. The `createOrder` version (implicitly) requires loading the SDK with `intent=capture`, whilst the `createSubscription` version requires `intent=subscription&vault=true`. I've investigated the option of loading the SDK twice on the one page with different parameters (inefficient, I know), using the `data-namespace` attribute to differentiate them, but the PayPal server responded with a 400 when we tried to load the script the second time:

 

<script src="https://www.paypal.com/sdk/js?client-id=BLAH&amp;currency=AUD"></script>

<script src="https://www.paypal.com/sdk/js?client-id=BLAH&amp;currency=AUD&amp;vault=true&amp;intent=subscription..." data-namespace="paypal_subscriptions"></script>

 

`window.paypal` was defined, but `window.paypal_subscriptions` was not.

 

If I can't load the first SDK on the page, I don't think I can put a `createOrder` callback on the PayPal button. If I can't load the second SDK, I don't think I can put a `createSubscription` callback on the button.

 

Does anybody have any expertise to offer as to how to combine two types of button on the same page like this?

Login to Me Too
6 REPLIES 6

benmacleod
Contributor
Contributor

I notice I've made an error in the second tag, where I had `intent=subscriptions`, instead of `intent=subscription`. I fixed this and the script still loads, but it doesn't populate `window.paypal_subscriptions`, so I'm not sure how to use the 2 "different" SDKs (if I need to).

Login to Me Too

lkatkus
Contributor
Contributor

Did you manage to solve this? I'm having the same situation.

Login to Me Too

lkatkus
Contributor
Contributor

I was able to solve my issue with paypal-js as recommended in https://github.com/paypal/paypal-checkout-components/issues/1463

Login to Me Too

VFI1
Contributor
Contributor

Thanks

a bit more complicated than I can do. Is there an more straightforward way?

Login to Me Too

RJ404
Contributor
Contributor

thanks for link, your safed my day.

if someone need a Typescript React example for switching between subscriptions and orders 

import { useEffect } from 'react'
import { loadScript } from "@paypal/paypal-js";
import {PayPalButtonsComponentOptions, OnApproveData, OnApproveActions, CreateSubscriptionActions, CreateOrderActions } from "@paypal/paypal-js/types/components/buttons"

interface Props {
    onApprove: (order: any) => void;
    onClick: () => void;
    type: 'subscription' | 'order'
}

export default function PayPalComponent(props: Props) {
    const components = "buttons";
    let buttons;

    useEffect(() => {
        loadAndRender(); 
    });


    function render(options: PayPalButtonsComponentOptions ) {
        if(window.paypal && window.paypal.Buttons){
            buttons = window.paypal.Buttons(options);
            buttons.render("#paypal-button-container").catch((err: any) => {
              console.warn(
                "Warning - Caught an error when attempting to render component",
                err
              );
            });
        }
      }

    function loadAndRender(transactionType = props.type) {
        const clientId = "YOUCLIENTIDHERE"
        function createOrder(data: any, actions: CreateOrderActions)  {
            return actions.order.create({
                purchase_units: [
                    {
                        amount: {
                            value: "0.01",
                        },
                    },
                ],
            });
        };
        function createSubscription(data: any, actions: CreateSubscriptionActions) {
            return actions.subscription.create({
                'plan_id': 'P-YOURESUBIDHERE'
              });
        }

        async function onApproveOrder(data: OnApproveData, actions: OnApproveActions) {
            const order = await actions.order.capture();
            props.onApprove(order)
        };
        async function onApproveSubsciption(data: OnApproveData, actions: OnApproveActions) {
            props.onApprove(data)
        };

        if (transactionType === "order") {
            loadScript({
            "client-id": clientId,
            vault: false,
            components
            })
            .then(() => {
            render({
                onApprove: onApproveOrder,
                createOrder
            });
            });
        } else {
            loadScript({
            "client-id": clientId,
            vault: true,
            intent: "subscription",
            components
            })
            .then(() => {
            render({
                style: {
                shape: "rect",
                color: "gold",
                layout: "vertical",
                label: "subscribe" as any
                },
                onApprove: onApproveSubsciption,
                createSubscription
            });
            });
        }
    }

///how to use         <PayPalComponent type="subscription" onApprove={saveOrder} onClick={() => undefined}></PayPalComponent>
    return (
        <div id='paypal-button-container'></div>
    );
}

the  

Login to Me Too

alemnunez
New Community Member

Hi Ben! Your idea about the `data-namespace` got me on the right direction! I had to change many things to make it work as expected but finally got to have buttons for a subscription and a one-time payment/capture on the same page at the same time.

I wrote a stackoverflow question and answer explaining all the details:

https://stackoverflow.com/questions/69024268/how-can-i-show-a-paypal-smart-subscription-button-and-a...

Login to Me Too

Haven't Found your Answer?

It happens. Hit the "Login to Ask the community" button to create a question for the PayPal community.