Online Payments with React + Square
Tags: react, javascript, webdev
Published: 2019-10-29 22:40:35

There are *a lot* of ways to accept payments online. Wouldn’t it be nice to implement a single form that could accept as many payment methods as we want at a single time? Let’s take a look at implementing a custom payment form using [Square](https://squareup.com/developers) and [React](https://reactjs.org/). This form will enable us to take credit cards online, *and* give us support for Apple Pay, Google Pay, and Masterpass in a single payment form.

### Things required to understand this post:

* [React](https://reactjs.org/) (simplified with [`create-react-app`](https://github.com/facebook/create-react-app))

* [Square’s Payment Form](https://developer.squareup.com/docs/payments/sqpaymentform/what-it-does)

### Our final (payment) form:

![](//images.ctfassets.net/1wryd5vd9xez/2Gm1wvgVvahTcDXqElYvL3/d5fba6560fe9a9da935d79f966f325be/https___cdn-images-1.medium.com_max_2000_1_OX259-SlAPzFpWyuVlj6Mg.png)

## React and Square’s Payment Form

If you’re familiar with React, then you’re accustomed to passing along `props` and controlling your component via its `state`. Let’s focus on how to get a basic setup up and running with Square’s payment form controlled by a React component. We’ll also demonstrate how to dynamically load the [Square payment form script](https://developer.squareup.com/docs/payment-form/payment-form-walkthrough) in case you want to simply insert the payment form component on a page. Dynamic loading the script is only relevant if you don’t want the script to only load on a checkout page (or wherever you’d like to take a payment).

If you’re not acquainted with Square’s payment form, head over [to the docs](https://developer.squareup.com/docs/payments/sqpaymentform/what-it-does) and get familiar. There are some templates, explanations, and guides on getting the form set up using basic HTML, CSS, and JavaScript.

At a basic level, the payment form directly captures your customer’s card details directly on Square’s servers using an `<iframe>`. The payment form facilitates the generation of these `<iframe>` elements and provides an API for creating a [nonce](https://developer.squareup.com/docs/payments/sqpaymentform/how-it-works) (a one-time-use token) to reference those details later (all without you knowing any sensitive information!).

The main problem you hit with these `<iframe>` elements replacing other elements in the [DOM](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Introduction) is that React usually likes to be in charge of managing all your DOM interactions. This requires doing a bit of extra setup on our components to be sure we render everything correctly, in the right order, and correctly handle different events generated by the Square payment form script.

## Dynamically Load The Square Payment Form Script

Our base component is where we’ll actually manage dynamically loading:

```html
<script src=”https://js.squareup.com/v2/paymentform”></script>
```

into the `<head>` of the DOM and trigger our child component to render. The child component will actually be responsible for assembling, formatting, and managing our payment form. This is done to ensure the script has loaded and we can pass the [`SqPaymentForm`](https://developer.squareup.com/docs/api/paymentform#paymentform-overview) object down to our child component.

```javascript
import React, { Component } from 'react';
import PaymentForm from './components/PaymentForm';

class App extends Component {
  constructor(props){
    super(props)
    this.state = {
      loaded: false
    }
  }

  componentWillMount(){
    const that = this;
    let sqPaymentScript = document.createElement('script');
    sqPaymentScript.src = "https://js.squareup.com/v2/paymentform";
    sqPaymentScript.type = "text/javascript"
    sqPaymentScript.async = false;
    sqPaymentScript.onload = ()=>{that.setState({
      loaded: true
    })};
    document.getElementsByTagName("head")[0].appendChild(sqPaymentScript);
  }

  render() {
    return (
      this.state.loaded &&
        <PaymentForm
          paymentForm={ window.SqPaymentForm }
        />
    );
  }
}

export default App;
```

You can see we’re just using some vanilla JavaScript inside the lifecycle method `componentWillMount()` to create a `<script>` element and set some attributes, and then making sure that we’re updating our React component’s state to `loaded` once the script has actually loaded on the page. This will trigger React to re-render and return `true` for `this.state.loaded` inside our `render()` method and allow our child component to render.

The other notable part of our code is how we’re passing `SqPaymentForm` via the `paymentForm` prop. We are passing in the SqPaymentForm object that is attached to the window, so rendering the payment form and triggering submission is easier to manage.
Full code example can also be found at [https://github.com/mootrichard/square-react-online-payments](https://github.com/mootrichard/square-react-online-payments)

<iframe width="600" height="500" src="https://codesandbox.io/embed/4zjrv7kry9?autoresize=1&amp;module=%2Fsrc%2FApp.js&amp;view=editor" style="width:600px; height:500px; border:0; border-radius: 4px; overflow:hidden;" sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"></iframe>

## React Payment Form Component

To keep things simple, we’re modifying existing templates found on [Square’s GitHub](https://github.com/square/connect-api-examples/tree/master/templates/web-ui/payment-form/basic-digital-wallet). For more info on customizing or setting up a Square payment form, check out our [guides](https://developer.squareup.com/docs/payments/sqpaymentform/what-it-does). We’ll focus more on the difference between those templates and wiring things into our React component.

### Our render() Method

```html
  render(){
    return (
      <div className="container">
        <div id="form-container">
          <div id="sq-walletbox">
            <button style={{display: (this.state.applePay) ? 'inherit': 'none'}}
                    className="wallet-button"
                    id="sq-apple-pay"></button>
            <button style={{display: (this.state.masterpass) ? 'block': 'none'}}
                    className="wallet-button"
                    id="sq-masterpass"></button>
            <button style={{display: (this.state.googlePay) ? 'inherit': 'none'}}
                    className="wallet-button"
                    id="sq-google-pay"></button>
            <hr />
          </div>

          <div id="sq-ccbox">
            <p>
              <span style={styles.leftCenter}>Enter Card Info Below </span>
              <span style={styles.blockRight}>
                {this.state.cardBrand.toUpperCase()}
              </span>
            </p>
            <div id="cc-field-wrapper">
              <div id="sq-card-number"></div>
              <input type="hidden" id="card-nonce" name="nonce" />
              <div id="sq-expiration-date"></div>
              <div id="sq-cvv"></div>
            </div>
            <input
              id="name"
              style={styles.name}
              type="text"
              placeholder="Name"
            />
            <div id="sq-postal-code"></div>
          </div>
          <button className="button-credit-card"
                  onClick={this.requestCardNonce}>Pay</button>
        </div>
        <p style={styles.center} id="error"></p>
      </div>
    )
  }
```

The key parts to note in the elements we have is the divs elements with the id’s: `sq-apple-pay`, `sq-masterpass`, `sq-google-pay`, `sq-card-number`, `sq-cvv`, `sq-expiration-date`, and `sq-postal-code`. We transformed the examples to use divs for everything instead of form components, since these are all the fields that will be targeted by Square’s payment form script to be replaced with `<iframe>` elements. Also, since we’re using React, we will have our own functions for controlling submission and triggering the request of a nonce from the payment form.

### Digital Wallet Payments & Methods Supported

To adjust what digital wallet options (*sometimes called mobile wallet options*) you want to support, just provide different key-value pairs in your `SqPaymentForm` configuration object ([see more on that here](https://developer.squareup.com/docs/payments/sqpaymentform/digitalwallet/intro)). You should be able to see in the `render()` method that we’re controlling the display of our mobile payment options using the component’s `state`.

```javascript
callbacks: {
  methodsSupported: (methods) => {
    if(methods.googlePay){
      this.setState({
        googlePay: methods.googlePay
      })
    }
    if(methods.applePay){
      this.setState({
        applePay: methods.applePay
      })
    }
    if(methods.masterpass){
      this.setState({
        masterpass: methods.masterpass
      })
    }
    return;
  },
```

We are setting the state inside of the `methodsSupported()` callback that the Square payment form has provided to us. Since each mobile wallet option is specific to the browser that a customer is visiting from, you need to conditionally render the buttons to match what should be available based on your customer’s browser or mobile device. We also have to make these separate conditionals since the payment form calls the `methodsSupport()` function once for each method you’re choosing to support. Our example is trying to support Masterpass, Apple Pay, and Google Pay, so three calls will be made. It’s a little aggressive in our calls to `setState()`, but only three calls, so no worries — just keep it in mind if you’re calling `setState()` elsewhere, since every call will trigger a re-render of the component.

### Linking & Controlling the Component

The main takeaway is to use `state` inside of the provided callback. Using `state` in the component allows us to react (*so punny*) to different events emitted by Square’s payment form script. You can learn more about all these events are [in the docs](https://developer.squareup.com/docs/api/paymentform#_callbackfunctions_detail). In our example, a key place for this tie-in would be the `inputEventReceived()` callback since it’s called on every input event. In our example component, we update the brand of the card (in the top right corner) once it has been identified by the payment form.

## Thoughts and Conclusions

This is only one way of approaching implementing the Square payment form in React. Initially it seemed like a good idea to try passing in the config object as a prop, but that doesn’t work too well for configuring your callback functions, unless you’re comfortable overriding them before creating your `paymentForm` object (this just *felt* wrong).

The main place I’ve seen developers trip up is usually on not disabling [`autoBuild`](https://developer.squareup.com/docs/api/paymentform#paymentform-configurationfields). The `paymentform` script is going to immediately look for elements with the provided element id’s on build, but the problem arises because React might not have rendered the elements to the DOM yet. It is better to control the build process by triggering it with a call to`.build()`.

The implementation of the form in React is fairly straight forward (if you know React) and just requires understanding of the React lifecycle in relation to the `paymentform` lifecycle.

You can find a full example of this form over at: [https://github.com/mootrichard/square-react-online-payments](https://github.com/mootrichard/square-react-online-payments).

If you liked this post on React + Square, but would like to see this refactored using [React’s Hooks API](https://reactjs.org/docs/hooks-intro.html), [tweet at me](https://twitter.com/@wootmoot), respond here on Medium, or bug me in [our Slack community](https://squ.re/slack) and I’ll follow up with a post with how to refactor this example using the React Hooks API.

---

*Want more? [Sign up](https://www.workwithsquare.com/developer-newsletter.html?channel=Online%20Social&sqmethod=Blog) for our monthly developer newsletter or come hang out with us in the Square Dev [Slack channel](https://squ.re/slack)! You can also follow us on Twitter at [@SquareDev](https://twitter.com/@SquareDev).*