Antonello Zanini I'm a software engineer, but I prefer to call myself a technology bishop. Spreading knowledge through writing is my mission.

How to integrate a Braintree payment system in React

8 min read 2510

Braintree Payment System React

Paying through a website to buy a service or product has become increasingly common. Consequently, being able to implement this feature is more important than ever. Thankfully, several payments platforms have been released to help developers, so you can avoid struggling with the most common problems related to payments.

Braintree is one such platform, and it promises to make it easy to accept payments in any app or website. Particularly, their APIs, libraries, and tools can be used to implement a payments system effortlessly.

In this article, you will learn how to integrate Braintree in your React app by employing the official Braintree JavaScript client SDK. Follow this tutorial to learn how to achieve the following result:

react-braintree-drop-in-demo

No Description

Prerequisites

This is the list of all the prerequisites for the demo application you are going to build:

Braintree JavaScript SDK

As stated in the official documentation, the Braintree JavaScript SDK allows you to easily accept payments while maintaining PCI (Payment Card Industry) compliance. In other terms, their SDK lets you implement a complete payments system while automatically ensuring the security of credit card transactions and data as required by credit card companies.

The Braintree JavaScript client SDK supports payments from credit cards, PayPal, digital wallets, and several other payment methods. You can find the complete list of supported payment methods here. Each of these payment methods can be configured and integrated with no effort.

To install the Braintree JavaScript SDK, launch the following npm command:

npm install --save braintree-web

This way, the braintree-web npm library will be added to your project’s dependencies. If you are not an npm user, you can find all the other supported ways to install the SDK here.

After installing it, you have to learn how to use it. In detail, what you need to know is that Braintree comes with two different integrations: Hosted Fields and Drop-in. The first one offers all the tools required to implement a custom UI, while the second one offers a ready-to-use, preformatted payment form with an elegant UI, and it represents the recommended approach.

We made a custom demo for .
No really. Click here to check it out.

In both cases, you need to retrieve an authorization token from Braintree. How to achieve this will be addressed later in the tutorial. This information is required by Braintree to authorize your frontend application.

Then, after completing the procedure to enter the data necessary to make the payment, a Braintree payment method nonce will be generated. This represents a secure and one-time-use nonce referencing the payment information. Such a nonce should be sent to your server and used to perform the actual payment by harnessing the Braintree server SDK.

In other words, the payment method nonce generated by the Braintree frontend UI allows your server to communicate sensitive payment information to the Braintree servers and perform the operation without exposing sensitive data.

You can see a simplified diagram of the Braintree payment flow below. Also, you can read this article from the official documentation to learn more about how Braintree works.

Braintree Payment Flow
The Braintree payment flow

Now, let’s delve into both frontend approaches offered by Braintree.

Hosted Fields

The Braintree Hosted Fields approach allows merchants to deal with the SAQ A (Self-Assessment Questionnaire A) eligibility while being in control over the styling and layout of their payment form.

Specifically, the Hosted Fields solution automatically renders an iframe containing the following payment fields: card number, CVV, expiration date, and postal code. This is because to meet the basic level of PCI compliance, you must host the information on an external payment gateway’s domain and present those five fields to your users in an iframe or with a redirect.

So, this approach allows you to fully customize your payment form while ensuring that you are compliant with PCI requirements. At the same time, Hosted Fields involve custom logic. This is especially true if you want to implement other payment methods, such as PayPal. This is why this solution is not the most widely used and should be taken into account only when being in full control over logic and style is required.

You can find a few examples of custom Hosted Fields integrations here.

Drop-in

The Braintree Drop-in UI is a complete, off-the-shelf payment UI offering a straightforward way to accept payments securely. By default, the interface includes a card entry form asking for a credit card number, cardholder name, expiration date, postal code, and CVV.

Plus, you can enable PayPal, Apple Pay, Google Pay, and Venmo buttons through special configurations. Follow this link to see what the default Braintree Drop-in UI looks like.

To use it, you need to launch the following npm command to add the required dependency to your project:

npm install --save braintree-web-drop-in

