Integrate Checkout

Integrate PayPal Checkout for online payments

SDKCurrentStandardLast updated: April 6th 2022, @ 4:06:12 pm


How it works

Integrate standard Checkout to show the PayPal payment buttons. To extend your integration, see Customize your payers' experience .

After you integrate standard Checkout, you can also offer the following options some additional configuration:

  • Pay Later
  • Venmo
  • PayPal Credit

This integration guide follows the code in this GitHub sample.

Get up and running in GitHub Codespaces

GitHub Codespaces are cloud-based development environments where you can code and test your PayPal integrations. Learn more

Integration video

Watch our video tutorial for this integration:

Know before you code

Required

You need a developer account to get sandbox credentials

  • PayPal uses the following REST API credentials, which you can get from the developer dashboard:
  • Client ID: Authenticates your account with PayPal and identifies an app in your sandbox.
  • Client secret: Authorizes an app in your sandbox. Keep this secret safe and don't share it.
DashboardRead the guide
Required

You'll need both PayPal and third-party tools.

  • JavaScript SDK: Adds PayPal-supported payment methods.
  • Orders REST API: Create, update, retrieve, authorize, and capture orders.
  • npm: Registry used to install third-party libraries.
  • You can use Postman to explore and test PayPal APIs.

Run in postman

1. Before you begin your integration

Set up npm

You'll need to install npm to run the sample application. For more info, visit npm's documentation.

Install third-party libraries

This sample integration using Node.js. 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:

1npm install dotenv express node-fetch
Third-party librariesDescription
dotenvSeparates your configuration and code by loading environment variables from a .env file into process.env.
expressThis lean Node.js web application framework supports web and mobile applications.
node-fetchThis function helps you make API requests, similar to window.fetch.

Verify Package.json

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:

1{
2 "name": "paypal-standard-integration",
3 "description": "Sample Node.js web app to integrate PayPal Standard Checkout for online payments",
4 "version": "1.0.0",
5 "main": "server/YOUR-SERVER-NAME.js",
6 "type": "module",
7 "scripts": {
8 "start": "node server/YOUR-SERVER-NAME.js",
9 },
10 "dependencies": {
11 "dotenv": "^16.3.1",
12 "express": "^4.18.2",
13 "node-fetch": "^3.3.2"
14 },
15}

Replace YOUR-SERVER-NAME.js in main with the name of your server file on lines 5 and 8.

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.

Set up .env

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:

1PAYPAL_CLIENT_ID=YOUR_CLIENT_ID_GOES_HERE
2PAYPAL_CLIENT_SECRET=YOUR_SECRET_GOES_HERE

Note:View your client ID and client secret in the PayPal Developer Dashboard under Apps & Credentials.

2. Integrate back-end

This section explains how to set up your back-end to integrate standard checkout.

Back-end process

  1. Your app creates an order on the back end by making a call to the Create Orders API endpoint.
  2. Your app makes a call to the Capture Payment for Order API endpoint on the back end to move the money when the payer confirms the order.

The following code sample uses the server.js file to set up the back-end to integrate with standard payments.

  1. server/server.js
