How to integrate Paypal with NextJs
Tags: tutorial, paypal, react, node
Published: 2023-07-03 09:06:36

Payment integration is the most required thing when we are heading to develop a an eCommerce website. And Paypal is one of the most common platforms which allow us to make our transactions smoothly. So let's learn how we can integrate Paypal with our NextJs eCommerce App.


## Installing the required packages
First, we need some packages which will help us a lot in the integration of Paypal and we would just need to use those according to our need.

1. [@paypal/react-paypal-js](https://www.npmjs.com/package/@paypal/react-paypal-js)
2. [@paypal/checkout-server-sdk](https://www.npmjs.com/package/@paypal/checkout-server-sdk)

#### @paypal/react-paypal-js
For the front-end, where we need Paypal Payment Buttons to be shown and the functionality of:

1. Creating an order
2. Capturing an order

This package will help us in those requriements

#### @paypal/checkout-server-sdk
For the back-end, where we actually need to call the paypal Api, this package provides us classes which are helpful in that.


## Getting Credentials and Setting Environment
Head over to [Paypal Developer](https://developer.paypal.com/) and create and account.
Once you are on the dashboard:

1. Click on 'Sandbox Accounts':
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c3ipaz1mwxrdpz0a42j6.png)


2. Scroll down and view the details of **business** account:
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/am0ft6n65pcfmjut8359.png)


3. Under **Rest Api Apps**, Copy the **Cliend ID** and **Secret** and store those in your app in .env file
If you are unable to find these things under **Rest Api Apps**, you need to create an app by clicking the button, **Create App**. When popup is shown, select **Merchant** account.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sh4lma5m6naral3iog9x.png)



**.env**:

```
PAYPAL_CLIENT_ID = "<your cliend id>"
PAYPAL_CLIENT_SECRET = "<your client secret>"
NEXT_PUBLIC_PAYPAL_CLIENT_ID = "<your client id>"
```


**Note**: We are using sandbox account for this tutorial purpose. You can easily switch to live account by replacing the sandbox credentials, cliend ID and Secret, with Live one.

## Back-end Integration
We need to define api endpoints through which we will create and capture order from the front-end.

We need to define two endpoints, one for creating an order, other for capturing an order.

But before that, we need to make a **client** with our required configuration. This client will help us call Paypal Api to create and capture orders. So make a file in 

/utils/paypal/index.js:

```
import checkoutNodeJssdk from '@paypal/checkout-server-sdk'

const configureEnvironment = function () {
  const clientId = process.env.PAYPAL_CLIENT_ID
  const clientSecret = process.env.PAYPAL_CLIENT_SECRET

  return process.env.NODE_ENV === 'production'
    ? new checkoutNodeJssdk.core.LiveEnvironment(clientId, clientSecret)
    : new checkoutNodeJssdk.core.SandboxEnvironment(clientId, clientSecret)
}

const client = function () {
  return new checkoutNodeJssdk.core.PayPalHttpClient(configureEnvironment())
}

export default client
```
Now we have configured our client to use the client ID and client secret that we defined earlier in .env file. These client ID and client secret are required by Paypal to create and capture order.

Now back to our api endpoints.
/api/paypal/createorder.js:

```
import client from 'backend/paypal'
import paypal from '@paypal/checkout-server-sdk'

...
...

export default async function Handler(req, res) {

  if(req.method != "POST")
    return res.status(404).json({success: false, message: "Not Found"})

  if(!req.body.order_price || !req.body.user_id)
    return res.status(400).json({success: false, message: "Please Provide order_price And User ID"})


  try{
    const PaypalClient = client()
    //This code is lifted from https://github.com/paypal/Checkout-NodeJS-SDK
    const request = new paypal.orders.OrdersCreateRequest()
    request.headers['prefer'] = 'return=representation'
    request.requestBody({
      intent: 'CAPTURE',
      purchase_units: [
        {
          amount: {
            currency_code: 'USD',
            value: req.body.order_price+"",
          },
        },
      ],
    })
    const response = await PaypalClient.execute(request)
    if (response.statusCode !== 201) {
      console.log("RES: ", response)
      return res.status(500).json({success: false, message: "Some Error Occured at backend"})
    }

    ...

    // Your Custom Code for doing something with order
    // Usually Store an order in the database like MongoDB

    ...   

    res.status(200).json({success: true, data: {order}})
  } 
  catch(err){
    console.log("Err at Create Order: ", err)
    return res.status(500).json({success: false, message: "Could Not Found the user"})
  }
  
}
```

