Integrate Advanced Checkout

SDKCurrentAdvancedLast updated: September 22nd 2023, @ 8:09:48 am


How it works

Advanced checkout lets you offer the PayPal payment button and custom credit and debit card fields. This guide covers integrating the PayPal button, credit, and debit card payments. You can find more ways to extend your integration in our customization doc.

Once you integrate advanced checkout, you can also offer options like Pay Later, Venmo, and alternative payment methods with some additional configuration.

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 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 need a combo of 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 the necessary third-party libraries.

You can use Postman to explore and test PayPal APIs.

1. Before you begin your integration

1Check your account setup for advanced card payments

This integration requires a sandbox business account with the Advanced Credit and Debit Card Payments capability. Your account should automatically have this capability.

To confirm that Advanced Credit and Debit Card Payments are enabled for you, check your sandbox business account as follows:

  1. Log into the PayPal Developer Dashboard, go to Apps & Credentials. In the REST API apps, select the name of your app.
  2. Go to Features > Accept payments.
  3. Select the Advanced Credit and Debit Card Payments checkbox and select Save Changes.

Note: If you created a sandbox business account through sandbox.paypal.com, and the advanced credit and debit card payments status for the account is disabled, complete the sandbox onboarding steps.

2 Set up npm

One of the requirements to run this sample application is to have npm installed. For more info, visit npm’s documentation.

1. Install third-party libraries

Install the following third-party libraries to set up your integration. Here is a sample command to install them all at the same time:

1npm install dotenv ejs express node-fetch

Select any of the links below to see more detailed installation instructions for each library:

Third-party librariesDescription
DotenvThis zero-dependency module separates your configuration and code by loading environment variables from a .env file into process.env.
ejsThese templates help you deliver HTML markup using plain JavaScript.
expressThis lean Node.js web application framework supports web and mobile applications.
node-fetchThis function helps you make API requests, similar to window.fetch.
2. Verify package.json

A package.json file has a list of the packages and version numbers needed for your app. You can share your package.json file with other developers so they can use the same settings as you.

The following snippet is an example of a package.json file for a PayPal integration. Compare this sample to the package.json in your project:

1{
2 "name": "@paypalcorp/advanced-integration",
3 "version": "1.0.0",
4 "description": "",
5 "main": "YOUR-SERVER-NAME.js",
6 "type": "module",
7 "scripts": {
8 "test": "echo "Error: no test specified" && exit 1",
9 "start": "node YOUR-SERVER-NAME.js"
10 },
11 "author": "",
12 "license": "Apache-2.0",
13 "dependencies": {
14 "dotenv": "^16.0.0",
15 "ejs": "^3.1.6",
16 "express": "^4.17.3",
17 "node-fetch": "^3.2.1"
18 }
19}
  • Pass the name of your server file using main by replacing the default YOUR-SERVER-NAME.js on lines 5 and 9.
  • Use scripts.test and scripts.start to customize how your app starts up.

If you’re having trouble with your app, try reinstalling your local library and package files using npm install.

If you’re getting the node error below, you need to 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.

3. 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 app secret for your app.

This code shows an example .env file. Replace the CLIENT_ID and APP_SECRET with values from your app:

Note: View your CLIENT_ID and APP_SECRET in your PayPal Developer Dashboard under "Apps & Credentials"

1CLIENT_ID="YOUR_CLIENT_ID_GOES_HERE"
2APP_SECRET="YOUR_SECRET_GOES_HERE"

2. Integrate back end

This section explains how to set up your back end to integrate advanced checkout payments.

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 buyer confirms the order.

Back-end code

The following example uses 2 files, paypal-api.js and server.js to show how to set up the back end to integrate with advanced payments:

  • paypal-api.js provides helper methods and functions for making the API calls and handling errors, such as creating and capturing a PayPal payment object, generating access and client tokens, and handling errors.
  • server.js imports the functions from paypal-api.js into your application and creates the endpoints that your app uses to call those functions.
  1. paypal-api.js
  2. server.js
