Integrate PayPal buttons and Card Fields

SDKCURRENTADVANCED

Last updated: Feb 27th, 7:42am

PayPal Checkout plus customized card fields

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, card payments, and customized credit card fields that handle input from the following payment fields:

  • Card number
  • CVV
  • Expiration date
  • Optional: Cardholder name

You can find more ways to extend your integration in our customization doc.

After you integrate advanced Checkout, you can also offer options like Pay Later, Venmo, and other payment methods with some additional configuration.

Visit the GitHub repo to download a sample integration.

Which integration to use

There are 2 versions of card payment integrations for direct merchants using the JavaScript SDK:

  • Recommended: Version 2 uses the PayPal-hosted CardFields component to accept and save cards without handling card information. PayPal handles all security and compliance issues associated with processing cards. The CardFields component is the recommended integration method and receives ongoing enhancements and improvements.
  • Version 1 is a legacy integration that uses the HostedFields component. This integration is no longer under active development and won’t receive further updates.

Other elements of the JavaScript SDK integration for direct merchants remain the same.

Get up and running in GitHub Codespaces

Quick start - GitHub Codespaces

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

Know before you code

  • 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.
  • You need the following PayPal tools for this integration:
    • JavaScript SDK: Adds PayPal-supported payment methods.
    • Orders v2 REST API: Create, update, retrieve, authorize, and capture orders.
    • You can use Postman to explore and test PayPal APIs.

1

Before you begin your integration

Check 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, toggle Sandbox, and go to Apps & Credentials.
  2. In REST API apps, select the name of your app.
  3. Go to Features > Accept payments.
  4. Select the Advanced Credit and Debit Card Payments checkbox and select Save Changes.

Check 3D Secure requirements

Add 3D Secure to reduce the risk of fraud and improve the payment experience by authenticating a cardholder through their card issuer.

Visit our 3D Secure page to see if 3D Secure is required in your region and learn more about implementing 3D Secure in your app.

2

Initialize JavaScript SDK

Add the JavaScript SDK to your web page and include the following:

  • Your app's client ID.
  • div to render the PayPal buttons.
  • div to render each of the card fields.

In the included JavaScript file, there are reference routes on the server that you'll add in the next step.

3

Add PayPal buttons and card fields

Integration pattern - HTML

This integration includes a full-stack Node.js example. The /client/checkout.ejs/public/app.js, and /server/server.js file samples show how to render the PayPal buttons and the card fields component:

  • Use PayPal buttons to process PayPal payments.
  • Use card fields to process card payments.

You'll need to:

  • Save the checkout.ejs file in a folder named /client.
  • Save the app.js file in a folder named /public.
  • Save the server.js file in a folder named /server.
  1. /client/checkout.ejs
  2. /public/app.js
  3. /server/server.js
1<!DOCTYPE html>
2<html>
3<head>
4<meta charset="utf-8" />
5<meta name="viewport" content="width=device-width, initial-scale=1" />
6<!-- To be replaced with your own stylesheet -->
7<link
8rel="stylesheet"
9type="text/css"
10href="https://www.paypalobjects.com/webstatic/en_US/developer/docs/css/cardfields.css"
11/>
12<!-- Express fills in the clientId variable -->
13<script src="https://www.paypal.com/sdk/js?components=buttons,card-fields&client-id=<%= clientId %>"></script>
14</head>
15<body>
16<div id="paypal-button-container" class="paypal-button-container"></div>
17<div id="checkout-form">
18<!-- Containers for Card Fields hosted by PayPal -->
19<div id="card-name-field-container"></div>
20<div id="card-number-field-container"></div>
21<div id="card-expiry-field-container"></div>
22<div id="card-cvv-field-container"></div>
23<!-- To be replaced with your own Billing Address Fields -->
24<div>
25 <label for="card-billing-address-line-1">Billing Address</label>
26 <input
27 type="text"
28 id="card-billing-address-line-1"
29 name="card-billing-address-line-1"
30 autocomplete="off"
31 placeholder="Address line 1"
32 >
33</div>
34<div>
35 <input
36 type="text"
37 id="card-billing-address-line-2"
38 name="card-billing-address-line-2"
39 autocomplete="off"
40 placeholder="Address line 2"
41 >
42</div>
43<div>
44 <input
45 type="text"
46 id="card-billing-address-admin-area-line-1""
47 name="card-billing-address-admin-area-line-1""
48 autocomplete="off"
49 placeholder="Admin area line 1"
50 >
51</div>
52<div>
53 <input
54 type="text"
55 id="card-billing-address-admin-area-line-2"
56 name="card-billing-address-admin-area-line-2"
57 autocomplete="off"
58 placeholder="Admin area line 2"
59 >
60</div>
61<div>
62 <input
63 type="text"
64 id="card-billing-address-country-code"
65 name="card-billing-address-country-code"
66 autocomplete="off"
67 placeholder="Country code"
68 >
69</div>
70<div>
71 <input
72 type="text"
73 id="card-billing-address-postal-code"
74 name="card-billing-address-postal-code"
75 autocomplete="off"
76 placeholder="Postal/zip code"
77 >
78</div>
79<br><br>
80<button id="card-field-submit-button" type="button">
81 Pay now with Card Fields
82</button>
83</div>
84<script src="./public/app.js"></script>
85</body>
86</html>

