Skip to main content

GraphQL for Beginners: Implementing a Robust Payment System

Not many payment providers offer a GraphQL API but here's the full guide to implement one with Braintree


When integrating a payment system on your app or website, a GraphQL API offers many benefits such as its simple yet powerful and flexible capabilities.

In this article, let's look at how we can implement a robust payment system using the Braintree GraphQL API, the next-generation API for payments.

Prerequisites

  • Intermediate React and JavaScript knowledge

  • Understanding of GraphQL, how it works and mutations (if not, see GraphQL for Beginners: Introduction)

  • Node and npm installed in machine

  • Any code editor

  • Braintree sandbox account (sign up here)

In a new React project, install the packages we need by running:

npm install braintree-web-drop-in-react @apollo/client graphql

In index.js, we initialize the ApolloClient and setup an authorization header with your Braintree credentials when calling API requests.

To initialize the ApolloClient, we must code the following:

  1. Import modules we need

  2. Specify the httpLink with a uri. This will be the Braintree sandbox GraphQL server, where we will make our requests to.

  3. Create the authLink to allow making API calls with your Braintree sandbox credentials

  4. Initialize the ApolloClient as client

  5. Wrap App component with the ApolloProvider component, and pass client as its prop.

Note: authLink is basically the authorization header, which is appended to the httpLink when setting up ApolloClient. This ensures that we have permission to call the Braintree GraphQL API.

This what our index.js will look like:

import React from 'react'
import { createRoot } from 'react-dom/client';
import App from './App'
import './index.css'
//1. Import modules we need
import {
  ApolloClient,
  InMemoryCache,
  ApolloProvider,
  createHttpLink
} from "@apollo/client";
import { setContext } from '@apollo/client/link/context';

//2. Set uri to where we will make requests to
const httpLink = createHttpLink({
  uri: 'https://payments.sandbox.braintree-api.com/graphql',
});

const encodeBase64 = (str) => {
  return window.btoa(unescape(encodeURIComponent(str)));
};

//3. Authorization header to allow access to API with our sandbox creds
const authLink = setContext((_, { headers }) => {
  return {
    // pass authorization in headers
    headers: {
      ...headers,
      authorization: "Basic " +
       //get our creds from .env file
      encodeBase64(
   `${process.env.REACT_APP_PUBLIC_KEY}:${process.env.REACT_APP_PRIVATE_KEY}`
      ),
    "Braintree-Version": "2019-12-17", //required as read from docs
    "Content-Type": "application/json", //required as read from docs
    }
  }
});

//4. Initialize client
const client = new ApolloClient({
  link: authLink.concat(httpLink),
  cache: new InMemoryCache()
});

//5. Wrap App with ApolloProvider
const root = createRoot(document.getElementById('root')); 
root.render(
  <ApolloProvider client={client}>
      <App />
  </ApolloProvider>
);

As seen from the code above, our sandbox credentials can be stored in an .env file, which we create in our root directory. Below is an example of what the .env file contains.

To get your Braintree public key and private key for this step, login to your sandbox account.

Then, click on the gear icon on the top-right, select API.

Now you should be navigated to a page where you can copy your public key and private key.

Braintree offers DropIn UI so that it is convenient for websites to accept payments quickly without having to make their own card payment fields from scratch. A simple DropIn Card form will look like:

In our App.js, we want to initialize our DropIn UI as shown in the code below:

import DropIn from "braintree-web-drop-in-react";

function App() {
  const [instance, setInstance] = useState(null)
  const [clientToken, setClientToken] = useState("");

  return (
    <div className="App">
      <DropIn
        options={{authorization: clientToken}}
        onInstance={(instance) => setInstance(instance)}
      />
      <button>Buy Now!</button>
    </div>
  )
}

export default App

If we run this app now, the DropIn UI will not render because we have not generated and passed a clientToken in the DropIn component yet.

To generate a clientToken, let's create our first GraphQL Mutation.

If you need a refresher on Mutations, read GraphQL for Beginners: Introduction.

Let's create a new file called Mutations.js. This is where we can write all our mutations in.

Our first mutation will be the createClientToken mutation as seen in the Braintree GraphQL docs.

//Mutations.js
import { gql } from "@apollo/client";

export const CREATE_CLIENT_TOKEN = gql`
  mutation createClientToken ($input: CreateClientTokenInput) {
    createClientToken (input: $input) {
      clientToken
    }
}
`;

Now back in App.js, let's import the mutation CREATE_CLIENT_TOKEN and import the useMutation Hook so that we can execute the mutation.

//App.js - add these 2 import lines
import { CREATE_CLIENT_TOKEN } from "Mutations";
import { useMutation } from "@apollo/client";
//and import useEffect
import { useState, useEffect } from "react";

Then we call the useMutation Hook to execute the mutation. The data we get from the mutation will be set as the clientToken.

//useMutation to execute the mutation.
//On completed, the clientToken variable will be set
const [createClientToken] = useMutation(CREATE_CLIENT_TOKEN, {
    onCompleted: (data) => {
      setClientToken(data.createClientToken.clientToken);
    },
    onError: (error) => {
      alert(error);
    },
});

//below, we make sure DropIn only renders if clientToken is created
return (
    <div className="App">
      {clientToken && <DropIn
        options={{authorization: clientToken}}
        onInstance={(instance) => setInstance(instance)}
      />}
      <button>Buy Now!</button>
    </div>
  )