1import fetch from 'node-fetch';
2
3// set some important variables
4const { CLIENT_ID, APP_SECRET } = process.env;
5const base = 'https://api-m.sandbox.paypal.com';
6
7// call the create order method
8export async function createOrder() {
9 const purchaseAmount = '100.00'; // TODO: pull prices from a database
10 const accessToken = await generateAccessToken();
11 const url = `${base}/v2/checkout/orders`;
12 const response = await fetch(url, {
13 method: 'post',
14 headers: {
15 'Content-Type': 'application/json',
16 Authorization: `Bearer ${accessToken}`,
17 },
18 body: JSON.stringify({
19 intent: 'CAPTURE',
20 purchase_units: [
21 {
22 amount: {
23 currency_code: 'USD',
24 value: purchaseAmount,
25 },
26 },
27 ],
28 }),
29 })
30 return handleResponse(response);
31}
32
33// capture payment for an order
34export async function capturePayment(orderId) {
35 const accessToken = await generateAccessToken();
36 const url = `${base}/v2/checkout/orders/${orderId}/capture`;
37 const response = await fetch(url, {
38 method: 'post',
39 headers: {
40 'Content-Type': 'application/json',
41 Authorization: `Bearer ${accessToken}`,
42 },
43 })
44 return handleResponse(response);
45}
46
47// generate access token
48export async function generateAccessToken() {
49 const auth = Buffer.from(CLIENT_ID + ':' + APP_SECRET).toString('base64');
50 const response = await fetch(`${base}/v1/oauth2/token`, {
51 method: 'post',
52 body: 'grant_type=client_credentials',
53 headers: {
54 Authorization: `Basic ${auth}`,
55 },
56 })
57 const jsonData = await handleResponse(response);
58 return jsonData.access_token;
59}
60
61// generate client token
62export async function generateClientToken() {
63 const accessToken = await generateAccessToken();
64 const response = await fetch(`${base}/v1/identity/generate-token`, {
65 method: 'post',
66 headers: {
67 Authorization: `Bearer ${accessToken}`,
68 'Accept-Language': 'en_US',
69 'Content-Type': 'application/json',
70 },
71 })
72 console.log('response', response.status);
73 const jsonData = await handleResponse(response);
74 return jsonData.client_token;
75}
76
77async function handleResponse(response) {
78 if (response.status === 200 || response.status === 201) {
79 return response.json();
80 }
81 const errorMessage = await response.text();
82 throw new Error(errorMessage);
83}

Understand the back-end code

This section explains critical parts of the back-end code samples.

This breakdown focuses on key lines from the paypal-api.js code sample.

Imports

This line of code imports the fetch function from the Node-Fetch library.

1import fetch from "node-fetch"

Grab variables and sandbox URL

This section of code grabs the environment variables and sets the base sandbox URL.

3// set some important variables
4const { CLIENT_ID, APP_SECRET } = process.env;
5const base = "https://api-m.sandbox.paypal.com";
  • Line 4 uses the DotEnv library to import and declare the client ID and app secret from your .env file.
  • Line 5 declares the base URL for PayPal’s sandbox API.

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.

7// call the create order method
8export async function createOrder() {
9 const purchaseAmount = "100.00";
10 // TODO: pull prices from a database
11 const accessToken = await generateAccessToken();
12 const url = `${base}/v2/checkout/orders`;
13 const response = await fetch(url, {
14 method: "post",
15 headers: {
16 "Content-Type": "application/json",
17 Authorization: `Bearer ${accessToken}`,
18 },
19 body: JSON.stringify({
20 intent: "CAPTURE",
21 purchase_units: [
22 {
23 amount: {
24 currency_code: "USD",
25 value: purchaseAmount,
26 },
27 },
28 ],
29 }),
30 });
31 return handleResponse(response);
32}
  • Line 9 declares a static price to use for testing. You can change this to a dynamic value before going live.
  • Line 11 establishes a listener to capture the accessToken from the generateAccessToken() function later in the API call.
  • Lines 12-30 create an order by sending a POST request to the Orders v2 API, using the accessToken.

Note: Visit the Create Order endpoint of the PayPal Orders v2 API to see 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.

This sample shows the processor response codes that are returned in the response of authorization and capture calls:

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.

Generate access token

