Broadleaf Microservices
  • v1.0.0-latest-prod

Frontend Integration

Note
If upgrading from PayPal Checkout JavaScript SDK v1, check out PayPal’s upgrade guide. NOTE: If upgrading from PayPal Checkout JavaScript SDK v5, check out PayPal’s upgrade guide.

Broadleaf provides a PayPal Checkout SDK for common interactions with the PayPal JavaScript SDK.

PayPal Script Rendering

To use the SDK in your frontend, you will need to handle several interactions. First, you’ll need to include the paypal sdk script during render at the top of your page, or in the App component of your application.

Placing the script in your App component will allow you to render and configure the script with the relevant options, such as page type. PayPal requires that configuration of the PayPal JS SDK uses relevant query parameters, including partnerAttributionId, clientId, and merchantId. PayPal recommends passing the pageType for the context of the page (i.e. cart, payment, etc.), and components to load buttons, paylater messaging, etc. to meet Best Practices. Other options are also available to configure and personalize your checkout experience with PayPal, and can be found on the PayPal JavaScript SDK v6 configuration page.

For example,

...
<PayPalProvider
      clientId={PayPalConfig.CLIENT_ID}
      components={['paypal-payments', 'venmo-payments', 'paypal-messages']}
      pageType={pageType}
      merchantId={PayPalConfig.MERCHANT_ID}
      partnerAttributionId="BroadleafCommerce_SP_PPCP"
    >
      {children}
    </PayPalProvider>
...

or

{PayPalConfig.CLIENT_ID && (
  <script src="https://www.paypal.com/web-sdk/v6/core"></script>
  ...
  const sdkInstance = await window.paypal.createInstance({
      clientId:{PayPalConfig.CLIENT_ID},
      components:['paypal-payments', 'venmo-payments', 'paypal-messages'],
      pageType:{pageType},
      merchantId={PayPalConfig.MERCHANT_ID},
      partnerAttributionId="BroadleafCommerce_SP_PPCP",
    });
)}
For PayPal SDK v5:

PayPal requires that configuration of the PayPal JS SDK uses relevant query parameters, including dataPartnerAttributionId, clientId, merchantId, commit, intent, and currency (defaults to USD, specify if using another currency). PayPal recommends passing the enableFunding to enable venmo and paylater payment methods, dataPageType for the context of the page (i.e. cart, payment, etc.), and components to load buttons, paylater messaging, etc. as options to meet Best Practices.

const payPalOptions = {
  clientId: PayPalConfig.CLIENT_ID,
  merchantId: PayPalConfig.MERCHANT_ID,
  commit: true,
  enableFunding: ['venmo', 'paylater'],
  disableFunding: ['card'],
  intent: 'authorize',
  components: ['buttons', 'messages'],
  dataCspNonce: nonce,
  dataPageType: pageType,
  dataPartnerAttributionId: 'BroadleafCommerce_SP_PPCP',
};
...
<PayPalScriptProvider options={payPalOptions}>
  <CurrentLayout>
    <Component {...pageProps} />
  </CurrentLayout>
</PayPalScriptProvider>

or

{PayPalConfig.CLIENT_ID && (
  <script
    data-partner-attribution-id="BroadleafCommerce_SP_PPCP"
    data-csp-nonce={this.props.nonce}
    defer
    src={`https://www.paypal.com/sdk/js?client-id=${PayPalConfig.CLIENT_ID}&merchant-id=${PayPalConfig.MERCHANT_ID}&intent=authorize&commit=true`}
  />
)}

PayPal Button Component Integration

Next, you’ll want to define the PayPal button(s) on your site.

  1. Identify the eligible payment methods from PayPal and render the appropriate buttons within your PDP, cart, or checkout page.

  2. Provide callback functions for createOrder, onApprove, and onError.

    1. For details on createOrder and handleApprove, see Handle Create Order and Handle Approve Order.

    2. If using the Paypal button flow for Payment only, the handleApprove function should be responsible for calling PaymentTransactionServices to create & persist the payment.

      1. In this flow, we render the buttons on the payment stage of checkout, alongside the credit card form & other available payment options.

    3. If using the Complete Payment Paypal button flow, the handleApprove function should be responsible for updating the contact & fulfillment information (i.e. shipping address), and then calling PaymentTransactionServices to create & persist the payment. You may also want to skip the review page and submit the checkout at this stage.

      1. The expectation in the Complete Payment flow is that the checkout process is reduced to the minimum interactions for the customer. To achieve this, we use the shipping address selected within PayPal to populate our shipping & fulfillment information so that the customer can immediately enter checkout. We also support the server-side shipping callbacks from PayPal to save the initial fulfillment address and provide shipping options to the customer within the Complete Payments page.

Below is an example of using the PayPal button(s) as a component within a React TypeScript application.

const { eligiblePaymentMethods } = useEligibleMethods({
    payload: { currencyCode: 'USD', paymentFlow: 'ONE_TIME_PAYMENT' },
  });

const isPayPalEligible = eligiblePaymentMethods?.isEligible('paypal');
const isVenmoEligible = eligiblePaymentMethods?.isEligible('venmo');
const isPayLaterEligible = eligiblePaymentMethods?.isEligible('paylater');

...

<div className={classStyle}>
    {isPayPalEligible && (
      <PayPalOneTimePaymentButton
        createOrder={createPayPalOrderV6}
        onApprove={handleApprove}
        onError={handleError}
        type={style.label}
      />
    )}
    {isPayLaterEligible && (
      <PayLaterOneTimePaymentButton
        createOrder={createPayPalOrderV6}
        onApprove={handleApprove}
        onError={handleError}
      />
    )}
    {isVenmoEligible && (
      <VenmoOneTimePaymentButton
        createOrder={createVenmoOrderV6}
        onApprove={handleApprove}
        onError={handleError}
      />
    )}