1import express from "express";
2import fetch from "node-fetch";
3import "dotenv/config";
4import path from "path";
5
6const { PAYPAL_CLIENT_ID, PAYPAL_CLIENT_SECRET, PORT = 8888 } = process.env;
7const base = "https://api-m.sandbox.paypal.com";
8const app = express();
9
10// host static files
11app.use(express.static("client"));
12
13// parse post params sent in body in json format
14app.use(express.json());
15
16/**
17* Generate an OAuth 2.0 access token for authenticating with PayPal REST APIs.
18* @see https://developer.paypal.com/api/rest/authentication/
19*/
20const generateAccessToken = async () => {
21 try {
22 if (!PAYPAL_CLIENT_ID || !PAYPAL_CLIENT_SECRET) {
23 throw new Error("MISSING_API_CREDENTIALS");
24 }
25 const auth = Buffer.from(
26 PAYPAL_CLIENT_ID + ":" + PAYPAL_CLIENT_SECRET,
27 ).toString("base64");
28 const response = await fetch(`${base}/v1/oauth2/token`, {
29 method: "POST",
30 body: "grant_type=client_credentials",
31 headers: {
32 Authorization: `Basic ${auth}`,
33 },
34 });
35
36 const data = await response.json();
37 return data.access_token;
38 } catch (error) {
39 console.error("Failed to generate Access Token:", error);
40 }
41};
42
43/**
44* Create an order to start the transaction.
45* @see https://developer.paypal.com/docs/api/orders/v2/#orders_create
46*/
47const createOrder = async (cart) => {
48 // use the cart information passed from the front-end to calculate the purchase unit details
49 console.log(
50 "shopping cart information passed from the frontend createOrder() callback:",
51 cart,
52 );
53
54 const accessToken = await generateAccessToken();
55 const url = `${base}/v2/checkout/orders`;
56 const payload = {
57 intent: "CAPTURE",
58 purchase_units: [
59 {
60 amount: {
61 currency_code: "USD",
62 value: "100.00",
63 },
64 },
65 ],
66 };
67
68 const response = await fetch(url, {
69 headers: {
70 "Content-Type": "application/json",
71 Authorization: `Bearer ${accessToken}`,
72 // Uncomment one of these to force an error for negative testing (in sandbox mode only). Documentation:
73 // https://developer.paypal.com/tools/sandbox/negative-testing/request-headers/
74 // "PayPal-Mock-Response": '{"mock_application_codes": "MISSING_REQUIRED_PARAMETER"}'
75 // "PayPal-Mock-Response": '{"mock_application_codes": "PERMISSION_DENIED"}'
76 // "PayPal-Mock-Response": '{"mock_application_codes": "INTERNAL_SERVER_ERROR"}'
77 },
78 method: "POST",
79 body: JSON.stringify(payload),
80 });
81
82 return handleResponse(response);
83};
84
85/**
86* Capture payment for the created order to complete the transaction.
87* @see https://developer.paypal.com/docs/api/orders/v2/#orders_capture
88*/
89const captureOrder = async (orderID) => {
90 const accessToken = await generateAccessToken();
91 const url = `${base}/v2/checkout/orders/${orderID}/capture`;
92
93 const response = await fetch(url, {
94 method: "POST",
95 headers: {
96 "Content-Type": "application/json",
97 Authorization: `Bearer ${accessToken}`,
98 // Uncomment one of these to force an error for negative testing (in sandbox mode only). Documentation:
99 // https://developer.paypal.com/tools/sandbox/negative-testing/request-headers/
100 // "PayPal-Mock-Response": '{"mock_application_codes": "INSTRUMENT_DECLINED"}'
101 // "PayPal-Mock-Response": '{"mock_application_codes": "TRANSACTION_REFUSED"}'
102 // "PayPal-Mock-Response": '{"mock_application_codes": "INTERNAL_SERVER_ERROR"}'
103 },
104 });
105
106 return handleResponse(response);
107};
108
109async function handleResponse(response) {
110 try {
111 const jsonResponse = await response.json();
112 return {
113 jsonResponse,
114 httpStatusCode: response.status,
115 };
116 } catch (err) {
117 const errorMessage = await response.text();
118 throw new Error(errorMessage);
119 }
120}
121
122app.post("/api/orders", async (req, res) => {
123 try {
124 // use the cart information passed from the front-end to calculate the order amount detals
125 const { cart } = req.body;
126 const { jsonResponse, httpStatusCode } = await createOrder(cart);
127 res.status(httpStatusCode).json(jsonResponse);
128 } catch (error) {
129 console.error("Failed to create order:", error);
130 res.status(500).json({ error: "Failed to create order." });
131 }
132});
133
134app.post("/api/orders/:orderID/capture", async (req, res) => {
135 try {
136 const { orderID } = req.params;
137 const { jsonResponse, httpStatusCode } = await captureOrder(orderID);
138 res.status(httpStatusCode).json(jsonResponse);
139 } catch (error) {
140 console.error("Failed to create order:", error);
141 res.status(500).json({ error: "Failed to capture order." });
142 }
143});
144
145// serve index.html
146app.get("/", (req, res) => {
147 res.sendFile(path.resolve("./client/checkout.html"));
148});
149
150app.listen(PORT, () => {
151 console.log(`Node server listening at http://localhost:${PORT}/`);
152});