You need an access token to authenticate all REST API requests. The following code sample creates an access token for you by making a POST call to the /v1/oauth2/ token endpoint.

49// generate access token
50export async function generateAccessToken() {
51const auth = Buffer.from(CLIENT_ID + ":" + APP_SECRET).toString("base64");
52const response = await fetch(`${base}/v1/oauth2/token`, {
53 method: "post",
54 body: "grant_type=client_credentials",
55 headers: {
56 Authorization: `Basic ${auth}`,
57 },
58});
59const jsonData = await handleResponse(response);
60return jsonData.access_token;
61}
  • Line 51 combines the CLIENT_ID and APP_SECRET as a key:value pair.
  • Lines 52-58 define a response that makes a POST call to the /v1/oauth2/token API endpoint to generate an access token.
  • Lines 59-60 establish a listener to capture the response jsonData from the request and return the access_token.
  1. Sample access token request
  2. Sample access token response
1curl -v -X POST "https://api-m.sandbox.paypal.com/v1/oauth2/token"
2 -u "CLIENT_ID:CLIENT_SECRET"
3 -H "Content-Type: application/x-www-form-urlencoded"
4 -d "grant_type=client_credentials"

Note: Read more about authenticating with PayPal.

Generate client token

A client token uniquely identifies your payer. You need a client token to use card fields. The following code sample creates a client token for you by making a POST call to the /v1/identity/generate-token endpoint.

63// generate client token
64export async function generateClientToken() {
65 const accessToken = await generateAccessToken();
66 const response = await fetch(`${base}/v1/identity/generate-token`, {
67 method: "post",
68 headers: {
69 Authorization: `Bearer ${accessToken}`,
70 "Accept-Language": "en_US",
71 "Content-Type": "application/json",
72 },
73 });
74 console.log('response', response.status)
75 const jsonData = await handleResponse(response);
76 return jsonData.client_token;
77}
  • Line 65 sets up a listener to capture the accessToken from the generateAccessToken() function.
  • Lines 66-73 make a POST call to the /v1/identity/generate-token API endpoint to generate a client token.
  • Lines 75-76 establish a listener to capture the response jsonData from the request and return the client_token.
  1. Sample client token request
  2. Sample client token response
1curl -X POST https://api-m.sandbox.paypal.com/v1/identity/generate-token \
2 -H 'Content-Type: application/json' \
3 -H 'Authorization: Bearer ACCESS-TOKEN' \
4 -H 'PayPal-Partner-Attribution-Id: BN-CODE \
5 -H 'Accept-Language: en_US'

Tip: Because each payer session is unique, set up your server to generate a new client token each time the card fields render on your page.

Handle responses

The handleResponse function sets up a listener for API responses.

79async function handleResponse(response) {
80 if (response.status === 200 || response.status === 201) {
81 return response.json();
82 }
83
84 const errorMessage = await response.text();
85 throw new Error(errorMessage);
86 }
  • Line 80 declares which HTTP status codes return a response. Other status codes return an error message.

Capture payment

Capture an order to successfully move money from the payer's payment method to the seller by making a POST call to /v2/checkout/orders/${orderId}/capture.

34// capture payment for an order
35 export async function capturePayment(orderId) {
36 const accessToken = await generateAccessToken();
37 const url = `${base}/v2/checkout/orders/${orderId}/capture`;
38 const response = await fetch(url, {
39 method: "post",
40 headers: {
41 "Content-Type": "application/json",
42 Authorization: `Bearer ${accessToken}`,
43 },
44 });
45
46 return handleResponse(response);
47 }
  • Line 36 establishes a listener to capture the accessToken from the generateAccessToken() function later in the API call.
  • Line 37 declares the URL of the Capture API endpoint using the Order ID generated from the Create Order endpoint.
  • Lines 38-43 define a response that makes a POST call to the /v2/checkout/orders/${orderId}/capture API endpoint to capture the order, using the accessToken.

Sample capture response

This code sample shows a response to a POST call to /v2/checkout/orders/{id}/capture. This response is the orderData that is grabbed by lines 71-72 in Render hosted card fields and 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 establishes a contract between a customer and merchant. Each purchase unit represents either a full or partial order that the customer intends to purchase from the merchant.
  • Line 16 passes the reference_id that identifies the 1 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 our 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.

