Skip to content

Building a PayPal Payment API Using Express (ES6) and a Fetch-Based Frontend

Integrating PayPal into your application is one of the most effective ways to accept secure and globally recognized online payments. This article walks you through creating a complete PayPal Checkout system using:

  • Express.js (ES6 modules)
  • PayPal REST API (v2 Orders API)
  • A simple index.html frontend using Fetch
  • Environment variables for security (.env)

You will also learn how to obtain your PayPal Client ID and Client Secret from the PayPal Developer Dashboard.


1. Getting Your PayPal Sandbox Credentials

Before writing any code, you need your PayPal API credentials.

Step 1 — Open the PayPal Developer Dashboard

Visit:

https://developer.paypal.com

Log in using your PayPal account.

Step 2 — Create Sandbox Test Accounts

Go to Sandbox → Accounts. PayPal automatically generates two accounts:

  • A Business (merchant) account
  • A Personal (buyer) account

You may create additional accounts if needed.

Step 3 — Create a REST API App

Navigate to My Apps & Credentials.

  1. Under Sandbox, click Create App

  2. Provide an app name (e.g., "Checkout App")

  3. PayPal generates:

    • Client ID
    • Client Secret

These will authenticate all PayPal API requests.


2. Setting Up the Node.js Project

Initialize your project:

bash\mkdir
cd paypal-api
npm init -y

Install required dependencies:

bash
npm install express cors axios dotenv

3. Configuring the .env File

Create a .env file in your project:

PAYPAL_CLIENT_ID=YOUR_CLIENT_ID
PAYPAL_CLIENT_SECRET=YOUR_CLIENT_SECRET
PAYPAL_API=https://sandbox.paypal.com
PORT=3000

Important: Use the correct PayPal API URL:

  • Sandbox: https://sandbox.paypal.com
  • Live: https://api-m.paypal.com

Avoid using https://sandbox.paypal.com—it is not an API endpoint.


4. Creating the Express Backend (ES6)

The following backend handles creating orders and capturing payments.

js
import express from "express";
import cors from "cors";
import axios from "axios";
import dotenv from "dotenv";

dotenv.config();

const app = express();
app.use(cors());
app.use(express.json());

const {PAYPAL_CLIENT_ID, PAYPAL_CLIENT_SECRET, PAYPAL_API, PORT} = process.env;

// Generate PayPal OAuth token
async function generateAccessToken() {
  const auth = Buffer.from(
    `${PAYPAL_CLIENT_ID}:${PAYPAL_CLIENT_SECRET}`
  ).toString("base64");

  const response = await axios.post(
    `${PAYPAL_API}/v1/oauth2/token`,
    "grant_type=client_credentials",
    {
      headers: {
        Authorization: `Basic ${auth}`,
        "Content-Type": "application/x-www-form-urlencoded",
      },
    }
  );

  return response.data.access_token;
}

// Create a PayPal order
app.post("/api/create-order", async (req, res) => {
  try {
    const access_token = await generateAccessToken();

    const order = await axios.post(
      `${PAYPAL_API}/v2/checkout/orders`,
      {
        intent: "CAPTURE",
        purchase_units: [
          {
            amount: {
              currency_code: "USD",
              value: "10.00",
            },
          },
        ],
      },
      {
        headers: {
          Authorization: `Bearer ${access_token}`,
          "Content-Type": "application/json",
        },
      }
    );

    res.json({id: order.data.id});
  } catch (err) {
    console.error(err.response?.data || err);
    res.status(500).json({error: "Failed to create order"});
  }
});

// Capture the PayPal order
app.post("/api/capture-order", async (req, res) => {
  try {
    const {orderID} = req.body;
    const access_token = await generateAccessToken();

    const capture = await axios.post(
      `${PAYPAL_API}/v2/checkout/orders/${orderID}/capture`,
      {},
      {
        headers: {
          Authorization: `Bearer ${access_token}`,
        },
      }
    );

    res.json(capture.data);
  } catch (err) {
    console.error(err.response?.data || err);
    res.status(500).json({error: "Failed to capture order"});
  }
});

app.listen(PORT, () =>
  console.log(`Server running at http://localhost:${PORT}`)
);

This backend provides two endpoints:

MethodEndpointDescription
POST/api/create-orderCreates a PayPal order
POST/api/capture-orderCaptures an approved payment

5. Creating the Frontend (index.html)

The frontend loads the PayPal SDK and communicates with the backend using fetch.

html
<!DOCTYPE html>
<html>
  <body>
    <h2>PayPal Checkout Integration</h2>

    <script src="https://www.paypal.com/sdk/js?client-id=YOUR_CLIENT_ID&currency=USD"></script>

    <div id="paypal-button-container"></div>

    <script>
      paypal
        .Buttons({
          createOrder: async () => {
            const response = await fetch(
              "http://localhost:3000/api/create-order",
              {
                method: "POST",
              }
            );

            const data = await response.json();
            return data.id;
          },

          onApprove: async (data) => {
            const response = await fetch(
              "http://localhost:3000/api/capture-order",
              {
                method: "POST",
                headers: {"Content-Type": "application/json"},
                body: JSON.stringify({orderID: data.orderID}),
              }
            );

            const orderData = await response.json();
            alert("Payment Successful!");
            console.log(orderData);
          },

          onError: (err) => {
            console.error(err);
            alert("Payment failed.");
          },
        })
        .render("#paypal-button-container");
    </script>
  </body>
</html>

Replace YOUR_CLIENT_ID with your PayPal sandbox Client ID.


6. Running the Payment System

Start your backend:

bash
node server.js

Open index.html in your browser.

You should now be able to:

  1. Generate a PayPal order
  2. Approve payment in the PayPal popup
  3. Capture the payment successfully

7. Final Thoughts

You now have a fully working PayPal Checkout integration using Express and the Fetch API. This setup is suitable for:

  • SaaS product payments
  • Digital goods
  • E-commerce checkout pages
  • Donation systems

It is secure, scalable, and easy to expand.

If you want to go further, you can add:

  • Webhook validation
  • Dynamic pricing endpoints
  • Database storage for transactions
  • A frontend built with frameworks like React, Vue, or Angular

Feel free to extend this base to fit your application's needs!