Understand the server.js code sample.

Declare imports

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.

1const { PAYPAL_CLIENT_ID, PAYPAL_CLIENT_SECRET, PORT = 8888 } = process.env;
2const base = "https://api-m.sandbox.paypal.com";
3const app = express();
  • Line 1 uses the DotEnv library to import and declare the client ID and client secret from your .env file.
  • Line 2 declares the base URL for PayPal's sandbox API.

Additional functions

This section of the code defines a file directory for static files and calls the express.json function to parse JSON response bodies.

1// host static files
2app.use(express.static("client"));
3// parse post params sent in body in json format
4app.use(express.json());

Generate access token

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.

1/**
2* Generate an OAuth 2.0 access token for authenticating with PayPal REST APIs.
3* @see https://developer.paypal.com/api/rest/authentication/
4*/
5const generateAccessToken = async () => {
6 try {
7 if (!PAYPAL_CLIENT_ID || !PAYPAL_CLIENT_SECRET) {
8 throw new Error("MISSING_API_CREDENTIALS");
9 }
10 const auth = Buffer.from(
11 PAYPAL_CLIENT_ID + ":" + PAYPAL_CLIENT_SECRET,
12 ).toString("base64");
13 const response = await fetch(`${base}/v1/oauth2/token`, {
14 method: "POST",
15 body: "grant_type=client_credentials",
16 headers: {
17 Authorization: `Basic ${auth}`,
18 },
19 });
20 const data = await response.json();
21 return data.access_token;
22 } catch (error) {
23 console.error("Failed to generate Access Token:", error);
24 }
25};
  • Line 10 combines the PAYPAL_CLIENT_ID and PAYPAL_CLIENT_SECRET as a key-value pair.
  • Lines 13-18 define a response that makes a POST call to the /v1/oauth2/token API endpoint to generate an access token.
  • Lines 20-21 establish a listener to capture the response data from the request and return the access_token.

Create order

Create an order to start a payment between a payer and a seller by making a POST request to /v2/checkout/orders.

Important:If you process payments that require Strong Customer Authentication, you need to provide additional context with payment indicators

1/**
2* Create an order to start the transaction.
3* @see https://developer.paypal.com/docs/api/orders/v2/#orders_create
4*/
5const createOrder = async (cart) => {
6 // use the cart information passed from the front-end to calculate the purchase unit details
7 console.log(
8 "shopping cart information passed from the frontend createOrder() callback:",
9 cart,
10 );
11 const accessToken = await generateAccessToken();
12 const url = `${base}/v2/checkout/orders`;
13 const payload = {
14 intent: "CAPTURE",
15 purchase_units: [
16 {
17 amount: {
18 currency_code: "USD",
19 value: "100.00",
20 },
21 },
22 ],
23 };
24 const response = await fetch(url, {
25 headers: {
26 "Content-Type": "application/json",
27 Authorization: `Bearer ${accessToken}`,
28 // Uncomment one of these to force an error for negative testing (in sandbox mode only). Documentation:
29 // https://developer.paypal.com/tools/sandbox/negative-testing/request-headers/
30 // "PayPal-Mock-Response": '{"mock_application_codes": "MISSING_REQUIRED_PARAMETER"}'
31 // "PayPal-Mock-Response": '{"mock_application_codes": "PERMISSION_DENIED"}'
32 // "PayPal-Mock-Response": '{"mock_application_codes": "INTERNAL_SERVER_ERROR"}'
33 },
34 method: "POST",
35 body: JSON.stringify(payload),
36 });
37 return handleResponse(response);
38};
  • Line 5 calls the createOrder function and uses the cart information from the front-end to calculate the purchase units for the order.
  • Line 12 establishes a listener to capture the accessToken from the generateAccessToken() function later in the API call.
  • Lines 13-38 create an order by sending a POST request to the Orders v2 API, using the accessToken.

Note: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.

1"processor_response": {
2 "avs_code": "Y",
3 "cvv_code": "S",
4 "payment_advice_code": "",
5 "response_code": "0000"
6}

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 payment

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.