This way, the braintree-web-drop-in will be installed. This premade payments UI was built using the aforementioned Braintree JS client SDK. As a result, braintree-web-drop-in depends on braintree-web. So, by adding braintree-web-drop-in to your dependencies, the braintree-web library will be automatically installed. Thus, when using the braintree-web-drop-in library, you can omit braintree-web in your project’s dependencies.

As stated in the official documentation, the Braintree Drop-in is the quickest and recommended approach to start accepting payments when using Braintree. This is because the default Drop-in UI qualifies you for SAQ A PCI compliance, natively supports 23 languages (and gives you the possibility to integrate your own language), supports the 3D Secure 2 verification, and allows users to delete saved payment methods. All this in a ready-to-use solution requiring only a bunch of lines of code to be implemented.

Hosted Fields vs. Drop-in

Here you can see an overview of the principal differences between the two approaches presented above.

Hosted Fields

  • It is SAQ A PCI compliant
  • It allows you to create a payment form with custom style and layout
  • It allows you to fully customize the behavior of your checkout
  • It requires you to manually integrate the PayPal Checkout component to accept PayPal

Drop-in

  • It is SAQ A PCI compliant
  • It allows you to integrate Braintree quickly and easily
  • It supports 23 languages
  • It natively supports PayPal, Apple Pay, Google Pay, and Venmo buttons
  • You can customize it by overwriting its CSS rules, but does not give you full control

Using braintree-drop-in in React

You can clone the GitHub repository supporting this article and try the demo application by launching the following commands:

git clone https://github.com/Tonel/react-braintree-drop-in-demo.git
cd react-braintree-drop-in-demo
npm i
npm start

Otherwise, you can keep following this step-by-step tutorial.

Initializing a React Project

You can easily initialize a React project is by using Create React App. This is the officially supported way to create a working, single-page React application. In particular, you can create a new project called react-braintree-drop-in-demo with the following command:

npx create-react-app react-braintree-drop-in-demo

Now, you will be able to explore a demo project located in the react-brantree-drop-in-demo folder having the following file structure:

react-braintree-drop-in-demo
├── README.md
├── node_modules
├── package.json
├── .gitignore
├── public
│   ├── favicon.ico
│   ├── index.html
│   ├── logo192.png
│   ├── logo512.png
│   ├── manifest.json
│   └── robots.txt
└── src
    ├── App.css
    ├── App.js
    ├── App.test.js
    ├── index.css
    ├── index.js
    ├── logo.svg
    ├── reportWebVitals.js
    └── setupTests.js

Then, enter the react-brantree-drop-in-demo folder and start the local server to check if everything is working by running this command:

cd react-braintree-drop-in-demo
npm start

Reach the http://localhost:3000/ page in your browser, and you should now be seeing the default Create React App screen, as follows:

Create React App Localhost

Setting up Braintree

As explained in the official documentation, there are two different ways to let Braintree authorize your frontend application.

The first one involves a client token, which must be generated by your server by using the Braintree server SDK. This gives you access to all the Braintree client API capabilities.

The second one is a tokenization key, which is a static key authorizing only a limited subset of the Braintree client API features. For the sake of simplicity, a tokenization key will be used to authorize the Braintree Drop-in.

You can retrieve a valid tokenization key in your Braintree Control Panel by following this procedure:

  1. Log into the Braintree Sandbox Control Panel if you already subscribed. Otherwise, sign up and then log in

  2. Click on the gear icon you can find in the upper right corner

  3. Select the API option from the drop-down menu

Braintree Control Panel API

  1. Scroll down to the Tokenization Keys section and retrieve your key. If you cannot see any key, click the Generate New Tokenization Key button to generate a new one
    Generate Tokenization Key Braintree

  2. Store your Braintree tokenization key in a safe place

You are now ready to start using braintree-drop-in. Let’s see how.

braintree-drop-in in action

First, launch the following command to install the braintree-web-drop-in library:

npm install --save braintree-web-drop-in

Then, you can start using it by implementing a BraintreeDropIn component as follows:

import React, {useEffect, useState} from 'react'
import './index.css';
import dropin from "braintree-web-drop-in"
import {Button} from "reactstrap";

export default function BraintreeDropIn(props) {
    const { show, onPaymentCompleted } = props;

    const [braintreeInstance, setBraintreeInstance] = useState(undefined)

    useEffect(() => {
        if (show) {
            const initializeBraintree = () => dropin.create({
                // insert your tokenization key or client token here
                authorization: "sandbox_s9gd7m2p_vp62s592633kc5p5", 
                container: '#braintree-drop-in-div',
            }, function (error, instance) {
                if (error)
                    console.error(error)
                else
                    setBraintreeInstance(instance);
            });

            if (braintreeInstance) {
                braintreeInstance
                    .teardown()
                    .then(() => {
                        initializeBraintree();
                    });
            } else {
                initializeBraintree();
            }
        }
    }, [show])

    return (
        <div
            style={{display: `${show ? "block" : "none"}`}}
        >
            <div
                id={"braintree-drop-in-div"}
            />

            <Button
                className={"braintreePayButton"}
                type="primary"
                disabled={!braintreeInstance}
                onClick={() => {
                    if (braintreeInstance) {
                        braintreeInstance.requestPaymentMethod(
                            (error, payload) => {
                                if (error) {
                                    console.error(error);
                                } else {
                                    const paymentMethodNonce = payload.nonce;
                                    console.log("payment method nonce", payload.nonce);

                                    // TODO: use the paymentMethodNonce to
                                    //  call you server and complete the payment here

                                    // ...

                                    alert(`Payment completed with nonce=${paymentMethodNonce}`);

                                    onPaymentCompleted();
                                }
                            });
                    }
                }}
            >
                {
                    "Pay"
                }
            </Button>
        </div>
    )
}

As you see, the Braintree Drop-in is created by using the create() function coming from the dropin variable imported from braintree-web-drop-in. That function requires an authorization field that contains your tokenization key or a valid client token, and a container pointing to the HTML identifier of a <div> where to mount the Braintree UI. Similarly, it accepts many other fields that can be used to enable the 3D Secure module, PayPal, and other options and payment methods. You can find all the parameters supported by the create() function here.

The create function immediately returns a Promise, which subsequently returns an error message or a valid Braintree Drop-in instance. This can be used to retrieve the payment nonce required by Braintree to complete the payment server-side. Specifically, it is generated by clicking the “Pay” button and calling the requestPaymentMethod() function.

Again, the requestPaymentMethod() function immediately returns a Promise, which will later return an error message or a payload object containing the desired payment method nonce. As explained early on, this is what the Braintree server SDK expects and requires to perform the payment server-side without exposing any personal data.

Then, you need to call your custom API harnessing the Braintree server SDK and the method payment nonce to perform the payment. Since this is a frontend-only tutorial, this part is omitted.

After successfully completing the payment, the show props will be set to false by the onPaymentCompleted() function and the BraintreeDropIn component will disappear. Plus, the next time the BraintreeDropIn component will be showed the Braintree Drop-in instance initializing process explained above will be performed again. But in that case, there will already be a valid braintreeInstance containing the data entered by the previous user. So, the create() function will return the same braintreeInstance.

This is a problem because Braintree might generate the same payment nonce used before again. As a result, this would cause the Braintree servers to respond with a 409 Conflict “Gateway Rejected” error. To avoid this, you must destroy the braintreeInstance first and then initialize it again.

This is why the teardown() function is called before invoking the create() function when braintreeInstance is not undefined. As you can imagine, such a problem happens only in single-page applications, where resources are not destroyed implicitly when changing the page.

Et voilà! You now have everything required to start using the Braintree Drop-in UI.

Putting it all together

Let’s see the BraintreeDropIn component in action by simulating a checkout page in React:

import './App.css';
import 'bootstrap/dist/css/bootstrap.min.css';
import BraintreeDropIn from "./components/BraintreeDropIn";
import {Col, Container, Input, Row, Button} from "reactstrap";
import {useState} from "react";