See the Capture payment for order API endpoint for more details about the capture response.


This is a breakdown of the server.js code sample.

Declare imports

This section of code imports the configuration from your .env file, the express library, and the content of your paypal-api.js file.

1import "dotenv/config";
2import express from "express";
3import * as PayPal from "./paypal-api.js";

Set up port and server

This section of code sets up the port to run your server.

4const {PORT = 8888} = process.env;

This code starts the express Node.js web application framework. Line 6 declares that the rendering engine uses Embedded JavaScript templates (ejs).

5const app = express();
6app.set("view engine", "ejs");
7app.use(express.static("public"));

This code runs your app on localhost, using the port 8888 that you set up in line 4 of the API call.

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

Render checkout page

Set up your app’s checkout page to load this section of code when it’s rendered by your server.

10// render checkout page with client id & unique client token
11app.get("/", async (req, res) => {
12 const clientId = process.env.CLIENT_ID;
13 try {
14 const clientToken = await paypal.generateClientToken();
15 res.render("checkout", { clientId, clientToken });
16 } catch (err) {
17 res.status(500).send(err.message);
18 }
19});
  • Line 14 generates a client token by calling the generateClientToken() function from your paypal-api.js file.
  • Link 15 renders the checkout page and passes the client-token to the page and injects the value in the script tag.

Run createOrder()

This section of code creates an endpoint that calls the createOrder() function from your paypal-api.js file. Call the endpoint when the payment is submitted, for example, when the payer selects the PayPal button, or submits a card payment. See the /public/app.js section for more information.

21// create order
22app.post("/api/orders", async (req, res) => {
23 try {
24 const order = await paypal.createOrder();
25 res.json(order);
26 } catch (err) {
27 res.status(500).send(err.message);
28 }
29});
  • Lines 24-25 make a call to the paypal.createOrder() function to create an order.

Run capturePayment()

This section of code creates an endpoint that calls the capturePayment() function from your paypal-api.js file. Call the endpoint to capture the order and move money from the payer’s payment method to the seller.

See /public/app.js in the next section for more details on capturing payments from the front-end

31// capture payment
32app.post("/api/orders/:orderID/capture", async (req, res) => {
33 const { orderID } = req.params;
34 try {
35 const captureData = await paypal.capturePayment(orderID);
36 res.json(captureData);
37 } catch (err) {
38 res.status(500).send(err.message);
39 }
40});
  • Line 33 pulls the orderID from the request parameters.
  • Lines 35-36 call the capturePayment() function from paypal-api.js and pass the orderID.

Test server

Run npm start to confirm your set up is correct.

This command starts the server on localhost. It will load on port 8888 by default, but you can specify a port as shown in line 4 of the sample server.js file.

3. Integrate front end

This section explains how to set up your front end to integrate advanced checkout payments.

Front-end process

  1. Your app displays the PayPal checkout button and the card checkout form.
  2. Your app calls server endpoints to create the order and capture payment. The specific method depends on whether the buyer uses the PayPal button or checkout form.

Front-end code

This example uses 2 files, app.js and checkout.ejs, to show how to set up the back end to integrate with advanced payments:

  • /public/app.js 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 PayPal JavaScript SDK, and handle the payer’s interactions with the PayPal checkout button. You'll need to save the app.js file in a folder named /public.
  • /views/checkout.ejs is an Embedded JavaScript (EJS) file that builds the checkout page, adds the PayPal button, and loads the app.js file. You'll need to save the checkout.ejs file in a folder named /views.
  1. /public/app.js
  2. /views/checkout.ejs