/api/paypal/captureorder.js
```
import client from 'backend/paypal'
import paypal from '@paypal/checkout-server-sdk'


...

export default async function Handler(req, res) {

  if(req.method != "POST")
    return res.status(404).json({success: false, message: "Not Found"})

  if(!req.body.orderID)
    return res.status(400).json({success: false, message: "Please Provide Order ID"})

  //Capture order to complete payment
  const { orderID } = req.body
  const PaypalClient = client()
  const request = new paypal.orders.OrdersCaptureRequest(orderID)
  request.requestBody({})
  const response = await PaypalClient.execute(request)
  if (!response) {
    return res.status(500).json({success: false, message: "Some Error Occured at backend"})
  }

  ...

  // Your Custom Code to Update Order Status
  // And Other stuff that is related to that order, like wallet
  // Here I am updateing the wallet and sending it back to frontend to update it on frontend

  ...

  res.status(200).json({success: true, data: {wallet}})
}
```


## Front-end Integration
Now open a component where you need to show the Paypal Payment Buttons. 
Import the following:

`import { PayPalScriptProvider, PayPalButtons } from '@paypal/react-paypal-js'`

Move over to the 'return' statement and add the Paypal Payment Buttons by:

```
return (
   ...
   ...
   
   <PayPalScriptProvider
            options={{
              'client-id': process.env.NEXT_PUBLIC_PAYPAL_CLIENT_ID,
              currency: 'USD',
              intent: 'capture'
            }}
          >
            <PayPalButtons
              style={{
                color: 'gold',
                shape: 'rect',
                label: 'pay',
                height: 50
              }}
              createOrder={async (data, actions) => {
                let order_id = await paypalCreateOrder()
                return order_id + ''
              }}
              onApprove={async (data, actions) => {
                let response = await paypalCaptureOrder(data.orderID)
                if (response) return true
              }}
            />
          </PayPalScriptProvider>
   
   ...
   ...
)
```

You can alter the currency and styles of the buttons.
As you can see in the code, we are calling 'paypalCreateOrder' in 'createOrder' and 'paypalCaptureOrder' in 'onApprove'. 

**createOrder** is where we need to define a function which must return an **order id** of the generated order in Paypal.
For that we are defining another function **paypalCaptureOrder** which will call our backend api and return order id of generated order.

**onApprove** is where we define a function which is called when our payment is approved. Here we will call our backend api to capture the order and to update the **wallet** and **order status**.


**paypalCreateOrder:**
```
   ...
   ...

   const paypalCreateOrder = async () => {
    try {
      let response = await axios.post('/api/paypal/createorder', {
        user_id: store.getState().auth.user._id,
        order_price: amountRef.current.value
      })
      return response.data.data.order.order_id
    } catch (err) {
      // Your custom code to show an error like showing a toast:
      // toast.error('Some Error Occured')
      return null
    }
  }

   ...
   ...
```

**paypalCreateOrder** is the function which will call our backend api which is responsible for creating an order in Paypal.




**paypalCaptureOrder:**
```
   ...
   ...

   const paypalCaptureOrder = async orderID => {
    try {
      let response = await axios.post('/api/paypal/captureorder', {
        orderID
      })
      if (response.data.success) {
        // Order is successful
        // Your custom code
        
        // Like showing a success toast:
        // toast.success('Amount Added to Wallet')
        
        // And/Or Adding Balance to Redux Wallet
        // dispatch(setWalletBalance({ balance: response.data.data.wallet.balance }))
    } catch (err) {
      // Order is not successful
      // Your custom code

      // Like showing an error toast
      // toast.error('Some Error Occured')
    }
  }

   ...
   ...
```

**paypalCaptureOrder** is the function which will call our backend api which is responsible for capturing an order, i.e fulfilling an order, in Paypal.

Our Buttons will look like this:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hpaed0t4ym2of9fqu6o9.png)

**Note:** Don't forget to replace the back-end endpoints with your back-end api endpoints in these two functions.

## Testing
Now you can test the paypal integration with sandbox personal account's email and password which you will find in:
1. Sandbox Accounts:
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wro7aguqwmxaj138wgyj.png)

2. Under Accounts, select Personal Account:
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mibfn54clpoqywk3fttw.png)

3. Finally use the credentials to make the payment:
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q7qyy0kvoj6nmo98dxx5.png)



---
That's all
If you got any questions, feel free to ask.