Square credit card payment integration using React and React Hooks

Square Payment Gateway

If you are reading this post then you already know square is a payment gateway that accepts payment. In this article, I will explain how you can integrate square payment using react.

Things you should do to understand this post.

  1. Learn the Basics of React and Node.
  2. Sign up to the square, create a sandbox account and make a note of application Id and access token of the application.
  3. Read the official doc which explains how you can integrate square using basic HTML, CSS, and JavaScript, this post focuses more on converting the same code into a React.

Steps

  1. Run below mentioned and command and test your app.
npx create-react-app client
cd client
npm start

2. Remove unnecessary codes and files like React Logo.

3. Inside client folder create .env file and add application id.

REACT_APP_APLLICATION_ID=Application Id

4. Add CSS code in app.css to design payment form.

button {
  border: 0;
  font-weight: 500;
}

fieldset {
  margin: 0;
  padding: 0;
  border: 0;
}

#form-container {
  position: relative;
  width: 380px;
  margin: 0 auto;
  top: 50%;
}

.third {
  float: left;
  width: calc((100% - 32px) / 3);
  padding: 0;
  margin: 0 16px 16px 0;
}

.third:last-of-type {
  margin-right: 0;
}

/* Define how SqPaymentForm iframes should look */
.sq-input {
  height: 56px;
  box-sizing: border-box;
  border: 1px solid #E0E2E3;
  background-color: white;
  border-radius: 6px;
  display: inline-block;
  -webkit-transition: border-color .2s ease-in-out;
  -moz-transition: border-color .2s ease-in-out;
  -ms-transition: border-color .2s ease-in-out;
  transition: border-color .2s ease-in-out;
}

/* Define how SqPaymentForm iframes should look when they have focus */
.sq-input--focus {
  border: 1px solid #4A90E2;
}

/* Define how SqPaymentForm iframes should look when they contain invalid values */
.sq-input--error {
  border: 1px solid #E02F2F;
}

#sq-card-number {
  margin-bottom: 16px;
}

/* Customize the "Pay with Credit Card" button */
.button-credit-card {
  width: 100%;
  height: 56px;
  margin-top: 10px;
  background: #4A90E2;
  border-radius: 6px;
  cursor: pointer;
  display: block;
  color: #FFFFFF;
  font-size: 16px;
  line-height: 24px;
  font-weight: 700;
  letter-spacing: 0;
  text-align: center;
  -webkit-transition: background .2s ease-in-out;
  -moz-transition: background .2s ease-in-out;
  -ms-transition: background .2s ease-in-out;
  transition: background .2s ease-in-out;
}

.button-credit-card:hover {
  background-color: #4281CB;
}

5. Load Payment Form Script inside app.js

import React, { useState, useEffect } from 'react';
import './App.css';
import Square from './Component/Square';

const App = () => {
  const [isLoad, setLoad] = useState(false);
  useEffect(() => {
    let sqPaymentScript = document.createElement("script");
    // sandbox: https://js.squareupsandbox.com/v2/paymentform
    // production: https://js.squareup.com/v2/paymentform
    sqPaymentScript.src = "https://js.squareupsandbox.com/v2/paymentform";
    sqPaymentScript.type = "text/javascript";
    sqPaymentScript.async = false;
    sqPaymentScript.onload = () => {
      setLoad(true);
    };
    document.getElementsByTagName("head")[0].appendChild(sqPaymentScript);
  });

  const squarePayment = isLoad ? (
        <Square paymentForm={ window.SqPaymentForm }/>
    ) : (
       null
    )

  return (
    <div className="App">
        <h1>Square</h1>
       {squarePayment}
    </div>
  );
}

export default App;

As you can see in the code we are using plan javascript inside the useEffect hook which is similar to componentDidMount and componentDidUpdate to load the square payment form in <head>.

The child component Square is used for loading, formatting and managing the payment form and isLoad state is used to make sure the child component is render only when the script is loaded and we can pass SqPaymentForm object as a prop.

6. Add a file Utils/paymentForm.js to initialize payment form elements.