1paypal
2 .Buttons({
3 // Sets up the transaction when a payment button is selected
4 createOrder: function () {
5 return fetch('/api/orders', {
6 method: 'post',
7 // use the "body" param to optionally pass additional order information
8 // like product skus and quantities
9 body: JSON.stringify({
10 cart: [
11 {
12 sku: 'YOUR_PRODUCT_STOCK_KEEPING_UNIT',
13 quantity: 'YOUR_PRODUCT_QUANTITY',
14 },
15 ],
16 }),
17 })
18 .then((response) => response.json())
19 .then((order) => order.id)
20 },
21 // Finalize the transaction after payer approval
22 onApprove: function (data) {
23 return fetch(`/api/orders/${data.orderID}/capture`, {
24 method: 'post',
25 })
26 .then((response) => response.json())
27 .then((orderData) => {
28 // Successful capture! For dev/demo purposes:
29 console.log(
30 'Capture result',
31 orderData,
32 JSON.stringify(orderData, null, 2),
33 )
34 const transaction = orderData.purchase_units[0].payments.captures[0]
35 alert(
36 `Transaction ${transaction.status}: ${transaction.id} See console for all available details`,
37 )
38 // When ready to go live, remove the alert and show a success message within this page. For example:
39 // var element = document.getElementById('paypal-button-container');
40 // element.innerHTML = '<h3>Thank you for your payment!</h3>';
41 // Or go to another URL: actions.redirect('thank_you.html');
42 })
43 },
44 })
45 .render('#paypal-button-container')
46// If this returns false or the card fields aren't visible, see Step #1.
47if (paypal.HostedFields.isEligible()) {
48 let orderId
49 // Renders card fields
50 paypal.HostedFields.render({
51 // Call your server to set up the transaction
52 createOrder: () => {
53 return fetch('/api/orders', {
54 method: 'post',
55 // use the "body" param to optionally pass additional order information
56 // like product skus and quantities
57 body: JSON.stringify({
58 cart: [
59 {
60 sku: 'YOUR_PRODUCT_STOCK_KEEPING_UNIT',
61 quantity: 'YOUR_PRODUCT_QUANTITY',
62 },
63 ],
64 }),
65 })
66 .then((res) => res.json())
67 .then((orderData) => {
68 orderId = orderData.id
69 // needed later to complete capture
70 return orderData.id
71 })
72 },
73 styles: {
74 '.valid': {
75 color: 'green',
76 },
77 '.invalid': {
78 color: 'red',
79 },
80 },
81 fields: {
82 number: {
83 selector: '#card-number',
84 placeholder: '4111 1111 1111 1111',
85 },
86 cvv: {
87 selector: '#cvv',
88 placeholder: '123',
89 },
90 expirationDate: {
91 selector: '#expiration-date',
92 placeholder: 'MM/YY',
93 },
94 },
95 }).then((cardFields) => {
96 document.querySelector('#card-form').addEventListener('submit', (event) => {
97 event.preventDefault()
98 cardFields
99 .submit({
100 // Cardholder's first and last name
101 cardholderName: document.getElementById('card-holder-name').value,
102 // Billing Address
103 billingAddress: {
104 // Street address, line 1
105 streetAddress: document.getElementById(
106 'card-billing-address-street',
107 ).value,
108 // Street address, line 2 (Ex: Unit, Apartment, etc.)
109 extendedAddress: document.getElementById(
110 'card-billing-address-unit',
111 ).value,
112 // State
113 region: document.getElementById('card-billing-address-state').value,
114 // City
115 locality: document.getElementById('card-billing-address-city')
116 .value,
117 // Postal Code
118 postalCode: document.getElementById('card-billing-address-zip')
119 .value,
120 // Country Code
121 countryCodeAlpha2: document.getElementById(
122 'card-billing-address-country',
123 ).value,
124 },
125 })
126 .then(() => {
127 fetch(`/api/orders/${orderId}/capture`, {
128 method: 'post',
129 })
130 .then((res) => res.json())
131 .then((orderData) => {
132 // Two cases to handle:
133 // (1) Other non-recoverable errors -> Show a failure message
134 // (2) Successful transaction -> Show confirmation or thank you
135 // This example reads a v2/checkout/orders capture response, propagated from the server
136 // You could use a different API or structure for your 'orderData'
137 const errorDetail =
138 Array.isArray(orderData.details) && orderData.details[0]
139 if (errorDetail) {
140 var msg = 'Sorry, your transaction could not be processed.'
141 if (errorDetail.description)
142 msg += '\n\n' + errorDetail.description
143 if (orderData.debug_id)
144 msg += ' (' + orderData.debug_id + ')'
145 return alert(msg) // Show a failure message
146 }
147 // Show a success message or redirect
148 alert('Transaction completed!')
149 })
150 })
151 .catch((err) => {
152 alert('Payment could not be captured! ' + JSON.stringify(err))
153 })
154 })
155 })
156} else {
157 // Hides card fields if the merchant isn't eligible
158 document.querySelector('#card-form').style = 'display: none'
159}

