Set up paypal sandbox environment using NextJS

sign in paypal developer account

  • Go to Paypal Developer and register an account.
  • Create a sandbox app and retrieve Client ID and Secret here
  • create a file named .env in the project directory. and copy the following settings in the file.
PAYPAL_CLIENT_ID = "<your-client-id>"
PAYPAL_CLIENT_SECRET = "<your-secret>"
NEXT_PUBLIC_PAYPAL_CLIENT_ID = "<your-client-id>"
  • create another sandbox app and retrieve the email and password in the account info of that sandbox app.
  • after click paypal button, enter the account info mentioned above to test the payment functionality.

Implement paypal component

in components/paypal.tsx


import React from "react";
import {
    PayPalButtons,
    PayPalScriptProvider,
    usePayPalScriptReducer,
} from "@paypal/react-paypal-js";

interface Order {
    id: string;
    // Add other order properties as needed
}

// interface OrderData {
//     // Add order data properties as needed
// }

async function createOrder(): Promise<string> {
    // replace this url with your server
    const response = await fetch(
        // replace this url with your server
        "/api/paypal/createOrder",
        {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            },
            // use the "body" param to optionally pass additional order information
            // like product ids and quantities
            body: JSON.stringify({
                currency_code: 'AUD',
                value: '100.00'
            }),
        }
    );

    const order: Order = await response.json();

    console.log(order)

    // Your code here after creating the order
    return order.id;
}

async function onApprove(data: { orderID: string }): Promise<void> {
    // replace this url with your server
    const response = await fetch(
        // replace this url with your server
        "/api/paypal/captureOrder",
        {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify({
                orderID: data.orderID,
            }),
        }
    );

    const orderData: Order = await response.json();
    console.log(orderData)


    // Your code here after capturing the order
}

// Custom component to wrap the PayPalButtons and show loading spinner
const ButtonWrapper: React.FC<{ showSpinner: boolean }> = ({showSpinner}) => {
    const [{isPending}] = usePayPalScriptReducer();

    return (
        <>
            {showSpinner && isPending && <div className="spinner"/>}
            <PayPalButtons
                fundingSource="paypal"
                style={{layout: "horizontal"}}
                disabled={false}
                forceReRender={[{layout: "horizontal"}]}
                createOrder={createOrder}
                onApprove={onApprove}
            />
        </>
    );
};
const Paypal: React.FC = () => {
    return (

        <PayPalScriptProvider
            options={{
                clientId: process.env["NEXT_PUBLIC_PAYPAL_CLIENT_ID"] as string,
                components: "buttons",
                currency: "AUD"
            }}
        >
            {/* <PayPalButtons fundingSource="paypal" style={{layout: "vertical", label: "donate"}} createOrder={createOrder} */}
            {/*                onApprove={onApprove}/> */}
            <ButtonWrapper showSpinner={false}/>
        </PayPalScriptProvider>

    );
};

export default Paypal;

Implement backend checkout server

In pages/api/paypal/client.ts

import checkoutNodeJssdk from "@paypal/checkout-server-sdk";

const configureEnvironment = () => {
    const clientId = process.env.PAYPAL_CLIENT_ID as string;
    const clientSecret = process.env.PAYPAL_CLIENT_SECRET as string;

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

const client = () =>
    new checkoutNodeJssdk.core.PayPalHttpClient(configureEnvironment());

export default client;

In pages/api/paypal/captureOrder.ts

import type {NextApiRequest, NextApiResponse} from "next";
import paypal from "@paypal/checkout-server-sdk";

import client from "./client";


export default async function handler(
    req: NextApiRequest,
    res: NextApiResponse
) {
    if (req.method != "POST")
        return res.status(404).json({success: false, message: "Not Found"})


    // Capture order to complete payment
    const {orderID} = req.body;
    const PaypalClient = client();
    const request = new paypal.orders.OrdersCaptureRequest(orderID);
    const response = await PaypalClient.execute(request);
    if (!response) {
        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

    res.status(200).json({success: true, data: response.result});
}

In pages/api/paypal/createOrder.ts

import type {NextApiRequest, NextApiResponse} from "next";
import paypal from "@paypal/checkout-server-sdk";

import client from "./client";

export default async function handler(
    req: NextApiRequest,
    res: NextApiResponse
) {

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

    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: req.body.currency_code,
                    value: req.body.value,
                },
            },
        ],
    });
    const response = await PaypalClient.execute(request);
    if (response.statusCode !== 201) {
        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, id: response.result.id});
}