PayPal Donate button in React: Donate SDK or Checkout SDK integration?

Viewed 761

The official NPM package @paypal/react-paypal-js: https://www.npmjs.com/package/@paypal/react-paypal-js only supports buttons that require client_id, I have not been able to find anything that simplifies the implementation of Donate button. PayPal provides only this HTML code below on the Donate button documentation. Please let me know if anyone has had a similar experience and how did you manage to implement it properly in React/Gatsby.

<head>
 <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- Ensures optimal rendering on mobile devices. -->
 <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <!-- Optimal Internet Explorer compatibility -->
</head>

<body>
 <script src="https://www.paypalobjects.com/donate/sdk/donate-sdk.js" charset="UTF-8"></script>
</body>

<body>
 <div id="paypal-donate-button-container"></div>

  <script>
   PayPal.Donation.Button({
       env: 'sandbox',
       hosted_button_id: 'YOUR_SANDBOX_HOSTED_BUTTON_ID',
       // business: 'YOUR_EMAIL_OR_PAYERID',
       image: {
           src: 'https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif',
           title: 'PayPal - The safer, easier way to pay online!',
           alt: 'Donate with PayPal button'
       },
       onComplete: function (params) {
           // Your onComplete handler
       },
   }).render('#paypal-donate-button-container');
</script>
</body>

3 Answers

There are two ways to use the Donate SDK HTML/JS from react.

  1. Configure the app to load the SDK script at page load time (typically as part of the page's <head>). The JS to render the button can then be run/outputted whenever as part of a component.

  2. Insert the script element into the DOM after page load and once loaded render buttons with a callback function. This can all be placed in a React component's useEffect or similar. The linked example using loadAsync is for the Checkout SDK, but that's easily swapped out for Donate SDK code.


You can use the Donate SDK with a hosted_button_id as you mentioned (created at https://www.paypal.com/buttons (live) or www.sandbox.paypal.com/buttons) -- or alternatively, instead of a hosted button id just pass a business parameter with either the receiving account's PayPal email address or (ideally) its PayPal Merchant ID (since that will never change whereas an email can be removed from the account and no longer point to it)


Later edit: Note that the Donate SDK is separate from simply relabeling a normal PayPal Checkout SDK button with the text "Donate", which can be done per the react-paypal-js storybook example, as another answer mentioned. The issue with this approach is after clicking the button it will look like a standard PayPal checkout, not a donation-specific flow...

Using the actual Donate SDK instead results in a more tailored donor flow where they can select any amount in the large input, and even check an option to make their donation recurring:

I reached out to the React SDK team and they said it actually comes with support for donate out of the box and there is an example in their storybook. Here's the relevant code. If you use this approach you shouldn't need hosted_button_id:

<PayPalButtons
  fundingSource="paypal"
  style={{
    layout: "vertical",
    label: "donate"
  }}
  createOrder={(data, actions) => {
    return actions.order
      .create({
        purchase_units: [{
            amount: {
              value: "2",
              currency_code: "USD",
            },
            items: [{
                name: "donation-example",
                quantity: "1",
                unit_amount: {
                  currency_code: "USD",
                  value: "2",
                },
                category: "DONATION",
            }],
        }],
      })
  }}
/>;

I had the same problem and I didn't see how exactly the accepted answer here could work with React because of the reference error: PayPal.Donation.Button is undefined, and the code wouldn't compile if I tried using it as a function.

I looked into appending the whole script tag as a string (dangerouslySetInnerHTML) and found this blog post to be a great explanation: Render dangerous content with React. The author makes a pretty cool use of document.Range API in order to solve the problem of executing scripts inside html. He has also published his solution as a small package: dangerously-set-html-content.

I gave it a try in my Gatsby project. First thing I'm adding the Donate SDK script tag on the body in gatsby-srr.js in order to fetch the JapaScript on page load:

exports.onRenderBody = ({setPreBodyComponents}) => {
    setPreBodyComponents(
        <script key={0} src="https://www.paypalobjects.com/donate/sdk/donate-sdk.js" charSet="UTF-8"/>
    )
}

(I need the bizarre-looking key attribute to avoid errors when html.js is compiled. Once it's merged, the attribute is removed)

And then in the component I'm setting the script as a string:

import React from 'react'
import InnerHTML from 'dangerously-set-html-content'
const html = `
    <div id="donate-button-container">
        <div id="donate-button">
            <script>
                PayPal.Donation.Button({
                    ...
                }}).render('#donate-button')
            </script>
        </div>
    </div>
`
function MyComponent() {
    return (
       <InnerHTML html={html} />
    )
}

You can have a look at the GitHub repo of the package for the explanation of how useEffect and useRef hooks are used, and for some usage examples.

Related