Understand the front-end code

This section explains critical parts of the front-end code samples.

This breakdown focuses on key lines from the /public/app.js code sample.

PayPal Button JavaScript

This section calls the JavaScript SDK that defines the PayPal buttons

1paypal
2 .Buttons({
3 {...}
4 })
5 .render("#paypal-button-container");

Create order for PayPal Button

This code sample defines the createOrder() function.

3// Sets up the transaction when a payment button is selected
4createOrder: function () {
5 return fetch("/api/orders", {
6 method: "post",
7 // use the "body" param to optionally pass additional order information
8 // like product skus and quantities
9 body: JSON.stringify({
10 cart: [
11 {
12 sku: "YOUR_PRODUCT_STOCK_KEEPING_UNIT",
13 quantity: "YOUR_PRODUCT_QUANTITY",
14 },
15 ],
16 }),
17 })
18 .then((response) => response.json())
19 .then((order) => order.id);
20},
  • Line 5 creates the order by calling the /api/orders endpoint on your server.
  • Lines 10-15 pass the SKU and quantity for the product in the cart. For more information about creating orders, see the createOrder section of the JavaScript SDK reference guide. For additional customization options, see our Orders REST API documentation

Capture payment when approved

This code sample defines a POST call to /api/orders/{orderID}/capture. The orderID gets passed from the SDK when you run the onCreate() call.

22onApprove: function (data) {
23 return fetch(`/api/orders/${data.orderID}/capture`, {
24 method: "post",
25 })
26 .then((response) => response.json())
27 .then((orderData) => {
28 // Successful capture! For dev/demo purposes:
29 console.log(
30 "Capture result",
31 orderData,
32 JSON.stringify(orderData, null, 2)
33 );
34 const transaction = orderData.purchase_units[0].payments.captures[0];
35 alert(`Transaction ${transaction.status}: ${transaction.id}
36 See console for all available details
37 `);
38 // When ready to go live, remove the alert and show a success message within this page. For example:
39 // var element = document.getElementById('paypal-button-container');
40 // element.innerHTML = '<h3>Thank you for your payment!</h3>';
41 // Or go to another URL: actions.redirect('thank_you.html');
42 });
43},
  • Line 23 calls your server’s capture endpoint to capture the order.
  • Lines 28-42 declare what happens when the payment is successfully captured. When you’re ready to go live, you can remove the alert and either show a success message or redirect the payer to another URL.

Check for HostedFields eligibility

Hosted card fields use the JavaScript SDK to help non-PCI-compliant sellers save a payer’s card data and create a token for future payments. During an eligible payment, the hosted fields show up in the payment experience and prompt the payer for consent to save their card data. For more information about hosted fields, see Hosted fields.

Note: You need to complete the account setup in sandbox to be eligible for testing hosted fields.

This code sample checks to see if a payment is eligible for hosted card fields. If not, the payment experience won’t show the hosted card fields.

48// If this returns false or the card fields aren't visible, see Step #1.
49if (paypal.HostedFields.isEligible()) {
50 let orderId;
51 {...}
52} else {
53 // Hides card fields if the merchant isn't eligible
54 document.querySelector("#card-form").style = "display: none";
55}

You can modify this code to show a custom message for eligible or ineligible payments.

Render hosted card fields and create order

This code sample renders the hosted card fields for an eligible payment.