Modify the code

This section explains how to customize the PayPal buttons and card fields for your integration.

PayPal buttons

  1. Copy a complete set of sample integration code from the GitHub repo.
  2. The CSS file in the head section is a sample for demo purposes. Instead, use styles that align with your brand using supported CSS properties.
  3. Optional: Customize JavaScript configurations, such as currency and intent.
  4. Optional: Change the layout, width, height, and outer styling of the PayPal buttons, such as borderbox-shadow, and background.

Card fields

  1. Copy and paste both examples of card field style objects into your existing /client/checkout.ejs and /public/app.js files.
  2. Include the required card form elements: card numbersecurity code, and expiration date. To learn about the available card form elements, see card fields.
  3. Add your own fields to accept billing address information.
  4. A complete set of sample integration code is available from the GitHub repo.
  5. Optional: Change the layout, width, height, and outer styling of the card fields, such as borderbox-shadow, and background. You can modify the elements you supply as containers.

Integration pattern - React

This integration includes a full-stack Node.js example. The /client/checkout.jsx, and /server/server.js file samples show how to render the PayPal buttons and the card field components in React.

  1. /client/checkout.jsx
  2. /server/server.js
1import React, { useState } from "react";
2
3import {
4 PayPalScriptProvider,
5 usePayPalCardFields,
6 PayPalCardFieldsProvider,
7 PayPalCardFieldsForm,
8 PayPalButtons,
9} from "@paypal/react-paypal-js";
10
11export default function App() {
12 const [isPaying, setIsPaying] = useState(false);
13 const [billingAddress, setBillingAddress] = useState({
14 addressLine1: "",
15 addressLine2: "",
16 adminArea1: "",
17 adminArea2: "",
18 countryCode: "",
19 postalCode: "",
20 });
21
22 async function createOrder(data) {
23 return fetch("myserver.com/api/orders", {
24 method: "POST",
25 // Use the "body" parameter to optionally pass additional order information
26 body: JSON.stringify({
27 cart: [
28 {
29 sku: "1blwyeo8",
30 quantity: 2,
31 },
32 ],
33 card: {
34 attributes: {
35 verification: {
36 method: "SCA_ALWAYS",
37 },
38 },
39 },
40 }),
41 })
42 .then((response) => response.json())
43 .then((order) => order.id)
44 .catch((err) => {
45 console.error(err);
46 });
47 }
48
49 function onApprove(data) {
50 return fetch(`myserver.com/api/orders/${data.orderID}/capture`, {
51 method: "POST",
52 })
53 .then((response) => response.json())
54 .then((orderData) => {
55 // Successful capture!
56 })
57 .catch((err) => {});
58 }
59
60 function onError(error) {
61 // Do something with the error from the SDK
62 }
63
64 function handleBillingAddressChange(field, value) {
65 setBillingAddress((prev) => ({
66 ...prev,
67 [field]: value,
68 }));
69 }
70
71 return (
72 <PayPalScriptProvider
73 options={{
74 clientId: "<%= clientId %>",
75 components: "card-fields,buttons",
76 }}
77 >
78 <PayPalButtons
79 createOrder={createOrder}
80 onApprove={onApprove}
81 onError={onError}
82 />
83 <PayPalCardFieldsProvider
84 createOrder={createOrder}
85 onApprove={onApprove}
86 onError={onError}
87 >
88 <PayPalCardFieldsForm />
89 <input
90 type="text"
91 id="card-billing-address-line-2"
92 name="card-billing-address-line-2"
93 placeholder="Address line 1"
94 onChange={(e) =>
95 handleBillingAddressChange("addressLine1", e.target.value)
96 }
97 />
98 <input
99 type="text"
100 id="card-billing-address-line-2"
101 name="card-billing-address-line-2"
102 placeholder="Address line 2"
103 onChange={(e) =>
104 handleBillingAddressChange("addressLine2", e.target.value)
105 }
106 />
107 <input
108 type="text"
109 id="card-billing-address-admin-area-line-1"
110 name="card-billing-address-admin-area-line-1"
111 placeholder="Admin area line 1"
112 onChange={(e) =>
113 handleBillingAddressChange("adminArea1", e.target.value)
114 }
115 />
116 <input
117 type="text"
118 id="card-billing-address-admin-area-line-2"
119 name="card-billing-address-admin-area-line-2"
120 placeholder="Admin area line 2"
121 onChange={(e) =>
122 handleBillingAddressChange("adminArea2", e.target.value)
123 }
124 />
125 <input
126 type="text"
127 id="card-billing-address-country-code"
128 name="card-billing-address-country-code"
129 placeholder="Country code"
130 onChange={(e) =>
131 handleBillingAddressChange("countryCode", e.target.value)
132 }
133 />
134 <input
135 type="text"
136 id="card-billing-address-postal-code"
137 name="card-billing-address-postal-code"
138 placeholder="Postal/zip code"
139 onChange={(e) =>
140 handleBillingAddressChange("postalCode", e.target.value)
141 }
142 />
143 {/* Custom client component to handle card fields submission */}
144 <SubmitPayment
145 isPaying={isPaying}
146 setIsPaying={setIsPaying}
147 billingAddress={billingAddress}
148 />
149 </PayPalCardFieldsProvider>
150 </PayPalScriptProvider>
151 );
152}
153
154const SubmitPayment = ({ isPaying, setIsPaying, billingAddress }) => {
155 const { cardFieldsForm, fields } = usePayPalCardFields();
156
157 const handleClick = async () => {
158 if (!cardFieldsForm) {
159 const childErrorMessage =
160 "Unable to find any child components in the <PayPalCardFieldsProvider />";
161
162 throw new Error(childErrorMessage);
163 }
164 const formState = await cardFieldsForm.getState();
165
166 if (!formState.isFormValid) {
167 return alert("The payment form is invalid");
168 }
169 setIsPaying(true);
170
171 cardFieldsForm.submit({ billingAddress }).catch((err) => {
172 setIsPaying(false);
173 });
174 };
175
176 return (
177 <button
178 className={isPaying ? "btn" : "btn btn-primary"}
179 style={{ float: "right" }}
180 onClick={handleClick}
181 >
182 {isPaying ? <div className="spinner tiny" /> : "Pay"}
183 </button>
184 );
185};