1/**
2 * Capture payment for the created order to complete the transaction.
3 * @see https://developer.paypal.com/docs/api/orders/v2/#orders_capture
4 */
5const captureOrder = async (orderID) => {
6 const accessToken = await generateAccessToken();
7 const url = `${base}/v2/checkout/orders/${orderID}/capture`;
8
9 const response = await fetch(url, {
10 method: "POST",
11 headers: {
12 "Content-Type": "application/json",
13 Authorization: `Bearer ${accessToken}`,
14 // Uncomment one of these to force an error for negative testing (in sandbox mode only). Documentation:
15 // https://developer.paypal.com/tools/sandbox/negative-testing/request-headers/
16 // "PayPal-Mock-Response": '{"mock_application_codes": "INSTRUMENT_DECLINED"}'
17 // "PayPal-Mock-Response": '{"mock_application_codes": "TRANSACTION_REFUSED"}'
18 // "PayPal-Mock-Response": '{"mock_application_codes": "INTERNAL_SERVER_ERROR"}'
19 },
20 });
  • Line 5 establishes a listener to capture the accessToken from the generateAccessToken() function later in the API call.
  • Line 7 declares the URL of the Capture API endpoint using the Order ID generated from the Create Order endpoint.
  • Lines 9-13 define a response that makes a POST call to the /v2/checkout/orders/ORDER-ID/capture endpoint to capture the order, using the accessToken.
  • Lines 16-18 include mock responses for negative testing in the sandbox.

Sample capture response

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.

1{
2 "id": "5O190127TN364715T",
3 "status": "COMPLETED",
4 "payment_source": {
5 "paypal": {
6 "name": {
7 "given_name": "Firstname",
8 "surname": "Lastname"
9 },
10 "email_address": "payer@example.com",
11 "account_id": "QYR5Z8XDVJNXQ",
12 },
13 },
14 "purchase_units": [
15 {
16 "reference_id": "d9f80740-38f0-11e8-b467-0ed5f89f718b",
17 "shipping": {
18 "address": {
19 "address_line_1": "123 Main St.",
20 "admin_area_2": "Anytown",
21 "admin_area_1": "CA",
22 "postal_code": "12345",
23 "country_code": "US",
24 },
25 },
26 "payments": {
27 "captures": [
28 {
29 "id": "3C679366HH908993F",
30 "status": "COMPLETED",
31 "amount": {
32 "currency_code": "USD",
33 "value": "100.00",
34 },
35 "seller_protection": {
36 "status": "ELIGIBLE",
37 "dispute_categories": [
38 "ITEM_NOT_RECEIVED",
39 "UNAUTHORIZED_TRANSACTION",
40 ],
41 },
42 "final_capture": true,
43 "disbursement_mode": "INSTANT",
44 "seller_receivable_breakdown": {
45 "gross_amount": {
46 "currency_code": "USD",
47 "value": "100.00",
48 },
49 "paypal_fee": {
50 "currency_code": "USD",
51 "value": "3.00",
52 },
53 "net_amount": {
54 "currency_code": "USD",
55 "value": "97.00",
56 },
57 },
58 "create_time": "2018-04-01T21:20:49Z",
59 "update_time": "2018-04-01T21:20:49Z",
60 "links": [
61 {
62 "href": "https://api-m.paypal.com/v2/payments/captures/3C679366HH908993F",
63 "rel": "self",
64 "method": "GET",
65 },
66 {
67 "href": "https://api-m.paypal.com/v2/payments/captures/3C679366HH908993F/refund",
68 "rel": "refund",
69 "method": "POST",
70 },
71 ],
72 },
73 ],
74 },
75 },
76 ],
77 "payer": {
78 "name": {
79 "given_name": "Firstname",
80 "surname": "Lastname",
81 },
82 "email_address": "payer@example.com",
83 "payer_id": "QYR5Z8XDVJNXQ",
84 },
85 "links": [
86 {
87 "href": "https://api-m.paypal.com/v2/checkout/orders/5O190127TN364715T",
88 "rel": "self",
89 "method": "GET",
90 },
91 ],
92}
  • Line 2 shows the ID for this orderData object.
  • Lines 4-13 pass details about the payment source.
  • Lines 14-76 pass the 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.
  • Line 16 passes the reference_id that identifies the purchase unit in this payment response.
  • Lines 17-24 pass details about the shipping address.
  • Line 26 declares the payments object that passes the payment details for this capture request.
  • Line 27 declares the captures object that passes details about the captured payments for this request.
  • Lines 28-57 pass the payment capture details, such as the capture identifier id, amount, disbursement_mode, and net_amount.
  • Lines 60-71 pass the HATEOAS details of the capture response. See the REST API Response Reference for more details about HATEOAS.
  • Lines 77-84 pass details about the payer.
  • Lines 85-91 pass the HATEOAS details for the orders response.

Note:Visit the Capture Payment endpoint of the PayPal Orders v2 API to see sample responses and other details.

Handle responses

The handleResponse function sets up a listener for API responses.

1async function handleResponse(response) {
2 try {
3 const jsonResponse = await response.json();
4 return {
5 jsonResponse,
6 httpStatusCode: response.status,
7 };
8 } catch (err) {
9 const errorMessage = await response.text();
10 throw new Error(errorMessage);
11 }
12 }
13
14 app.post("/api/orders", async (req, res) => {
15 try {
16 // use the cart information passed from the front-end to calculate the order amount detals
17 const { cart } = req.body;
18 const { jsonResponse, httpStatusCode } = await createOrder(cart);
19 res.status(httpStatusCode).json(jsonResponse);
20 } catch (error) {
21 console.error("Failed to create order:", error);
22 res.status(500).json({ error: "Failed to create order." });
23 }
24 });
25
26 app.post("/api/orders/:orderID/capture", async (req, res) => {
27 try {
28 const { orderID } = req.params;
29 const { jsonResponse, httpStatusCode } = await captureOrder(orderID);
30 res.status(httpStatusCode).json(jsonResponse);
31 } catch (error) {
32 console.error("Failed to create order:", error);
33 res.status(500).json({ error: "Failed to capture order." });
34 }
35 });
  • Line 1 creates a function which returns a HTTP status code response. Error status codes send an error message.
  • Line 14 makes a POST call to the api/orders/ endpoint and returns an HTTP status code response. Errors status codes send an error message.
  • Line 26 makes a 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.

3. Integrate front-end

Set up your front-end to integrate standard checkout payments.

Front-end process

  1. Your app displays the PayPal checkout buttons.
  2. Your app calls server endpoints to create the order and capture payment.

Front-end code

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.

  1. /client/checkout.html
  2. /client/app.js
1<!DOCTYPE html>
2<html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>PayPal JS SDK Standard Integration</title>
7 </head>
8 <body>
9 <div id="paypal-button-container"></div>
10 <p id="result-message"></p>
11 <!-- Replace the "test" client-id value with your client-id -->
12 <script src="https://www.paypal.com/sdk/js?client-id=test&currency=USD"></script>
13 <script src="app.js"></script>
14 </body>
15</html>

Understand the front-end code

This section explains the back-end code samples.

PayPal Buttons JavaScript

This code sample calls the PayPal JavaScript SDK

1<!DOCTYPE html>
2<html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>PayPal JS SDK Standard Integration</title>
7 </head>
8 <body>
9 <div id="paypal-button-container"></div>
10 <p id="result-message"></p>
11 <!-- Replace the "test" client-id value with your client-id -->
12 <script src="https://www.paypal.com/sdk/js?client-id=test&currency=USD"></script>
13 <script src="app.js"></script>
14 </body>
15</html>
  • Line 9 displays the PayPal buttons.
  • Line 10 displays the transaction results.
  • Line 12 calls the PayPal SDK.
  • line 13 runs the app.js script to start a checkout transaction.

PayPal Buttons JavaScript

This code sample calls the JavaScript SDK that defines the PayPal buttons.

1window.paypal.Buttons({{}}).render("#paypal-button-container");

Create order for PayPal Button

This code sample defines the createOrder() function.

1async createOrder() {
2 try {
3 const response = await fetch("/api/orders", {
4 method: "POST",
5 headers: {
6 "Content-Type": "application/json",
7 },
8 // use the "body" param to optionally pass additional order information
9 // like product ids and quantities
10 body: JSON.stringify({
11 cart: [
12 {
13 id: "YOUR_PRODUCT_ID",
14 quantity: "YOUR_PRODUCT_QUANTITY",
15 },
16 ],
17 }),
18 });
19
20 const orderData = await response.json();
21
22 if (orderData.id) {
23 return orderData.id;
24 } else {
25 const errorDetail = orderData?.details?.[0];
26 const errorMessage = errorDetail
27 ? `${errorDetail.issue} ${errorDetail.description} (${orderData.debug_id})`
28 : JSON.stringify(orderData);
29
30 throw new Error(errorMessage);
31 }
32 } catch (error) {
33 console.error(error);
34 resultMessage(`Could not initiate PayPal Checkout...<br><br>${error}`);
35 }
36},
  • Line 3 creates the order by calling the api/orders endpoint
  • Lines 10-15 pass the SKU and quantity for the product in the cart. See the createOrder section of the JavaScript SDK reference guide for more information about creating orders. See the Orders REST API documentation for additional customization options.

Capture payment when approved

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.

1async onApprove(data, actions) {
2 try {
3 const response = await fetch(/api/orders/${data.orderID}/capture, {
4 method: "POST",
5 headers: {
6 "Content-Type": "application/json",
7 }
8 ,});
9
10 const orderData = await response.json();
11 // Three cases to handle:
12 // (1) Recoverable INSTRUMENT_DECLINED -> call actions.restart()
13 // (2) Other non-recoverable errors -> Show a failure message
14 // (3) Successful transaction -> Show confirmation or thank you message
15
16 const errorDetail = orderData?.details?.[0];
17
18 if (errorDetail?.issue === "INSTRUMENT_DECLINED") {
19 // (1) Recoverable INSTRUMENT_DECLINED -> call actions.restart()
20 // recoverable state, per https://developer.paypal.com/docs/checkout/standard/customize/handle-funding-failures/
21 return actions.restart();
22 } else if (errorDetail) {
23 // (2) Other non-recoverable errors -> Show a failure message
24 throw new Error(`${errorDetail.description} (${orderData.debug_id})`);
25 } else if (!orderData.purchase_units) {
26 throw new Error(JSON.stringify(orderData));
27 } else {
28 // (3) Successful transaction -> Show confirmation or thank you message
29 // Or go to another URL: actions.redirect('thank_you.html');
30 const transaction =
31 orderData?.purchase_units?.[0]?.payments?.captures?.[0] ||
32 orderData?.purchase_units?.[0]?.payments?.authorizations?.[0];
33 resultMessage(
34 `Transaction ${transaction.status}: ${transaction.id}<br><br>See console for all available details`,
35 );
36 console.log(
37 "Capture result",
38 orderData,
39 JSON.stringify(orderData, null, 2),
40 );
41 }
42 } catch (error) {
43 console.error(error);
44 resultMessage(
45 `Sorry, your transaction could not be processed...<br><br>${error}`,
46 );
47 }
48},
49})
  • Line 3 creates the order by calling the api/orders endpoint
  • Lines 10-15 pass the SKU and quantity for the product in the cart. See the createOrder section of the JavaScript SDK reference guide for more information about creating orders. See the Orders REST API documentation for additional customization options.