export default function App() {
const [showBraintreeDropIn, setShowBraintreeDropIn] = useState(false);
const [numberOfProducts, setNumberOfProducts] = useState(1);
const PRICE = 50;

return (
    <Container>
        <Col>
            <Row
                className={"productHeaderRow"}
            >
                <Col
                    xs={4}
                >
                    {
                        "Product"
                    }
                </Col>
                <Col
                    xs={2}
                    className={"text-center"}
                >
                    {
                        "Price"
                    }
                </Col>
                <Col
                    xs={2}
                    className={"text-center"}
                >
                    {
                        "Quantity"
                    }
                </Col>
                <Col
                    xs={2}
                    className={"text-center"}
                >
                    {
                        "Total"
                    }
                </Col>
                <Col
                    xs={2}
                />
            </Row>
            <Row
                className={"productInfoRow"}
            >
                <Col
                    xs={4}
                    className={"productInfoColumn"}
                >
                    {
                        "Foo product"
                    }
                </Col>
                <Col
                    xs={2}
                    className={"productInfoColumnCenter"}
                >
                    {
                        `$ ${PRICE}`
                    }
                </Col>
                <Col
                    xs={2}
                    className={"productInfoColumnCenter"}
                >
                    <Input
                        placeholder="0"
                        min={1}
                        max={100}
                        type="number"
                        step="1"
                        value={numberOfProducts}
                        onChange={((e) => {
                            setNumberOfProducts(e.target.value)
                        })}
                    />
                </Col>
                <Col
                    xs={2}
                    className={"productInfoColumnCenter"}
                >
                    {
                        `$ ${numberOfProducts * PRICE}`
                    }
                </Col>
                <Col
                    xs={2}
                    className={"productInfoColumnCenter"}
                >
                    <Button
                        onClick={() => {setShowBraintreeDropIn(true)}}
                        disabled={showBraintreeDropIn}
                    >
                        {
                            "Go to Checkout"
                        }
                    </Button>
                </Col>
            </Row>
        </Col>
        <BraintreeDropIn
            show={showBraintreeDropIn}
            onPaymentCompleted={() => {
                setShowBraintreeDropIn(false);
                setNumberOfProducts(1);
            }}
        />
    </Container>
);
}

The application implemented here is the same one you saw at the beginning of the article. Click on Go to Checkout and test out the Braintree Drop-In payment approach by following the instructions and using one of the many test credit card numbers accepted by Braintree, such as:

  • 5555555555554444 (Mastercard)
  • 4111111111111111 (Visa)
  • 6304000000000000 (Maestro)

Any valid expiration date after today and before 2030 will do.

Now, you only have to learn how to make your application accept real payments. The only thing you have to do is to replace the tokenization key or client token you put in the authorization field during the Braintree Drop-in initialization.

Instead of using this data coming from the Sandbox platform, replace it with a valid one by retrieving it in the Braintree Production platform. You can now receive real money.

Conclusion

In this article, we looked at what Braintree is and how it can be used to accept payments frontend. As shown, Braintree comes with a JavaScript client SDK offering many tools and feature to make this easy and quick. In particular, it offers two different possible integrations: Hosted Fields and Drop-in.

The first one involves custom logic and should only be used in special circumstances. On the other hand, the second one is a ready-made solution that allows you to start accepting payments using Braintree in few lines of code.

Thanks to this latter approach, implementing this increasingly requested feature is not complex.

Thanks for reading! I hope that you found this article helpful. Feel free to reach out to me with any questions, comments, or suggestions.

Full visibility into production React apps

Debugging React applications can be difficult, especially when users experience issues that are hard to reproduce. If you’re interested in monitoring and tracking Redux state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, try LogRocket.

LogRocket is like a DVR for web apps, recording literally everything that happens on your React app. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app's performance, reporting with metrics like client CPU load, client memory usage, and more.

The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.

Modernize how you debug your React apps — .

Antonello Zanini I'm a software engineer, but I prefer to call myself a technology bishop. Spreading knowledge through writing is my mission.

Leave a Reply