Now we will call the mutation as soon as the app loads using the useEffect Hook.

If you need a refresher on the useEffect Hook, check out this Introduction to useEffect article.

//App.js
useEffect(() => {
    createClientToken();
  }, [])

And now, if we run the app, we can see that when the clientToken is successfully generated, the DropIn UI will show:

Now all that's left is to execute the ChargePaymentMethod mutation so Braintree can process the transaction. We can execute this mutation when the user clicks the 'Buy Now!' button.

Let's add another mutation in our Mutations.js file.

//Mutations.js
export const CHARGE = gql`
  mutation ChargePaymentMethod($input: ChargePaymentMethodInput!) {
    chargePaymentMethod(input: $input) {
      transaction {
        id
        legacyId
        amount {
          value
          currencyCode
        }
        status
      }
    }
  }
`;

Same as before, we go back to App.js to import this mutation and call it.

//App.js
//add the CHARGE mutation
import { CREATE_CLIENT_TOKEN, CHARGE } from "./Mutations.jsx";

//add the useMutation function to call CHARGE
const [chargePaymentMethod] = useMutation(CHARGE, {
    onCompleted: (data) => {
      alert("Payment Successful");
      console.log(data);
    },
    onError: (error) => {
      console.log(error);
      alert(error);
    },
  });

Finally, we should have our button calling this useMutation Hook when it is clicked. Let's create a buyNow function for our button.

const buyNow = async () => {
    //first, get the nonce (payment details) after user clicks the button
    const { nonce } = await instance.requestPaymentMethod();
    //then, execute the mutation to process the transaction
    chargePaymentMethod({
      variables: {
        input: {
          paymentMethodId: nonce,
          transaction: {
            amount: 1, //hardcoded for this demo
          },
        },
      },
    });
  };

//pass buyNow in the button's onClick attribute
return (
    <div className="App">
      {clientToken && <DropIn
        options={{authorization: clientToken}}
        onInstance={(instance) => setInstance(instance)}
      />}
      <button onClick={buyNow}>Buy Now!</button>
    </div>
  )

Let's use a test card and run the app. The result is shown in the clip below.

As seen in the clip above and the screenshot below, the chargePaymentMethod is executed upon clicking the button. This mutation processes the transaction and the status is SUBMITTED_FOR_SETTLEMENT, which means it is a success.

We can double-check the transaction in our Braintree sandbox account. Login and check that the transaction is there. The transaction id (i.e. fdqh7ews) from this demo is properly recorded and received.

In this article, we have learned how to implement a payment system using Braintree's GraphQL API easily with its DropIn UI.

I hope it has helped you in getting started exploring the Braintree GraphQL API. There are many features that I haven't covered in this article that Braintree offers to give any business a secure and easy-to-setup payment gateway and system. Do check out their GraphQL API Explorer to see more.

Thanks for reading! If you have any questions, do let me know in the comments or refer to the References section below. Cheers!

Comments

Popular Posts

How I Reduced the Size of My React Native App by 85%

How and Why You Should Do It I borrowed 25$ from my friend to start a Play Store Developer account to put up my first app. I had already created the app, created the assets and published it in the store. Nobody wants to download a todo list app that costs 25mb of bandwidth and another 25 MB of storage space. So today I am going to share with you how I reduced the size of Tet from 25 MB to around 3.5 MB. Size Matters Like any beginner, I wrote my app using Expo, the awesome React Native platform that makes creating native apps a breeze. There is no native setup, you write javascript and Expo builds the binaries for you. I love everything about Expo except the size of the binaries. Each binary weighs around 25 MB regardless of your app. So the first thing I did was to migrate my existing Expo app to React Native. Migrating to React Native react-native init  a new project with the same name Copy the  source  files over from Expo project Install all de...

How to recover data of your Android KeyStore?

These methods can save you by recovering Key Alias and Key Password and KeyStore Password. This dialog becomes trouble to you? You should always keep the keystore file safe as you will not be able to update your previously uploaded APKs on PlayStore. It always need same keystore file for every version releases. But it’s even worse when you have KeyStore file and you forget any credentials shown in above box. But Good thing is you can recover them with certain tricks [Yes, there are always ways]. So let’s get straight to those ways. 1. Check your log files → For  windows  users, Go to windows file explorer C://Users/your PC name/.AndroidStudio1.4 ( your android studio version )\system\log\idea.log.1 ( or any old log number ) Open your log file in Notepad++ or Any text editor, and search for: android.injected.signing and if you are lucky enough then you will start seeing these. Pandroid.injected.signing.store.file = This is  file path where t...

React Native - Text Input

In this chapter, we will show you how to work with  TextInput  elements in React Native. The Home component will import and render inputs. App.js import React from 'react' ; import Inputs from './inputs.js' const App = () => { return ( < Inputs /> ) } export default App Inputs We will define the initial state. After defining the initial state, we will create the  handleEmail  and the  handlePassword  functions. These functions are used for updating state. The  login()  function will just alert the current value of the state. We will also add some other properties to text inputs to disable auto capitalisation, remove the bottom border on Android devices and set a placeholder. inputs.js import React , { Component } from 'react' import { View , Text , TouchableOpacity , TextInput , StyleSheet } from 'react-native' class Inputs extends Component { state = { ...