</div>
For PayPal SDK v5:
export const usePayPal = (): PayPalState => {
  const [state, setState] = useState<PayPalState>(
    isPayPalLoaded()
      ? { api: window['paypal'], button: getButtonComponent() }
      : {}
  );

  useEffect(() => {
    setState(
      isPayPalLoaded()
        ? { api: window['paypal'], button: getButtonComponent() }
        : {}
    );
  }, []);

  return state;
};

function isPayPalLoaded() {
  return window && window['paypal'];
}

function getButtonComponent() {
  return window['paypal'].Buttons.driver('react', { React, ReactDOM });
}
const { api, button: Button } = usePayPal();

return (
    <Button
      appSwitchWhenAvailable={true}
      fundingSource={api.FUNDING[funding]}
      funding={api.FUNDING[funding]}
      createOrder={createOrder}
      onApprove={handleApprove}
      onError={error => {
        if (error.message === 'Expected an order id to be passed') {
          // don't want to show this message
          return;
        }

        setError(error.message);
      }}
      style={style}
    />
)

Building the payload to create the order in PayPal

When interacting with PayPal to create the order via their order create api, we have to consider how to map the data in our system over to a PayPal Order.

PayPal Create Order Request

  1. intent [required]

    1. Type: ENUM string

    2. This can either be set to AUTHORIZE or CAPTURE, which determines which transaction type is executed in our checkout workflow.

  2. purchase_units [required]

    1. Type: Array of purchase unit requests

    2. Each purchase unit is a list of data points that correlates to a payment in Broadleaf.

      1. amount: This is equivalent to the payment amount in Broadleaf. It can be broken down to include totals for items, discounts, shipping, and taxes.

      2. custom_id: This is an external id which Broadleaf populates with the {fulfillmentReference}-{currentTimestamp} by default. This is used by our system as the transaction reference id, which allows us to reference the transaction if we do not receive a response back from the payment gateway during transaction execution.

      3. items: The list of cart items for the order, populated by cart items on Broadleaf’s cart representation.

      4. shipping: The shipping address for the order. By default, this is populated from Broadleaf’s shipping address stored on the fulfillment group. Sending this field to PayPal is required for Complete Payments flow, but optional otherwise.

      5. Refer to PayPal docs for additional fields on purchase unit request which can be populated as your implementation requires.

Venmo vs PayPal Payment Source

In addition to the properties listed on the request above, you’ll also need to populate the payment_source. This varies slightly based on the funding source chosen - i.e. PayPal vs Venmo.

PayPal Request

  1. payment_source

    1. paypal

      1. experience_context: Customizes the payer experience during the approval process for payment with PayPal.

        1. locale: The locale for this request.

        2. shipping_preference

          1. Type: ENUM string

          2. The location from which the shipping address is derived.

        3. contact_preference

          1. Type: ENUM string

          2. The location from which the contact information is derived.

        4. user_action: Configures a Continue or Pay Now checkout flow.

        5. return_url: The URL where the customer is redirected after the customer approves the payment.

        6. cancel_url: The URL where the customer is redirected after the customer cancels the payment.

        7. app_switch_preference: Customizes the payer experience to support app switching.

          1. launch_paypal_app: Whether or not to support launching the PayPal App, if available.

        8. order_update_callback_config: See below section on server-side shipping callback config.

App Switch Config

Of note in the PayPal experience context, there are several properties that can be set for switching to the PayPal App.

  1. app_switch_preference: This can customize whether or not to enable app switching if it’s available.

  2. return_url and cancel_url: These are the URLs to configure where to return when enabling App Switch, so make sure these are set if launch_paypal_app is enabled.

More information on App Switch & setup can be found in the PayPal App Switch docs.

Venmo Request

  1. payment_source

    1. venmo

      1. experience_context: Customizes the payer experience during the approval process for payment with Venmo.

        1. shipping_preference

          1. Type: ENUM string

          2. The location from which the shipping address is derived.

        2. order_update_callback_config: See below section on server-side shipping callback config.

Server-Side Shipping Callback Config

Within the PayPal or Venmo request if you’re also supporting sever-side shipping callbacks, you’ll want to build out the following config:

  1. order_update_callback_config: The callback configuration for fulfillment address & fulfillment options. Should only be included if using server-side shipping callbacks & when the shipping_preference is `GET_FROM_FILE.

    1. callback_events: A list of events to be supported by the shipping callback.

    2. callback_url: The URL to call for the shipping callback.

More information on the Shipping Callbacks can be found at PayPal’s Shipping Module documentation.

Enabling PayLater Messaging

PayPal supports & encourages use of messaging for their PayLater funding source. To enable PayLater messaging, you can add paypal-messages to the list of components when setting the options in the PayPal script. Then, you just need to include the paypal-message component with amount and currency parameters.

For example,

export const PayPalMessaging: FC<PayPalMessagingType> = ({
  amount,
  currency,
}) => {
  const { error, isReady } = usePayPalMessages({});

  if (error || !isReady) {
    return null;
  }

  return (
    <paypal-message
      auto-bootstrap={true}
      amount={amount}
      currency-code={currency}
    />
  );
};
For PayPal SDK v5:
<div
  className="mb-2"
  data-pp-message
  data-pp-style-layout="text"
  data-pp-style-logo-type="inline"
  data-pp-style-text-color="black"
  data-pp-style-text-size="12"
  data-pp-amount={cart?.cartPricing?.total?.amount}
  data-pp-placement="payment"
></div>

For a complete reference of the options available to integrate and customize the PayLater messaging, see the PayLater messaging documentation.