On this page
No Headings
Last updated: June 30, 2026
Note: This integration uses the PayPal JavaScript SDK v5, which remains supported.
For a faster, more secure integration, use the latest JavaScript SDK v6.
Integrate PayPal Checkout to show the PayPal payment buttons. To extend your integration see Customize your buyers' experience.
After you integrate PayPal Checkout, you can also offer the following options with some additional configuration:
This integration guide follows the code in this GitHub sample.
PayPal uses the following REST API credentials, which you can get from the developer dashboard:
You can use Postman to explore and test PayPal APIs. Learn more in our Postman guide.
Complete the following steps to set up your development environment.
This sample integration uses Node.js. You'll need to install npm to run the sample application. For more info, visit npm's documentation.
You'll need to install the following third-party libraries to set up your integration. This sample command installs all libraries at the same time:
npm install dotenv express node-fetch
| Third-party libraries | Description |
|---|---|
| dotenv | Separates your configuration and code by loading environment variables from a .env file into process.env. |
| express | This lean Node.js web application framework supports web and mobile applications. |
| node-fetch | This function helps you make API requests, similar to window.fetch. |
A package.json file lists the packages and version numbers your app needs. You can share your package.json file with other developers so they can use the same settings as you.
The following code sample shows a package.json file for a PayPal integration. Compare this sample to the paste in your project:
{
"name": "paypal-standard-integration",
"description": "Sample Node.js web app to integrate PayPal Standard Checkout for online payments",
"version": "1.0.0",
"main": "server/server.js",
"type": "module",
"scripts":
{
"test": "echo \"Error: no test specified\" && exit 1",
"start": "nodemon server/server.js",
"format": "npx prettier --write **/*.{js,md}",
"format:check": "npx prettier --check **/*.{js,md}",
"lint": "npx eslint server/*.js --env=node && npx eslint client/*.js --env=browser"
},
"license": "Apache-2.0",
"dependencies":
{
"dotenv": "^16.3.1",
"express": "^4.18.2",
"node-fetch": "^3.3.2"
},
"devDependencies":
{
"nodemon": "^3.0.1"
}
}Replace YOUR-SERVER-NAME.js in main with the name of your server file on lines 5 and 9.
If you're having trouble with your app, reinstall your local library and package files using npm install.
If you're getting the following node error, include "type": "module" in your package.json file. This line isn't automatically added when package.json is created.
Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension (Use node --trace-warnings ... to show where the warning was created)
See line 6 of the sample package.json file for an example.
A .env file is a line-delimited text file that sets your local working environment variables. Use this .env file to securely pass the client ID and client secret for your app.
The following code shows an example .env file. Replace the PAYPAL-CLIENT-ID and PAYPAL-CLIENT-SECRET with values from your app:
PAYPAL_CLIENT_ID=YOUR_CLIENT_ID_GOES_HERE
PAYPAL_CLIENT_SECRET=YOUR_SECRET_GOES_HERENote: View your client ID and client secret in the PayPal Developer Dashboard under Apps & Credentials.
This section explains how to set up your back end to integrate Paypal Checkout.
This sample shows how to generate a PayPal-Auth-Assertion header, add it to a node.js file, and set up a /server/server.js file to integrate with PayPal Checkout.
Pass the PayPal-Auth-Assertion header with standard Content-Type, Authorization, and PayPal-Request-ID headers. Copy and modify the following code to generate the PayPal-Auth-Assertion header.
const clientId = "CLIENT-ID";
const sellerPayerId = "SELLER-PAYER-ID"
const jwt = getAuthAssertionValue(clientId, sellerPayerId);
console.log(jwt);
function getAuthAssertionValue(clientId, sellerPayerId) {
const header = {
"alg": "none"
};
const encodedHeader = base64url(header);
const payload = {
"iss": clientId,
"payer_id": sellerPayerId
};
const encodedPayload = base64url(payload);
return `${encodedHeader}.${encodedPayload}.`;
}
function base64url(json) {
return btoa(JSON.stringify(json))
.replace(/=+$/, '')
.replace(/\+/g, '-')
.replace(/\//g, '_');
}The preceding example contains period characters ( . ), which are required.
CLIENT-ID with the client ID of the platform or marketplace from the PayPal developer dashboard.SELLER-PAYER-ID with the payer ID or the email of the receiving seller's PayPal account.Example functions to generate the PayPal-Auth-Assertion header:
const clientID = "Acuy17p2LcOf9RMv8SUVBb3wic3FPEP2NHFFqfSCBRFrNFdmbC1JQ0w8HIKRxW3RDy2R8QTL93eptFYl";
const merchantIDOrEmail = "identity_seller@paypal.com";
const auth1 = Buffer.from('{"alg":"none"}').toString("base64");
const auth2 = Buffer.from(`{"iss":${clientID},"payer_id":${merchantIDOrEmail}}`).toString("base64");
const authAssertionHeader = `${auth1}.${auth2}.`;The following code sample uses the server.js file to set up the back-end to integrate with PayPal Checkout.
import express from "express";
import fetch from "node-fetch";
import "dotenv/config";
import path from "path";
const
{
PAYPAL_CLIENT_ID,
PAYPAL_CLIENT_SECRET,
PORT = 8888
} = process.env;
const base = "https://api-m.sandbox.paypal.com";
const app = express();
// host static files
app.use(express.static("client"));
// parse post params sent in body in json format
app.use(express.json());
/**
* Generate an OAuth 2.0 access token for authenticating with PayPal REST APIs.
* @see /api/rest/authentication
*/
const generateAccessToken = async () =>
{
try
{
if (!PAYPAL_CLIENT_ID || !PAYPAL_CLIENT_SECRET)
{
throw new Error("MISSING_API_CREDENTIALS");
}
const auth = Buffer.from(
PAYPAL_CLIENT_ID + ":" + PAYPAL_CLIENT_SECRET,
).toString("base64");
const response = await fetch(`${base}/v1/oauth2/token`,
{
method: "POST",
body: "grant_type=client_credentials",
headers:
{
Authorization: `Basic ${auth}`,
},
});
const data = await response.json();
return data.access_token;
}
catch (error)
{
console.error("Failed to generate Access Token:", error);
}
};
/**
* Create an order to start the transaction.
* @see /api/orders/v2/orders-create
*/
const createOrder = async (cart) =>
{
// use the cart information passed from the front-end to calculate the purchase unit details
console.log(
"shopping cart information passed from the frontend createOrder() callback:",
cart,
);
const accessToken = await generateAccessToken();
const url = `${base}/v2/checkout/orders`;
const payload = {
intent: "CAPTURE",
purchase_units: [
{
amount:
{
currency_code: "USD",
value: "100.00",
},
}, ],
};
const response = await fetch(url,
{
headers:
{
"Content-Type": "application/json",
Authorization: `Bearer ${accessToken}`,
// Uncomment one of these to force an error for negative testing (in sandbox mode only). Documentation:
// https://developer.paypal.com/negative-testing/request-headers
// "PayPal-Mock-Response": '{"mock_application_codes": "MISSING_REQUIRED_PARAMETER"}'
// "PayPal-Mock-Response": '{"mock_application_codes": "PERMISSION_DENIED"}'
// "PayPal-Mock-Response": '{"mock_application_codes": "INTERNAL_SERVER_ERROR"}'
},
method: "POST",
body: JSON.stringify(payload),
});
return handleResponse(response);
};
/**
* Capture payment for the created order to complete the transaction.
* @see /api/orders/v2/orders-capture
*/
const captureOrder = async (orderID) =>
{
const accessToken = await generateAccessToken();
const url = `${base}/v2/checkout/orders/${orderID}/capture`;
const response = await fetch(url,
{
method: "POST",
headers:
{
"Content-Type": "application/json",
Authorization: `Bearer ${accessToken}`,
// Uncomment one of these to force an error for negative testing (in sandbox mode only). Documentation:
// https://developer.paypal.com/negative-testing/request-headers
// "PayPal-Mock-Response": '{"mock_application_codes": "INSTRUMENT_DECLINED"}'
// "PayPal-Mock-Response": '{"mock_application_codes": "TRANSACTION_REFUSED"}'
// "PayPal-Mock-Response": '{"mock_application_codes": "INTERNAL_SERVER_ERROR"}'
},
});
return handleResponse(response);
};
async function handleResponse(response)
{
try
{
const jsonResponse = await response.json();
return {
jsonResponse,
httpStatusCode: response.status,
};
}
catch (err)
{
const errorMessage = await response.text();
throw new Error(errorMessage);
}
}
app.post("/api/orders", async (req, res) =>
{
try
{
// use the cart information passed from the front-end to calculate the order amount detals
const
{
cart
} = req.body;
const
{
jsonResponse,
httpStatusCode
} = await createOrder(cart);
res.status(httpStatusCode).json(jsonResponse);
}
catch (error)
{
console.error("Failed to create order:", error);
res.status(500).json(
{
error: "Failed to create order."
});
}
});
app.post("/api/orders/:orderID/capture", async (req, res) =>
{
try
{
const
{
orderID
} = req.params;
const
{
jsonResponse,
httpStatusCode
} = await captureOrder(orderID);
res.status(httpStatusCode).json(jsonResponse);
}
catch (error)
{
console.error("Failed to create order:", error);
res.status(500).json(
{
error: "Failed to capture order."
});
}
});
// serve index.html
app.get("/", (req, res) =>
{
res.sendFile(path.resolve("./client/checkout.html"));
});
app.listen(PORT, () =>
{
console.log(`Node server listening at http://localhost:${PORT}/`);
});The following section explains key parts of the server.js code sample.
In the back-end code, you'll need to modify as follows:
BN-CODE to your PayPal Partner Attribution ID.ACCESS-TOKEN to your access token.PAYPAL-AUTH-ASSERTION to the Paypal-auth-assertion header you generated in the Generate a PayPal-Auth-Assertion header section.This section of code imports the dotenv dependency, the express module, the node fetch module and the path module.
import express from "express";
import fetch from "node-fetch";
import "dotenv/config";
import path from "path";This section of code sets up the port to run your server and starts the express Node.js web application framework. It also retreives variables and sets the base sandbox URL.
const { PAYPAL_CLIENT_ID, PAYPAL_CLIENT_SECRET, PORT = 8888 } = process.env;
const base = "https://api-m.sandbox.paypal.com";
const app = express();This section of the code defines a file directory for static files and calls the express.json function to parse JSON response bodies.
// host static files
app.use(express.static("client"));
// parse post params sent in body in json format
app.use(express.json());You need an access token to authenticate all REST API requests. The following code sample makes a POST call to the /v1/oauth2/ token endpoint to create an access token.
/**
* Generate an OAuth 2.0 access token for authenticating with PayPal REST APIs.
* @see /api/rest/authentication
*/
const generateAccessToken = async () =>
{
try
{
if (!PAYPAL_CLIENT_ID || !PAYPAL_CLIENT_SECRET)
{
throw new Error("MISSING_API_CREDENTIALS");
}
const auth = Buffer.from(
PAYPAL_CLIENT_ID + ":" + PAYPAL_CLIENT_SECRET,
).toString("base64");
const response = await fetch(`${base}/v1/oauth2/token`,
{
method: "POST",
body: "grant_type=client_credentials",
headers:
{
Authorization: `Basic ${auth}`,
},
});
const data = await response.json();
return data.access_token;
}
catch (error)
{
console.error("Failed to generate Access Token:", error);
}
};PAYPAL_CLIENT_ID and PAYPAL_CLIENT_SECRET as a key-value pair.POST call to the /v1/oauth2/token API endpoint to generate an access token.access_token.Create an order to start a payment between a payer and a seller by making a POST request to /v2/checkout/orders.
If you process payments that require Strong Customer Authentication, you need to provide additional context with payment indicators.
/**
* Create an order to start the transaction.
* @see /api/orders/v2/orders-create
*/
const createOrder = async (cart) =>
{
// use the cart information passed from the front-end to calculate the purchase unit details
console.log(
"shopping cart information passed from the frontend createOrder() callback:",
cart,
);
const accessToken = await generateAccessToken();
const url = `${base}/v2/checkout/orders`;
const payload = {
intent: "CAPTURE",
purchase_units: [
{
amount:
{
currency_code: "USD",
value: "100.00",
},
}, ],
};
const response = await fetch(url,
{
headers:
{
"Content-Type": "application/json",
Authorization: `Bearer ${accessToken}`,
// Uncomment one of these to force an error for negative testing (in sandbox mode only). Documentation:
// https://developer.paypal.com/negative-testing/request-headers
// "PayPal-Mock-Response": '{"mock_application_codes": "MISSING_REQUIRED_PARAMETER"}'
// "PayPal-Mock-Response": '{"mock_application_codes": "PERMISSION_DENIED"}'
// "PayPal-Mock-Response": '{"mock_application_codes": "INTERNAL_SERVER_ERROR"}'
"PayPal - Partner - Attribution - Id": "BN - CODE",
"PayPal - Auth - Assertion": "PAYPAL - AUTH - ASSERTION",
},
method: "POST",
body: JSON.stringify(payload),
});
return handleResponse(response);
};createOrder function and uses the cart information from the front-end to calculate the purchase units for the order.accessToken from the generateAccessToken() function later in the API call.POST request to the Orders v2 API, using the accessToken.See the Create order endpoint of the PayPal Orders v2 API for sample responses and other details.
Payment processors return processor response codes when they receive a transaction request. For advanced card payments, the code displays in the authorization object under the response_code field.
"processor_response": {
"avs_code": "Y",
"cvv_code": "S",
"payment_advice_code": "",
"response_code": "0000"
}If an external payment processor declines a transaction, PayPal returns a HTTP 201 CREATED status code and a status of DECLINED in the capture status.
See the Orders API response_code object to get the processor response code for the non-PayPal payment processor errors.
Capture an order to move money from the payer to the merchant by making a POST call to the /v2/checkout/orders/ORDER-ID/capture endpoint.
/**
* Capture payment for the created order to complete the transaction.
* @see /api/orders/v2/orders-capture
*/
const captureOrder = async (orderID) =>
{
const accessToken = await generateAccessToken();
const url = `${base}/v2/checkout/orders/${orderID}/capture`;
const response = await fetch(url,
{
method: "POST",
headers:
{
"Content-Type": "application/json",
Authorization: `Bearer ${accessToken}`,
// Uncomment one of these to force an error for negative testing (in sandbox mode only). Documentation:
// https://developer.paypal.com/negative-testing/request-headers
// "PayPal-Mock-Response": '{"mock_application_codes": "INSTRUMENT_DECLINED"}'
// "PayPal-Mock-Response": '{"mock_application_codes": "TRANSACTION_REFUSED"}'
// "PayPal-Mock-Response": '{"mock_application_codes": "INTERNAL_SERVER_ERROR"}'
"PayPal - Partner - Attribution - Id": "BN - CODE",
"PayPal - Auth - Assertion": "PAYPAL - AUTH - ASSERTION",
},
});accessToken from the generateAccessToken() function later in the API call.POST call to the /v2/checkout/orders/ORDER-ID/capture endpoint to capture the order, using the accessToken.This code sample shows a response to a POST call to /v2/checkout/orders/ORDER-ID/capture. The response is the orderData retrieved in the create order section.
{
"id": "5O190127TN364715T",
"status": "COMPLETED",
"payment_source":
{
"paypal":
{
"name":
{
"given_name": "Firstname",
"surname": "Lastname"
},
"email_address": "payer@example.com",
"account_id": "QYR5Z8XDVJNXQ",
},
},
"purchase_units": [
{
"reference_id": "d9f80740-38f0-11e8-b467-0ed5f89f718b",
"shipping":
{
"address":
{
"address_line_1": "123 Main St.",
"admin_area_2": "Anytown",
"admin_area_1": "CA",
"postal_code": "12345",
"country_code": "US",
},
},
"payments":
{
"captures": [
{
"id": "3C679366HH908993F",
"status": "COMPLETED",
"amount":
{
"currency_code": "USD",
"value": "100.00",
},
"seller_protection":
{
"status": "ELIGIBLE",
"dispute_categories": [
"ITEM_NOT_RECEIVED",
"UNAUTHORIZED_TRANSACTION",
],
},
"final_capture": true,
"disbursement_mode": "INSTANT",
"seller_receivable_breakdown":
{
"gross_amount":
{
"currency_code": "USD",
"value": "100.00",
},
"paypal_fee":
{
"currency_code": "USD",
"value": "3.00",
},
"net_amount":
{
"currency_code": "USD",
"value": "97.00",
},
},
"create_time": "2018-04-01T21:20:49Z",
"update_time": "2018-04-01T21:20:49Z",
"links": [
{
"href": "https://api-m.paypal.com/v2/payments/captures/3C679366HH908993F",
"rel": "self",
"method": "GET",
},
{
"href": "https://api-m.paypal.com/v2/payments/captures/3C679366HH908993F/refund",
"rel": "refund",
"method": "POST",
}, ],
}, ],
},
}, ],
"payer":
{
"name":
{
"given_name": "Firstname",
"surname": "Lastname",
},
"email_address": "payer@example.com",
"payer_id": "QYR5Z8XDVJNXQ",
},
"links": [
{
"href": "https://api-m.paypal.com/v2/checkout/orders/5O190127TN364715T",
"rel": "self",
"method": "GET",
}, ],
}orderData object.purchase_units in this transaction. Each purchase unit represents either a full or partial order and establishes a contract between a payer and a merchant.reference_id that identifies the purchase unit in this payment response.id, amount, disbursement_mode, and net_amount.The handleResponse function sets up a listener for API responses.
async function handleResponse(response) {
try {
const jsonResponse = await response.json();
return {
jsonResponse,
httpStatusCode: response.status,
};
} catch (err) {
const errorMessage = await response.text();
throw new Error(errorMessage);
}
}
app.post("/api/orders", async (req, res) => {
try {
// use the cart information passed from the front-end to calculate the order amount detals
const { cart } = req.body;
const { jsonResponse, httpStatusCode } = await createOrder(cart);
res.status(httpStatusCode).json(jsonResponse);
} catch (error) {
console.error("Failed to create order:", error);
res.status(500).json({ error: "Failed to create order." });
}
});
app.post("/api/orders/:orderID/capture", async (req, res) => {
try {
const { orderID } = req.params;
const { jsonResponse, httpStatusCode } = await captureOrder(orderID);
res.status(httpStatusCode).json(jsonResponse);
} catch (error) {
console.error("Failed to create order:", error);
res.status(500).json({ error: "Failed to capture order." });
}
});POST call to the api/orders/ endpoint and returns an HTTP status code response. Errors status codes send an error message.POST call to the api/orders/:orderID/capture endpoint and returns an HTTP status code response for the particular order. Error status codes send an error message.Set up your front-end to integrate PayPal Checkout payments.
This example uses the checkout.html file to show how to set up the front end to integrate standard payments.
/client/checkout.html handles the client-side logic and defines how the PayPal front-end components connect with the back end. Use this file to set up the PayPal checkout using the JavaScript SDK and handle the payer's interactions with the PayPal checkout button.
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PayPal JS SDK Standard Integration</title>
</head>
<body>
<div id="paypal-button-container"></div>
<p id="result-message"></p>
<!-- Replace the "test" client-id value with your client-id -->
<script src="https://www.paypal.com/sdk/js?client-id={CLIENT_ID}&merchant-id={SELLER_PAYER_ID}&components=buttons" data-partner-attribution-id="{PARTNER_BN_CODE}"></script>
<script src="app.js"></script>
</body>
</html>This section explains the checkout.html code sample.
This code sample calls the PayPal JavaScript SDK
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PayPal JS SDK Standard Integration</title>
</head>
<body>
<div id="paypal-button-container"></div>
<p id="result-message"></p>
<!-- Replace the "test" client-id value with your client-id -->
<script src="https://www.paypal.com/sdk/js?client-id={CLIENT_ID}&merchant-id={SELLER_PAYER_ID}&components=buttons" data-partner-attribution-id="{PARTNER_BN_CODE}"></script>
<script src="app.js"></script>
</body>
</html>This section explains the app.js code sample.
This code sample calls the JavaScript SDK that defines the PayPal buttons.
window.paypal.Buttons({{…}}).render("#paypal-button-container");This code sample defines the createOrder() function.
async createOrder() {
try {
const response = await fetch("/api/orders", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer ACCESS-TOKEN",
"PayPal-Partner-Attribution-Id": "BN-CODE"
"PayPal-Auth-Assertion": "PAYPAL-AUTH-ASSERTION"
},
// use the "body" param to optionally pass additional order information
// like product ids and quantities
body: JSON.stringify({
cart: [
{
id: "YOUR_PRODUCT_ID",
quantity: "YOUR_PRODUCT_QUANTITY",
},
],
}),
});
const orderData = await response.json();
if (orderData.id) {
return orderData.id;
} else {
const errorDetail = orderData?.details?.[0];
const errorMessage = errorDetail
? `${errorDetail.issue} ${errorDetail.description} (${orderData.debug_id})`
: JSON.stringify(orderData);
throw new Error(errorMessage);
}
} catch (error) {
console.error(error);
resultMessage(`Could not initiate PayPal Checkout...<br><br>${error}`);
}
},api/orders endpointcreateOrder section of the JavaScript SDK reference guide for more information about creating orders. See the Orders REST API documentation for additional customization options.This code sample defines a POST call to /api/orders/orderID/capture. The orderID is passed from the SDK when you run the onCreate() call.
async onApprove(data, actions) {
try {
const response = await fetch(/api/orders / $ {
data.orderID
}
/capture, {
method: "POST",
headers: {
"Content - Type": "application / json",
"Authorization": "Bearer ACCESS-TOKEN",
"PayPal - Partner - Attribution - Id": "BN - CODE"" PayPal - Auth - Assertion": "PAYPAL - AUTH - ASSERTION"
},
}
);
const orderData = await response.json();
// Three cases to handle:
// (1) Recoverable INSTRUMENT_DECLINED -> call actions.restart()
// (2) Other non-recoverable errors -> Show a failure message
// (3) Successful transaction -> Show confirmation or thank you message
const errorDetail = orderData?.details?.[0];
if (errorDetail?.issue === "INSTRUMENT_DECLINED") {
// (1) Recoverable INSTRUMENT_DECLINED -> call actions.restart()
// recoverable state, per /checkout/handle-funding-failure/
return actions.restart();
}
else if (errorDetail) {
// (2) Other non-recoverable errors -> Show a failure message
throw new Error(`${errorDetail.description} (${orderData.debug_id})`);
}
else if (!orderData.purchase_units) {
throw new Error(JSON.stringify(orderData));
}
else {
// (3) Successful transaction -> Show confirmation or thank you message
// Or go to another URL: actions.redirect('thank_you.html');
const transaction =
orderData?.purchase_units?.[0]?.payments?.captures?.[0] ||
orderData?.purchase_units?.[0]?.payments?.authorizations?.[0];
resultMessage(
`Transaction ${transaction.status}: ${transaction.id}<br><br>See console for all available details`,
);
console.log(
"Capture result",
orderData,
JSON.stringify(orderData, null, 2),
);
}
}
catch (error) {
console.error(error);
resultMessage(
`Sorry, your transaction could not be processed...<br><br>${error}`,
);
}
},
})api/orders/capture endpointapi/orders/capture call.This code sample defines an example resultMessage() function.
// Example function to show a result to the user. Your site's UI library can be used instead.function resultMessage(message) {const container = document.querySelector("#result-message");container.innerHTML = message;This code sample renders the PayPal buttons.
.render("#paypal-button-container");npm start to run your server again.localhost:8888
Before going live, test your integration in the sandbox environment.
Learn more about the following resources on card testing:
Use the credit card generator to generate test credit cards for sandbox testing.
Test the following use cases before going live:
Confirm the money reached the business account
Follow this checklist to take your application live:
See Move your app to production for more details.
Add more payment methods or customize your integration.
Payers buy now and pay in installments.
Add the Venmo button to your checkout integration.
Support local payment methods across the globe.
Add Apple Pay as a payment method.
Captures payment for an order.
Refund all or part of a captured payment.
Reduce declines by getting card updates from the issuer.
Customize your integration with script config parameters.