52// Renders card fields
53paypal.HostedFields.render({
54 // Call your server to set up the transaction
55 createOrder: () => {
56 return fetch("/api/orders", {
57 method: "post",
58 // use the "body" param to optionally pass additional order information
59 // like product skus and quantities
60 body: JSON.stringify({
61 cart: [
62 {
63 sku: "YOUR_PRODUCT_STOCK_KEEPING_UNIT",
64 quantity: "YOUR_PRODUCT_QUANTITY",
65 },
66 ],
67 }),
68 })
69 .then((res) => res.json())
70 .then((orderData) => {
71 orderId = orderData.id;
72 // needed later to complete capture
73 return orderData.id;
74 });
75 },
76 styles: {
77 ".valid": {
78 color: "green",
79 },
80 ".invalid": {
81 color: "red",
82 },
83 },
84 fields: {
85 number: {
86 selector: "#card-number",
87 placeholder: "4111 1111 1111 1111",
88 },
89 cvv: {
90 selector: "#cvv",
91 placeholder: "123",
92 },
93 expirationDate: {
94 selector: "#expiration-date",
95 placeholder: "MM/YY",
96 },
97 },
98})
  • Line 56 creates the order by calling the /api/orders endpoint on your server.
  • Lines 63-64 pass the SKU and quantity for the product in the cart. For additional customization options, see our Orders REST API documentation.
  • Lines 71-73 get the order identifier id from the orderData object returned.
  • Lines 76-83 define styles for the hosted card fields. You can change these styles as needed for your implementation.
  • Lines 84-96 define the selector values and placeholders for the input fields. You can edit this section as needed for your implementation, such as adding more fields. For more information about optional configuration, see Options in the JavaScript SDK reference.

Capture eligible payment

This code sample captures the payment when it is eligible for hosted card fields. Add this code to the PayPal button in /public/app.js, right after rendering the card fields.

97.then((cardFields) => {
98 document.querySelector('#card-form').addEventListener('submit', (event) => {
99 event.preventDefault()
100 cardFields
101 .submit({
102 // Cardholder's first and last name
103 cardholderName: document.getElementById('card-holder-name').value,
104 // Billing Address
105 billingAddress: {
106 // Street address, line 1
107 streetAddress: document.getElementById('card-billing-address-street')
108 .value,
109 // Street address, line 2 (Ex: Unit, Apartment, etc.)
110 extendedAddress: document.getElementById('card-billing-address-unit')
111 .value,
112 // State
113 region: document.getElementById('card-billing-address-state').value,
114 // City
115 locality: document.getElementById('card-billing-address-city').value,
116 // Postal Code
117 postalCode: document.getElementById('card-billing-address-zip').value,
118 // Country Code
119 countryCodeAlpha2: document.getElementById(
120 'card-billing-address-country',
121 ).value,
122 },
123 })
124 .then(() => {
125 fetch(`/api/orders/$&#123;orderId}/capture`, {
126 method: 'post',
127 })
128 .then((res) => res.json())
129 .then((orderData) => {
130 // Two cases to handle:
131 // (1) Other non-recoverable errors -> Show a failure message
132 // (2) Successful transaction -> Show confirmation or thank you
133 // This example reads a v2/checkout/orders capture response, propagated from the server
134 // You could use a different API or structure for your 'orderData'
135 const errorDetail =
136 Array.isArray(orderData.details) && orderData.details[0]
137 if (errorDetail) {
138 var msg = 'Sorry, your transaction could not be processed.'
139 if (errorDetail.description)
140 msg += '\n\n' + errorDetail.description
141 if (orderData.debug_id) msg += ' (' + orderData.debug_id + ')'
142 return alert(msg)
143 // Show a failure message
144 }
145 // Show a success message or redirect
146 alert('Transaction completed!')
147 })
148 })
149 .catch((err) => {
150 alert('Payment could not be captured! ' + JSON.stringify(err))
151 })
152 })
153})
  • Line 98 sets up an event listener for when the payer submits an eligible card payment.
  • Lines 100-127 pass the hosted card field values, such as the cardholder’s name and address, to the POST call in lines 128-131. Anything you pass into the submit is sent to the iframe that communicates with the Orders API. The iframe retrieves the relevant data and sends it along to the POST call. See the Orders v2 API for details about billing address fields and other parameters. For example, use the 2-character country code to test the billing address.
  • Lines 124-127 define a POST call to /v2/checkout/orders/{id}/capture that captures the order using the orderId.
  • Lines 129-149 declare how to handle the payment capture response. When you’re ready to go live, you can remove the alert and either show a success message or redirect the payer to another URL.