Modify the code

This section explains how to customize the PayPal buttons and card fields for your React integration.

PayPal buttons

  1. You should use styles that align with your brand using the supported CSS properties inside the style prop.
  2. Optional: Customize configurations, such as currency and intent inside the options prop in the PayPalScriptProvider.

Card fields

  1. Include the required card form elements: card numbersecurity code, and expiration date. To learn about the available card form elements, see Card fields. These are included out of the box in our PayPalCardFieldsForm component.
  2. Add more control over the rendered fields by using our Individual Field components.
  3. Add your own fields to accept billing address information.
  4. Optional: Change the outer styling of the card fields, such as borderbox-shadow, and background. You can do this by adding a class or style to each individual field container.
4

Call Orders API for PayPal buttons and card fields

Create API endpoints on your server that communicate with the Orders v2 API to create an order and capture payment for an order.

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

Server-side example (Node.js)

The paypal-api.js and /server/server.js code samples show how to integrate back-end code for advanced payments. The code adds routes to an Express server to create orders and capture payments using the Orders v2 API.

You'll need to:

  • Save the paypal-api.js file in your app's main folder.
  • Save the server.js file in a folder named /server.
  1. /server/server.js
  2. paypal-api.js
1import * as paypal from "./paypal-api.js";
2
3// create order
4app.post("/api/orders", async (req, res) => {
5 const order = await paypal.createOrder(req.body.paymentSource);
6 res.json(order);
7});
5