Capture payment when approved

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.

1async onApprove(data, actions) {
2 try {
3 const response = await fetch(/api/orders/${data.orderID}/capture, {
4 method: "POST",
5 headers: {
6 "Content-Type": "application/json",}
7 ,}
8 );
9 const orderData = await response.json();
10 // Three cases to handle:
11 // (1) Recoverable INSTRUMENT_DECLINED -> call actions.restart()
12 // (2) Other non-recoverable errors -> Show a failure message
13 // (3) Successful transaction -> Show confirmation or thank you message
14
15 const errorDetail = orderData?.details?.[0];
16
17 if (errorDetail?.issue === "INSTRUMENT_DECLINED") {
18 // (1) Recoverable INSTRUMENT_DECLINED -> call actions.restart()
19 // recoverable state, per https://developer.paypal.com/docs/checkout/standard/customize/handle-funding-failures/
20 return actions.restart();
21 } else if (errorDetail) {
22 // (2) Other non-recoverable errors -> Show a failure message
23 throw new Error(`${errorDetail.description} (${orderData.debug_id})`);
24 } else if (!orderData.purchase_units) {
25 throw new Error(JSON.stringify(orderData));
26 } else {
27 // (3) Successful transaction -> Show confirmation or thank you message
28 // Or go to another URL: actions.redirect('thank_you.html');
29 const transaction =
30 orderData?.purchase_units?.[0]?.payments?.captures?.[0] ||
31 orderData?.purchase_units?.[0]?.payments?.authorizations?.[0];
32 resultMessage(
33 `Transaction ${transaction.status}: ${transaction.id}<br><br>See console for all available details`,
34 );
35 console.log(
36 "Capture result",
37 orderData,
38 JSON.stringify(orderData, null, 2),
39 );
40 }
41 } catch (error) {
42 console.error(error);
43 resultMessage(
44 `Sorry, your transaction could not be processed...<br><br>${error}`,
45 );
46 }
47 },
48})
  • Line 3 calls the api/orders/capture endpoint.
  • Line 8 sets up a listener for responses from the api/orders/capture call.
  • Line 13-43 parses the order details from the response.
  • Line 31 calls a response function to display the transaction results.