This breakdown focuses on key lines from the /views/checkout.ejs code sample.

Import style sheet

This section calls the JavaScript SDK that defines the PayPal buttons

5<link
6 rel="stylesheet"
7 type="text/css"
8 href="https://www.paypalobjects.com/webstatic/en_US/developer/docs/css/cardfields.css"
9/>

Add PayPal button

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

Line 16: Include the PayPal Checkout button before you request the credit card fields or on the same page that you request this information. For more information, see Section 8, Required Use of PayPal Checkout, PayPal Credit, in the PayPal Online Card Payments Services Agreement.

Create your card form

This section of code shows how to create the form fields and Submit button you need to take card payments.

17<div class="card_container">
18 <form id="card-form">
19 <label for="card-number">Card Number</label>
20 <div id="card-number" class="card_field"></div>
21 <div style="display: flex; flex-direction: row;">
22 <div>
23 <label for="expiration-date">Expiration Date</label>
24 <div id="expiration-date" class="card_field"></div>
25 </div>
26 <div style="margin-left: 10px;">
27 <label for="cvv">CVV</label>
28 <div id="cvv" class="card_field"></div>
29 </div>
30 </div>
31 <label for="card-holder-name">Name on Card</label>
32 <input
33 type="text"
34 id="card-holder-name"
35 name="card-holder-name"
36 autocomplete="off"
37 placeholder="card holder name"
38 />
39 <div>
40 <label for="card-billing-address-street">Billing Address</label>
41 <input
42 type="text"
43 id="card-billing-address-street"
44 name="card-billing-address-street"
45 autocomplete="off"
46 placeholder="street address"
47 />
48 </div>
49 <div>
50 <label for="card-billing-address-unit">&nbsp;</label>
51 <input
52 type="text"
53 id="card-billing-address-unit"
54 name="card-billing-address-unit"
55 autocomplete="off"
56 placeholder="unit"
57 />
58 </div>
59 <div>
60 <input
61 type="text"
62 id="card-billing-address-city"
63 name="card-billing-address-city"
64 autocomplete="off"
65 placeholder="city"
66 />
67 </div>
68 <div>
69 <input
70 type="text"
71 id="card-billing-address-state"
72 name="card-billing-address-state"
73 autocomplete="off"
74 placeholder="state"
75 />
76 </div>
77 <div>
78 <input
79 type="text"
80 id="card-billing-address-zip"
81 name="card-billing-address-zip"
82 autocomplete="off"
83 placeholder="zip / postal code"
84 />
85 </div>
86 <div>
87 <input
88 type="text"
89 id="card-billing-address-country"
90 name="card-billing-address-country"
91 autocomplete="off"
92 placeholder="country code"
93 />
94 </div>
95 <br /><br />
96 <button value="submit" id="submit" class="btn">Pay</button>
97 </form>
98</div>
  • Lines 18-97 declare a card-form object with fields for card-number, expiration-date, cvv, card-holder-name, and billing address information. See the Orders v2 API for details about billing address fields and other parameters. For example, use the 2-character country code to test the billing address.
  • The fields in this example match up with fields in the Capture eligible payment code sample.

Import app.js

This code sample renders the hosted card fields for an eligible payment. Add this section of code to the PayPal button in public/app.js.

99<script src="app.js"></script>

Line 99 imports the app.js file that you created.


Run front end integration

  • 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 the Card Testing page:

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

5. Go Live

Test and go live. Before you go live:
  1. Log into the PayPal Developer Dashboard with your PayPal business account.
  2. Complete production oboarding so you can process cards with your live PayPal business account.
  3. Request Advanced Credit and Debit Card Payments for your business account to accept card payments on your website.

Important: The code for the integration checks eligibility requirements, so the payment card fields won't display if the production request isn't successful.

Add 3D Secure to reduce the chance of fraud and improve the payment experience by authenticating a card holder through their card issuer. Visit our 3D Secure page to see if 3DS is required in your region.

Next steps & customizations

Add security to your checkout experience, or create customizations for your audience.

Add more payment methods

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 button.


Security and customizations

Required
Implement 3D Secure

Authenticate card holders through card issuers.

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.