Capture order

Set up your server-side code to capture the order when a payer uses a credit or debit card. The paypal-api.js and /server/server.js files show how using server-side code prevents exposing your access token on the client side.

You'll need to:

  • Save the server.js file in a folder named /server.
  • Save the paypal-api.js file in your app's main folder.

Server-side example (Node.js)

  1. /server/server.js
  2. paypal-api.js
1// capture payment
2app.post("/api/orders/:orderID/capture", async (req, res) => {
3 const { orderID } = req.params;
4 const captureData = await paypal.capturePayment(orderID);
5 res.json(captureData);
6});
6

Handle payment responses

PayPal returns a status code and payment status for all authorize and capture calls.

Use the following API responses to handle errors and declined payments in your system:

Sample capture response

    1{
    2 "id": "8UV51279W51319843",
    3 "status": "COMPLETED",
    4 "payment_source": {
    5 "card": {
    6 "name": "Firstname Lastname",
    7 "last_digits": "1234",
    8 "expiry": "2026-11",
    9 "brand": "VISA",
    10 "available_networks": ["VISA"],
    11 "type": "UNKNOWN",
    12 "bin_details": {}
    13 }
    14 },
    15 "purchase_units": [{
    16 "reference_id": "default",
    17 "payments": {
    18 "captures": [{
    19 "id": "76R688678R689884Y",
    20 "status": "COMPLETED", // This is where the capture statuses appear.
    21 "amount": {
    22 "currency_code": "USD",
    23 "value": "100.00"
    24 },
    25 "final_capture": true,
    26 "seller_protection": {
    27 "status": "NOT_ELIGIBLE"
    28 },
    29 "seller_receivable_breakdown": {
    30 "gross_amount": {
    31 "currency_code": "USD",
    32 "value": "100.00"
    33 },
    34 "paypal_fee": {
    35 "currency_code": "USD",
    36 "value": "3.00"
    37 },
    38 "net_amount": {
    39 "currency_code": "USD",
    40 "value": "97.00"
    41 },
    42 "receivable_amount": {
    43 "currency_code": "CAD",
    44 "value": "124.99"
    45 },
    46 "exchange_rate": {
    47 "source_currency": "USD",
    48 "target_currency": "CAD",
    49 "value": "1.28856"
    50 }
    51 },
    52 "links": [{
    53 "href": "https://api-m.sandbox.paypal.com/v2/payments/captures/76R688678R689884Y",
    54 "rel": "self",
    55 "method": "GET"
    56 }, {
    57 "href": "https://api-m.sandbox.paypal.com/v2/payments/captures/76R688678R689884Y/refund",
    58 "rel": "refund",
    59 "method": "POST"
    60 }, {
    61 "href": "https://api-m.sandbox.paypal.com/v2/checkout/orders/8UV51279W51319843",
    62 "rel": "up",
    63 "method": "GET"
    64 }],
    65 "create_time": "2023-10-27T17:37:56Z",
    66 "update_time": "2023-10-27T17:37:56Z",
    67 "network_transaction_reference": {
    68 "id": "746206625503785",
    69 "network": "VISA"
    70 },
    71 "processor_response": {
    72 // Check this section for processor response specifics.
    73 "avs_code": "Y",
    74 "cvv_code": "S",
    75 "response_code": "0000"
    76 }
    77 }]
    78 }
    79 }],
    80 "links": [{
    81 "href": "https://api-m.sandbox.paypal.com/v2/checkout/orders/8UV51279W51319843",
    82 "rel": "self",
    83 "method": "GET"
    84 }]
    85}

    Check processor response

    In response to an authorize or capture request, payment processor codes are returned in the processor_response of the payment object.

    For a complete list of responses, refer to the Orders v2 processor response definitions.

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

      Test integration

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

      Learn more about the following resources on the Card Testing page:

      8

      Go live

      If you have fulfilled the requirements for accepting Advanced Credit and Debit Card Payments for your business account, review the Move your app to production page to learn how to test and go live.

      If this is your first time testing in a live environment, follow these steps:

      1. Log into the PayPal Developer Dashboard with your PayPal business account.
      2. Complete production onboarding so you can process card payments with your live PayPal business account.
      3. Request Advanced Credit and Debit Card Payments for your business account.