Integrating PayMongo API in Next.js - Part 1
Tags: nextjs, paymongo, javascript
Published: 2021-12-27 07:05:45

## Hi there,

In this two part article, I'll be guiding you on how to integrate the [PayMongo API](https://developers.paymongo.com/docs) in NextJS

In [Part 1](https://dev.to/xunylpay/integrating-paymongo-api-in-nextjs-part-1-1ee5), we have a brief discussion on PayMongo payment workflows and how to setup credit card payments.

In [Part 2](https://dev.to/xunylpay/integrating-paymongo-api-in-nextjs-part-2-3o5f), we tackle using webhooks to monitor payments and we move forward to processing GrabPay and GCash Payments. 


## Table of Contents
- [Introduction](#introduction)
  * [Demo](#demo)
  * [PayMongo Payment Workflows](#paymongo-payment-workflows)
- [Let's Build](#let-s-build)
  * [Setting Up](#setting-up)
  * [Running the project](#running-the-project)
  * [Implementing Card Payments](#implementing-card-payments)
    + [Creating a PaymentIntent from the server-side](#creating-a-paymentintent-from-the-server-side)
    + [Collect Card Information from the client-side](#collect-card-information-from-the-client-side)
    + [Send Card Information to PayMongo](#send-card-information-to-paymongo)
    + [Monitoring PaymentIntents through webhooks](#monitoring-paymentintents-through-webhooks)
- [Conclusion](#conclusion)


## Introduction
This is a simple guide for developers to integrate the PayMongo API in NextJS. Here, we'll start with a simple checkout and payment template project, and then move forward by filling up the API calls necessary to process payments. 

**PayMongo** provides businesses an easy, user-friendly way to accept payments from their customers. It is a payment gateway that process Visa/Mastercard, GCash, and GrabPay payments. 

**PayMongo API** is for those who want to directly integrate their site or app with PayMongo. Using the API allows you to take full control of the user's experience and integrate the payments directly with your systems and databases.



### Demo
**Live Preview:** https://nextjs-paymongo-api.vercel.app/
**GitHub:** https://github.com/xunylpay/nextjs-paymongo-api



### PayMongo Payment Workflows
Before diving into the code, I would like to discuss PayMongo's two Payment Workflows. These workflows differ in implementation and payment method. I have summarized the steps below: 


1.**Payment Intent Payment Method Workflow**

This is what PayMongo uses to process credit card and PayMaya payments. It involves creating a payment intent, creating a payment method, and then attaching the intent and method together. 

![Payment Intent Workflow](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pp1vpa9mcthgwyi06nwh.png)

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- Creating a PaymentIntent from the server-side
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- Collect Card Information from the client-side
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- Send Payment Information to PayMongo
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- Monitoring PaymentIntents through webhooks

2.**Source and Payment Workflow**

This is what PayMongo uses to process GCash and GrabPay payments. This involves creating a source, listening to customer authorization, and creating a payment for chargeable source. 

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- Create a Source
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- Have the customer authorize the payment
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;- Create a Payment using the chargeable Source


## Let's Build

### Setting Up
As mentioned in the title, we'll be using Next.js for building this.

For Next.js to work, we need to have Node.js and `npm` installed.

So, first, install [Node.js](https://nodejs.org/en/download/) if you haven't yet.

Let's also download `yarn`

```
npm install --global yarn
```

Additionally, you also need a PayMongo Account. You can sign up [here](https://dashboard.paymongo.com/signup) and get your test API keys at the developers tab. 

![API Keys Dashboard](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/taq1c5992ipswn30l903.png)

Lastly, to follow along this tutorial, we will be starting with the front end already built. Feel free to download or clone the [front-end-done branch](https://github.com/xunylpay/nextjs-paymongo-api/tree/front-end-done) in the repository. 

I built this simple front-end to simulate a simple payment process. The site uses localstorage to simulate database push and pull requests. We will be primarily concerned with the `src/components/payments` folder and `src/pages/api/createPaymentIntent.js` file. 


### Running the project
Now open the project in your favorite text editor or IDE (Integrated Development Environment). Open a terminal then run the following commands to start up the development server. 

```
yarn
yarn dev
```

On localhost:3000 you should be seeing this: 

![localhost:3000 home](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/543fgp56pu6zfafci8pt.png)

Next, create a .env file on the root folder. Insert your public and secret keys in this format: 

```
NEXT_PUBLIC_PAYMONGO_PUBLIC=pk_test_xxxxxxxxxxxxxxxxxxxxxxxx
PAYMONGO_SECRET=sk_test_xxxxxxxxxxxxxxxxxxxxxxxx
```
* The `NEXT_PUBLIC_` is important when exposing the environment variable in the front end


### Implementing Card Payments

As mentioned previously, the payment intent payment method workflow is used when implementing card payments. Let's do the steps provided in the [PayMongo Accepting Cards Documentation](https://developers.paymongo.com/docs/accepting-cards)

#### Creating a PaymentIntent from the server-side

In the `src/pages/api/createPaymentIntent.js` file, let us create an endpoint that would allow us to create a PaymentIntent given a POST request. The req.body should contain the necessary information as required by the [Create A PaymentIntent](https://developers.paymongo.com/reference/create-a-paymentintent) API call. 

As per the API reference, we need to authenticate our API requests. PayMongo uses HTTP Basic Auth and your API key as the basic auth username, encoded in Base64. This may seem complicated but it is really easy to implement in JavaScript as shown below. 

 `src/pages/api/createPaymentIntent.js`

```javascript
// This function is called to create a Payment intent
// Step 1 of https://developers.paymongo.com/docs/accepting-cards

export default async function handler(req, res) {
  if (req.method === "POST") {

    // Creating our options for the Create a Payment Intent Call
    const optionsIntent = {
      method: "POST",
      headers: {
        Accept: "application/json",
        "Content-Type": "application/json",
        Authorization: `Basic ${Buffer.from(
          process.env.PAYMONGO_SECRET
        ).toString("base64")}`, // HTTP Basic Auth and Encoding
      },
      body: JSON.stringify(req.body),
      // The req.body should follow this specific format
      //   {
      //     "data": {
      //          "attributes": {
      //               "amount": 10000 (int32) note that 10000 = PHP 100.00,
      //               "payment_method_allowed": [
      //                    "card",
      //                    "paymaya"
      //               ](string array),
      //               "payment_method_options": {
      //                    "card": {
      //                         "request_three_d_secure": "any"
      //                    }
      //               },
      //               "currency": "PHP" (string),
      //               "description": "description" (string),
      //               "statement_descriptor": "descriptor business name" (string)
      //          }
      //     }
      //  }
    };

    // Calling the Create a Payment Intent API
    await fetch("https://api.paymongo.com/v1/payment_intents", optionsIntent)
      .then((response) => response.json())
      .then(async (response) => {
        if (response.errors) {
          console.log(JSON.stringify(response.errors));
        } else {
          res.status(200).json({ body: response });
        }
      });
  } else {
  }
}
```


In the `src/components/payments/CreditCard.js`, we fill up the `createPaymentIntent` so that the function calls the `src/pages/api/createPaymentIntent.js` we just made. Keep in mind that we are using data from component props, but you can handle it in any way that you like. 

`src/components/payments/CreditCard.js - createPaymentIntent`
```javascript
// Function to Create a Payment Intent by calling the site's api
  const createPaymentIntent = async () => {
    setPaymentStatus("Creating Payment Intent");
    const paymentIntent = await fetch("/api/createPaymentIntent", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        data: {
          attributes: {
            amount: amount * 100,
            payment_method_allowed: ["card"],
            payment_method_options: {
              card: { request_three_d_secure: "any" },
            },
            currency: "PHP",
            description: description,
            statement_descriptor: "descriptor business name",
          },
        },
      }),
    })
      .then((response) => {
        return response.json();
      })
      .then((response) => {
        return response.body.data;
      });

    return paymentIntent;
  };
```


#### Collect Card Information from the client-side

The starter template already included this. I handled this by simply creating a useState for all the fields and setting the value on change. PayMongo does not recommend to send this data on the server or store it anywhere. We will be using this card information on the next step. 


#### Send Card Information to PayMongo

To send card information to PayMongo securely we will be [creating a paymentMethod](https://developers.paymongo.com/reference/create-a-paymentmethod) and [attaching it to the paymentIntent](https://developers.paymongo.com/reference/attach-to-paymentintent) we created in the first step. 

In calling the creating a payment method, we use the card details that the client provided. We also use our public key encoded in the base64 when calling the API Call.

`src/components/payments/CreditCard.js - createPaymentMethod`
```javascript
// Function to Create a Payment Method by calling the PayMongo API
  const createPaymentMethod = async () => {
    setPaymentStatus("Creating Payment Method");
    const paymentMethod = fetch("https://api.paymongo.com/v1/payment_methods", {
      method: "POST",
      headers: {
        Accept: "application/json",
        "Content-Type": "application/json",
        Authorization: `Basic ${Buffer.from(process.env.NEXT_PUBLIC_PAYMONGO_PUBLIC).toString("base64")}`,
      },
      body: JSON.stringify({
        data: {
          attributes: {
            details: {
              card_number: `${number}`, //"4343434343434345",
              exp_month: parseInt(`${month}`), //2
              exp_year: parseInt(`${year}`), //22
              cvc: `${code}`, //"123",
            },
            billing: {
              name: `${name}`,
              email: `${email}`,
              phone: `${phone}`,
            },
            type: "card",
          },
        },
      }),
    })
      .then((response) => {
        return response.json();
      })
      .then((response) => {
        return response.data;
      })
      .catch((err) => {
        console.log(err);
        setPaymentStatus(err);
        return err;
      });

    return paymentMethod;
  };
```

Now, to complete the credit card payment, we attach the payment intent and the payment method we created together. 

Here, we need to keep in mind two scenarios. Basic cards and 3DS cards. 3DS cards are cards where we need to handle a page wherein the user will enter their OTP (One Time Pin). The OTP is part of the 3DS protocol implemented by banks for clients to approve their online transactions. Basic cards are rare in the Philippines, and almost all PayMongo card transactions are done with 3DS cards. We render this page by creating an iframe or opening a window as seen below. 
 
`src/components/payments/CreditCard.js - attachIntentMethod`

```javascript
// Function to Attach a Payment Method to the Intent by calling the PayMongo API
  const attachIntentMethod = async (intent, method) => {
    setPaymentStatus("Attaching Intent to Method");
    fetch(`https://api.paymongo.com/v1/payment_intents/${intent.id}/attach`, {
      method: "POST",
      headers: {
        Accept: "application/json",
        "Content-Type": "application/json",
        Authorization: `Basic ${Buffer.from(process.env.NEXT_PUBLIC_PAYMONGO_PUBLIC).toString("base64")}`,
      },
      body: JSON.stringify({
        data: {
          attributes: {
            payment_method: `${method.id}`,
            client_key: `${intent.attributes.client_key}`,
          },
        },
      }),
    })
      .then((response) => response.json())
      .then((response) => {
        const paymentIntent = response.data;
        console.log(paymentIntent)
        const paymentIntentStatus = paymentIntent.attributes.status;
        if (paymentIntentStatus === 'awaiting_next_action') {
          // Render your modal for 3D Secure Authentication since next_action has a value. You can access the next action via paymentIntent.attributes.next_action.
          setPaymentStatus(paymentIntentStatus);
          window.open(
            paymentIntent.attributes.next_action.redirect.url, "_blank");
        } else {
          setPaymentStatus(paymentIntentStatus);
        }
      })
      .catch((err) => {
        console.log(err);
        setPaymentStatus(JSON.stringify(err));
      });
  };
```


Now, let's call up these functions in our submit function. 

`src/components/payments/CreditCard.js - onSubmit`
```javascript
const onSubmit = async (event) => {
    event.preventDefault();
    const paymentIntent = await createPaymentIntent();
    const paymentMethod = await createPaymentMethod();
    await attachIntentMethod(paymentIntent, paymentMethod);
};

```


#### Monitoring PaymentIntents through webhooks

The last step of the Payment Intent Payment Method workflow is monitoring the payment intent through the use of webhooks. We'll discuss this in the next part of the guide. However, we can still monitor paymentIntents in the client side by calling the [Retrieve a Payment Intent API](https://developers.paymongo.com/reference/retrieve-a-paymentintent). Let us create this function and call it after attaching the payment intent and method. 

This is kind of a hack to set a timeout that for every 5 seconds we check the status by calling the API until payment is resolved.

`src/components/payments/CreditCard.js - listenToPayment`
```javascript
// Function to Listen to the Payment in the Front End
  const listenToPayment = async (fullClient) => {
    const paymentIntentId = fullClient.split('_client')[0];
    let i = 5;
    for (let i = 5; i > 0; i--) {
      setPaymentStatus(`Listening to Payment in ${i}`)
      await new Promise(resolve => setTimeout(resolve, 1000))

      if (i == 1) {
        const paymentIntentData = await fetch(
          'https://api.paymongo.com/v1/payment_intents/' + paymentIntentId + '?client_key=' + fullClient,
          {
            headers: {
              // Base64 encoded public PayMongo API key.
              Authorization: `Basic ${Buffer.from(process.env.NEXT_PUBLIC_PAYMONGO_PUBLIC).toString("base64")}`
            }
          }
        ).then((response) => {
          return response.json()
        }).then((response) => {
          console.log(response.data)
          return response.data
        })

        if (paymentIntentData.attributes.last_payment_error) {
          setPaymentStatus(JSON.stringify(paymentIntentData.attributes.last_payment_error))
        }
        else if (paymentIntentData.attributes.status === "succeeded") {
          setPaymentStatus("Payment Success")
        }
        else {
          i = 5;
        }
      }
    }
  }

```


In attachIntentMethod, we listen to the status of the payment intent after opening the OTP page. 

`src/components/payments/CreditCard.js - attachIntentMethod`
```javascript
...
      .then((response) => response.json())
      .then((response) => {
        const paymentIntent = response.data;
        console.log(paymentIntent)
        const paymentIntentStatus = paymentIntent.attributes.status;
        if (paymentIntentStatus === 'awaiting_next_action') {
          // Render your modal for 3D Secure Authentication since next_action has a value. You can access the next action via paymentIntent.attributes.next_action.
          setPaymentStatus(paymentIntentStatus);
          window.open(
            paymentIntent.attributes.next_action.redirect.url, "_blank");
            listenToPayment(paymentIntent.attributes.client_key);
        } else {
          setPaymentStatus(paymentIntentStatus);
        }
      })
      .catch((err) => {
        console.log(err);
        setPaymentStatus(JSON.stringify(err));
      });
...

```

After doing this, the app should be able to accept Credit card payments that would reflect on your PayMongo dashboard. PayMongo conveniently provides test cards that you can use for testing [here](https://developers.paymongo.com/docs/testing).


## Conclusion

In this guide, you learned how to use PayMongo API keys, PayMongo payment workflows, and accepting credit card payments. For the next part of the guide, we'll learn more about webhooks and using the payment source and payment to process e-wallet transactions (GCash and GrabPay).