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:
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.
Under Sandbox, click Create App
Provide an app name (e.g., "Checkout App")
PayPal generates:
- Client ID
- Client Secret
These will authenticate all PayPal API requests.
2. Setting Up the Node.js Project
Initialize your project:
cd paypal-api
npm init -y
Install required dependencies:
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.
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:
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/create-order | Creates a PayPal order |
| POST | /api/capture-order | Captures an approved payment |
5. Creating the Frontend (index.html)
The frontend loads the PayPal SDK and communicates with the backend using fetch.
<!DOCTYPE html>
<html>
<body>
<h2>PayPal Checkout Integration</h2>
<script src="https://www.paypal.com/sdk/js?client-id=YOUR_CLIENT_ID¤cy=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:
node server.js
Open index.html in your browser.
You should now be able to:
- Generate a PayPal order
- Approve payment in the PayPal popup
- 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!