const config = {
    // Initialize the payment form elements
    
    //TODO: Replace with your sandbox application ID
    applicationId: process.env.REACT_APP_APLLICATION_ID,
    inputClass: 'sq-input',
    autoBuild: false,
    // Customize the CSS for SqPaymentForm iframe elements
    inputStyles: [{
        fontSize: '16px',
        lineHeight: '24px',
        padding: '16px',
        placeholderColor: '#a0a0a0',
        backgroundColor: 'transparent',
    }],
    // Initialize the credit card placeholders
    cardNumber: {
        elementId: 'sq-card-number',
        placeholder: 'Card Number'
    },
    cvv: {
        elementId: 'sq-cvv',
        placeholder: 'CVV'
    },
    expirationDate: {
        elementId: 'sq-expiration-date',
        placeholder: 'MM/YY'
    },
    postalCode: {
        elementId: 'sq-postal-code',
        placeholder: 'Postal'
    },
    // SqPaymentForm callback functions
    callbacks: {
        /*
        * callback function: cardNonceResponseReceived
        * Triggered when: SqPaymentForm completes a card nonce request
        */
        cardNonceResponseReceived: function (errors, nonce, cardData) {
        if (errors) {
            // Log errors from nonce generation to the browser developer console.
            console.error('Encountered errors:');
            errors.forEach(function (error) {
                console.error('  ' + error.message);
            });
            alert('Encountered errors, check browser developer console for more details');
            return;
        }
           //alert(`The generated nonce is:\n${nonce}`);
           fetch('http://localhost:4000/process-payment', {
              method: 'POST',
              headers: {
                'Accept': 'application/json',
                'Content-Type': 'application/json'
              },
              body: JSON.stringify({
                nonce: nonce
              })
            })
            .catch(err => {
              alert('Network error: ' + err);
            })
            .then(response => {
              if (!response.ok) {
                return response.text().then(errorInfo => Promise.reject(errorInfo));
              }
              return response.text();
            })
            .then(data => {
              console.log(JSON.stringify(data));
              alert('Payment complete successfully!\nCheck browser developer console form more details');
            })
            .catch(err => {
              console.error(err);
              alert('Payment failed to complete!\nCheck browser developer console form more details');
            });
        }
      }
}

export default config;

7. Create a child component.

import React from 'react';
import config from '../Utils/paymentForm';

const Square = ({ paymentForm }) => {

    paymentForm = new paymentForm(config);
    paymentForm.build();
    const requestCardNonce = () =>{
        paymentForm.requestCardNonce();
    }

    return (
        <div id="form-container">
            <div id="sq-card-number"></div>
            <div className="third" id="sq-expiration-date"></div>
            <div className="third" id="sq-cvv"></div>
            <div className="third" id="sq-postal-code"></div>
            <button id="sq-creditcard" className="button-credit-card" onClick={requestCardNonce}> Pay £ 1.00</button>
        </div>
      
    )
}

export default Square;

8. The next step is to work on the backend, create a package.json and run npm install command to install all dependencies.

{
  "name": "sqpaymentform-nodejs-starterkit",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "start": "nodemon server.js"
  },
  "dependencies": {
    "body-parser": "^1.19.0",
    "express": "^4.17.0",
    "square-connect": "2.20190814.0"
  },
  "devDependencies": {
    "nodemon": "^1.19.0"
  }
}

9. Create .env on the backend and define an access token.

ACCESS_TOKEN=acess_token

10. The final step is to add server.js file.

const express = require('express');
const bodyParser = require('body-parser');
const crypto = require('crypto');
const squareConnect = require('square-connect');
const dotenv = require('dotenv');
dotenv.config();
const app = express();
const port = 4000;
var cors = require('cors');
app.use(cors())
// Set the Access Token
const accessToken = process.env.ACCESS_TOKEN

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(express.static(__dirname));

// Set Square Connect credentials and environment
const defaultClient = squareConnect.ApiClient.instance;

// Configure OAuth2 access token for authorization: oauth2
const oauth2 = defaultClient.authentications['oauth2'];
oauth2.accessToken = accessToken;

// Set 'basePath' to switch between sandbox env and production env
// sandbox: https://connect.squareupsandbox.com
// production: https://connect.squareup.com
defaultClient.basePath = 'https://connect.squareupsandbox.com';

app.post('/process-payment', async (req, res) => {
  const request_params = req.body;

  // length of idempotency_key should be less than 45
  const idempotency_key = crypto.randomBytes(22).toString('hex');

  // Charge the customer's card
  const payments_api = new squareConnect.PaymentsApi();
  const request_body = {
    source_id: request_params.nonce,
    amount_money: {
      amount: 100, // £1.00 charge
      currency: 'GBP'
    },
    idempotency_key: idempotency_key
  };

  try {
    const response = await payments_api.createPayment(request_body);
    res.status(200).json({
      'title': 'Payment Successful',
      'result': response
    });
  } catch(error) {
    res.status(500).json({
      'title': 'Payment Failure',
      'result': error.response.text
    });
  }
});

app.listen(
  port,
  () => console.log(`listening on - http://localhost:${port}`)
);

once all steps are done, run both react and node servers to test the functionality.

Test Credentials

Card Number: 4111 1111 1111 1111, CVV: 111, Postal: 11111 (you can provide any postal code).

Launch your app in sandbox mode to see the transaction.

Git Hub

Payment Form
Sadam Bapunawar
Sadam Bapunawar
SJ Innovation LLC
Where employee happiness generates client success.
Follow us on Facebook or Twitter