Display transaction results

This code sample defines an example resultMessage() function.

1// 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;

Render the PayPal buttons

This code sample renders the PayPal buttons.

1.render("#paypal-button-container");

Run your app

  • Run npm start to run your server again.
  • Open your browser and navigate to localhost:8888
  • When your server is running, proceed to the next section to test your integration.

4. Test integration

Before going live, test your integration in the sandbox environment.

Learn more about the following resources on card testing:

Note:Use the credit card generator to generate test credit cards for sandbox testing.

Test the following use cases before going live:

Test a purchase as a payer

  1. Select the PayPal button on your checkout page.
  2. Log in using one of your personal sandbox accounts. This ensures the payments will be sent to the correct account. Make sure that you use the sandbox business account that corresponds to the REST app you are using.
  3. Note the purchase amount in the PayPal checkout window.
  4. Approve the purchase with the Pay Now button. The PayPal window closes and redirects you to your page, indicating that the transaction was completed.

Confirm the money reached the business account

  1. Log in to the PayPal sandbox using the sandbox business account that received the payment. Remember that the SDK source now uses a sandbox client ID from one of your own REST apps, and not the default test ID.
  2. In Recent Activity, confirm that the sandbox business account received the money, subtracting any fees.
  3. Log out of the account.

5. Best practices

  • Show the PayPal buttons on all pages that start checkout.
  • Give PayPal equal prominence and function for your payers alongside all other acceptance marks, including cards, split tender, and buy online, pickup in-store.
  • It should take no more than 2 steps for your payer to pay and complete their order when they return to your site.
  • Leave room on your checkout page for the Debit or Credit Card button to expand. If you place the payment buttons too low on the page, payers won't be able to access the drop-down credit card form fields.
  • If the Debit or Credit Card drop-down form isn't available when your payers check out, the PayPal guest checkout shows up in a pop-up window.

6. Go live

Follow this checklist to to successfully take your application live.

  1. Log into the PayPal Developer Dashboard with your PayPal business account.
  2. Obtain your live credentials.
  3. Include the new credentials in your integration and Update your PayPal endpoint.

Next steps & customizations

Add more payment methods or customize your integration.

Optional
Pay Later

Payers buy now and pay in installments.

Optional
Pay with Venmo

Add the Venmo button to your checkout integration.

Optional
Alternative payment methods

Support local payment methods across the globe.

Optional
Apple Pay

Add Apple Pay as a payment method.

Optional
Capture payment

Captures payment for an order.

Optional
Refund a captured payment

Refund all or part of a captured payment.

Optional
Real-time account updater

Reduce declines by getting card updates from the issuer.

Optional
JavaScript SDK

Customize your integration with script config parameters.