Integrate card payments

SDKCurrentADVANCED

Last updated: Oct 10th, 5:09pm

How it works

Integrate advanced Checkout to present credit and debit card fields customized with your site's branding style. Advanced Checkout helps customers pay with PayPal, debit and credit cards, Pay Later options, Venmo, and other payment methods.

You can find more ways to extend your integration in Customize your buyer's experience.

How it works

Sequence diagram

The following sequence diagram shows an advanced Checkout integration with card fields and 3D Secure:

  1. The script tag fetches the JavaScript SDK when your checkout page renders.
  2. Your page uses the JavaScript SDK to render card fields.
  3. The buyer enters their card details into the card fields.
  4. Optionally, you can collect the billing address with your own input fields and include the values in the submit method.
  5. The buyer selects the Submit button which calls the CardFields.submit method.
  6. The createOrder callback requests your server to create a PayPal order.
  7. Your server requests the PayPal Orders API with the appropriate 3DS verification.
  8. The createOrder callback returns an orderId to your page.
  9. Depending on the 3DS verification method passed by your createOrder call, the buyer sees a 3DS User Authentication from the card-issuing bank.
  10. The onApprove callback returns a liabilityShift response that your page can use to determine how to process the order.
  11. If there are no issues during the createOrder process, the onApprove callback sends a capture request to your server.
  12. Your server requests the PayPal Orders API to capture the order.
  13. Your page can use the capture response to verify the payment was completed, catch issues with the payment method, and approve or decline the transaction.

User experience

The PayPal card fields component can be rendered on your page and customized to match your user experience. You can load the PayPal buttons component alongside card fields to help your buyer select their preferred payment method. The PayPal buttons component shows up on your page based on the configurations set in the JavaScript SDK.

When your buyer chooses to pay with a credit or debit card:

  1. Card fields will render on your page.
  2. The buyer fills each of the fields with their card information, such as cardholder name, card number, expiration date, postal code, and CVV.
  3. Optionally, the buyer can enter their billing address into your own input elements to pass along with the order.
  4. After the buyer has filled in the card fields, the buyer selects the Pay or Submit button to process the card transaction.
  5. The order goes to PayPal servers, where we process the payment.

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.

Required
You need a developer account to get sandbox credentials

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

Prerequisites

Check your account setup for advanced card payments

This integration requires a sandbox business account with Advanced Credit and Debit Card Payments, which should be enabled automatically in your sandbox account.

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

  1. Log into the PayPal Developer Dashboard, go to Apps & Credentials > Sandbox > REST API apps, and 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.

Set up npm

You'll need to have npm installed to run this sample application 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 libraries Description
    Dotenv This zero-dependency module separates your configuration and code by loading environment variables from an .env file into process.env.
    ejs These templates help you deliver HTML markup using plain JavaScript.
    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.
    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":"paypal-js-advanced-integration-ib",
      3"description":"Sample Node.js web app to integrate PayPal Advanced Checkout for online payments",
      4"version":"1.0.1",
      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}
      • Pass the name of your server file using main by replacing the default YOUR-SERVER-NAME.js on lines 5 and 8.
      • 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 following node error, include "type": "module" in your package.json file: 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:

        1APP_SECRET="YOUR_SECRET_GOES_HERE"
        2

        Integrate front end

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

        Front-end process

        1. Your app shows PayPal payment buttons and card fields.
        2. Your app calls server endpoints to create the order and capture payment. The request details depend 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. Save the app.js file in a folder named /public.
        • /server/views/checkout.ejs is an Embedded JavaScript (EJS) file that builds the checkout page, adds the PayPal button, and loads the app.js file. Save the checkout.ejs file in a folder named /server/views.
        1Initilize JavaScript SDK

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

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

        The JavaScript file in the sample code includes reference routes on the server that you’ll add later.

        • Include the <script> tag on any page that shows the PayPal buttons. This script will fetch all the necessary JavaScript to access the buttons on the window object.
        • Pass a client-id and specify which components you want to use. The SDK offers buttons, marks, card fields, and other components. This sample focuses on the buttons component.
        • In addition to passing the client-id and specifying which components you want to use, you can also pass the currency you want to use for pricing. In this example, USD is used.
          1<!--checkout.ejs file-->
          2<!DOCTYPEhtml>
          3<htmllang="en">
          4<head>
          5<metacharset="UTF-8"/>
          6<metaname="viewport"content="width=device-width, initial-scale=1.0"/>
          7<linkrel="stylesheet"type="text/css"href="https://www.paypalobjects.com/webstatic/en_US/developer/docs/css/cardfields.css"/>
          8<title>PayPal JS SDK Advanced Integration - Checkout Flow</title>
          9<scriptsrc="https://www.paypal.com/sdk/js?components=buttons,card-fields&client-id=<CLIENT_ID>&merchant-id=<MERCHANT_ID>¤cy=USD&intent=capture"data-partner-attribution-id="<BN_CODE>"data-client-token="<CLIENT_TOKEN>"></script>
          10</head>
          11<body>
          12<divid="paypal-button-container"class="paypal-button-container"></div>
          13<!-- Containers for card fields hosted by PayPal -->
          14<divid="card-form"class="card_container">
          15<divid="card-name-field-container"></div>
          16<divid="card-number-field-container"></div>
          17<divid="card-expiry-field-container"></div>
          18<divid="card-cvv-field-container"></div>
          19<div>
          20<labelfor="card-billing-address-line-1">Billing address line 1</label>
          21<inputtype="text"id="card-billing-address-line-1"name="card-billing-address-line-1"autocomplete="off"placeholder="Billing address line 1"/>
          22</div>
          23<div>
          24<labelfor="card-billing-address-line-2">Billing address line 2</label>
          25<inputtype="text"id="card-billing-address-line-2"name="card-billing-address-line-2"autocomplete="off"placeholder="Billing address line 2"/>
          26</div>
          27<div>
          28<labelfor="card-billing-address-admin-area-line-1">Admin area line 1</label>
          29<inputtype="text"id="card-billing-address-admin-area-line-1"name="card-billing-address-admin-area-line-1"autocomplete="off"placeholder="Admin area line 1"/>
          30</div>
          31<div>
          32<labelfor="card-billing-address-admin-area-line-2">Admin area line 2</label>
          33<inputtype="text"id="card-billing-address-admin-area-line-2"name="card-billing-address-admin-area-line-2"autocomplete="off"placeholder="Admin area line 2"/>
          34</div>
          35<div>
          36<labelfor="card-billing-address-country-code">Country code</label>
          37<inputtype="text"id="card-billing-address-country-code"name="card-billing-address-country-code"autocomplete="off"placeholder="Country code"/>
          38</div>
          39<div>
          40<labelfor="card-billing-address-postal-code">Postal/zip code</label>
          41<inputtype="text"id="card-billing-address-postal-code"name="card-billing-address-postal-code"autocomplete="off"placeholder="Postal/zip code"/>
          42</div>
          43<br/><br/>
          44<buttonid="card-field-submit-button"type="button">
          45 Pay now with Card
          46</button>
          47</div>
          48<pid="result-message"></p>
          49<scriptsrc="app.js"></script>
          50</body>
          51</html>
          2Render card fields and PayPal buttons

          After setting up the SDK for your website, you need to render the PayPal buttons and the card field components.

          Card fields

          The PayPal namespace has a CardFields component to accept and save cards without handling card information. PayPal handles all security and compliance issues associated with processing cards. The CardFields function checks to see if a payment is eligible for card fields. If not, the card fields won't appear during the payment flow.

          The /public/app.js file needs a function that submits a createOrder() request:

          • Declare a createOrder callback that launches when the customer selects the payment button. The callback starts the order and returns an order ID. After the customer checks out using the PayPal pop-up, this order ID helps you to confirm when the payment is completed.
          • Set up your app to call cardField.isEligible() for each card field to determine if a payment is eligible for card fields.
          • Render each card field by declaring it as an object in cardField and then applying a .render() function.
          • Create the order by calling the createOrder function.
          • Define styles for the card fields. You can change these styles for your implementation.
          • Define the selector and placeholder values for the input fields. You can edit this section for your implementation, such as adding more fields. For more information about optional configurations, see Options in the JavaScript SDK reference.
          • Set up your app to capture the payment when it is eligible for card fields by adding code to the PayPal button in /server/view/checkout.ejs, right after rendering the card fields.
          • You need to declare an event listener /public/app.js for when the payer submits an eligible card payment.
          • Pass the card field values, such as the cardholder's name and address, to the POST call. Anything you pass into the submit function is sent to the iframe that communicates with the Orders API. The iframe retrieves the data and sends it along to the POST call. See the Orders v2 API reference for details about billing address fields and other parameters. For example, use the 2-character country code to test the billing address.

          Completing the payment launches an onApprove callback, which sends a POST call to /v2/checkout/orders/{id}/capture to capture the order using the orderId in the data object in /server/server.js. Use the onApprove response to update business logic, show a celebration page, or handle error responses.

          Set up your app to handle the payment capture response, such as when the payment is captured, when the payer's instrument is declined, or when there is an error:

          • Include a function that shows a message to the user by passing data to the result-message HTML element container of checkout.ejs.
          • Your app needs to call the JavaScript SDK that defines the PayPal card fields and links them to the createOrder() function.
          • If your website handles shipping physical items, see details about our shipping callbacks.

          PayPal buttons

          The paypal namespace has a Buttons function that initiates the callbacks needed to set up a payment. The app.js file needs a function that submits a createOrder() request.

          • Declare a createOrder callback that launches when the customer selects the payment button. The callback starts the order and returns an order ID. After the customer checks out using the PayPal pop-up, this order ID helps you to confirm when the payment is completed.
          • Completing the payment launches an onApprove callback. Use the onApprove response to update business logic, show a celebration page, or handle error responses.
          • Set up your app to handle the payment capture response, such as when the payment is captured, when the payer's instrument is declined, or when there is an error.
          • Include a function that shows a message to the user by passing data to the result-message HTML element container of checkout.ejs.
          • Your app must call the JavaScript SDK that defines the PayPal buttons and links them to the createOrder() function.
            1// app.js file
            2// Render the button component
            3paypal
            4.Buttons({
            5// Sets up the transaction when a payment button is selected
            6createOrder: createOrderCallback,
            7onApprove: onApproveCallback,
            8onError:function(error){
            9// Do something with the error from the SDK
            10},
            11style:{
            12shape:"rect",
            13layout:"vertical",
            14color:"gold",
            15label:"paypal",
            16},
            17message:{
            18amount:100,
            19},
            20})
            21.render("#paypal-button-container");
            22// Render each field after checking for eligibility
            23const cardField =window.paypal.CardFields({
            24createOrder: createOrderCallback,
            25onApprove: onApproveCallback,
            26style:{
            27input:{
            28"font-size":"16px",
            29"font-family":"courier, monospace",
            30"font-weight":"lighter",
            31color:"#ccc",
            32},
            33".invalid":{
            34color:"purple"
            35},
            36// Optional to handle buyer checkout errors
            37onError:(err)=>{
            38// redirect to your error catch-all page
            39window.location.assign("/your-error-page-here");
            40},
            41},
            42});
            43if(cardField.isEligible()){
            44const nameField = cardField.NameField({
            45style:{
            46input:{
            47color:"blue"
            48},
            49".invalid":{
            50color:"purple"
            51}
            52},
            53});
            54 nameField.render("#card-name-field-container");
            55const numberField = cardField.NumberField({
            56style:{
            57input:{
            58color:"blue"
            59}
            60},
            61});
            62 numberField.render("#card-number-field-container");
            63const cvvField = cardField.CVVField({
            64style:{
            65input:{
            66color:"blue"
            67}
            68},
            69});
            70 cvvField.render("#card-cvv-field-container");
            71const expiryField = cardField.ExpiryField({
            72style:{
            73input:{
            74color:"blue"
            75}
            76},
            77});
            78 expiryField.render("#card-expiry-field-container");
            79// Add click listener to submit button and call the submit function on the CardField component
            80document
            81.getElementById("card-field-submit-button")
            82.addEventListener("click",()=>{
            83 cardField
            84.submit({
            85// From your billing address fields
            86billingAddress:{
            87addressLine1:document.getElementById(
            88"card-billing-address-line-1"
            89).value,
            90addressLine2:document.getElementById(
            91"card-billing-address-line-2"
            92).value,
            93adminArea1:document.getElementById(
            94"card-billing-address-admin-area-line-1"
            95).value,
            96adminArea2:document.getElementById(
            97"card-billing-address-admin-area-line-2"
            98).value,
            99countryCode:document.getElementById(
            100"card-billing-address-country-code"
            101).value,
            102postalCode:document.getElementById(
            103"card-billing-address-postal-code"
            104).value,
            105},
            106})
            107.then(()=>{
            108// submit successful
            109});
            110});
            111}
            112asyncfunctioncreateOrderCallback(){
            113resultMessage("");
            114try{
            115const response =awaitfetch("/api/orders",{
            116method:"POST",
            117headers:{
            118"Content-Type":"application/json",
            119},
            120// Use the "body" param to optionally pass additional order information
            121// like product ids and quantities
            122body:JSON.stringify({
            123cart:[{
            124id:"YOUR_PRODUCT_ID",
            125quantity:"YOUR_PRODUCT_QUANTITY",
            126},],
            127}),
            128});
            129const orderData =await response.json();
            130if(orderData.id){
            131return orderData.id;
            132}else{
            133const errorDetail = orderData?.details?.[0];
            134const errorMessage = errorDetail ?
            135`${errorDetail.issue}${errorDetail.description} (${orderData.debug_id})`:
            136JSON.stringify(orderData);
            137thrownewError(errorMessage);
            138}
            139}catch(error){
            140console.error(error);
            141resultMessage(`Could not initiate PayPal Checkout...<br><br>${error}`);
            142}
            143}
            144asyncfunctiononApproveCallback(data, actions){
            145try{
            146const response =awaitfetch(`/api/orders/${data.orderID}/capture`,{
            147method:"POST",
            148headers:{
            149"Content-Type":"application/json",
            150},
            151});
            152const orderData =await response.json();
            153// Three cases to handle:
            154// (1) Recoverable INSTRUMENT_DECLINED -> call actions.restart()
            155// (2) Other non-recoverable errors -> Show a failure message
            156// (3) Successful transaction -> Show confirmation or thank you message
            157const transaction =
            158 orderData?.purchase_units?.[0]?.payments?.captures?.[0]||
            159 orderData?.purchase_units?.[0]?.payments?.authorizations?.[0];
            160const errorDetail = orderData?.details?.[0];
            161// Optional to handle funding failures
            162// This actions.restart() behavior only applies to the buttons component
            163if(
            164 errorDetail?.issue ==="INSTRUMENT_DECLINED"&&
            165!data.card&&
            166 actions
            167){
            168// (1) Recoverable INSTRUMENT_DECLINED -> call actions.restart()
            169// recoverable state, per https://developer.paypal.com/docs/multiparty/checkout/standard/customize/handle-funding-failures/
            170return actions.restart();
            171}elseif(
            172// End optional failure handling
            173if(errorDetail ||!transaction || transaction.status==="DECLINED"){
            174// (2) Other non-recoverable errors -> Show a failure message
            175let errorMessage;
            176if(transaction){
            177 errorMessage =`Transaction ${transaction.status}: ${transaction.id}`;
            178}elseif(errorDetail){
            179 errorMessage =`${errorDetail.description} (${orderData.debug_id})`;
            180}else{
            181 errorMessage =JSON.stringify(orderData);
            182}
            183thrownewError(errorMessage);
            184}else{
            185// (3) Successful transaction -> Show confirmation or thank you message
            186// Or go to another URL: actions.redirect('thank_you.html');
            187resultMessage(
            188`Transaction ${transaction.status}: ${transaction.id}<br><br>See console for all available details`
            189);
            190console.log(
            191"Capture result",
            192 orderData,
            193JSON.stringify(orderData,null,2)
            194);
            195}
            196}catch(error){
            197console.error(error);
            198resultMessage(
            199`Sorry, your transaction could not be processed...<br><br>${error}`
            200);
            201}
            202}
            203// Example function to show a result to the user. Your site's UI library can be used instead.
            204functionresultMessage(message){
            205const container =document.querySelector("#result-message");
            206 container.innerHTML= message;
            207}
            3Customize card fields and buttons

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

            Configure card field layout

            Copy and paste both examples of card field style objects into your existing /client/checkout.ejs and /public/app.js files.

            • Include the required card form elements: NumberField, CVVField, and ExpiryField. To learn about the available card form elements, see card fields.
            • Add your own fields to accept billing address information.
            • A complete set of sample integration code is available from the GitHub repo.
            • Optional: Change the layout, width, height, and outer styling of the card fields, such as border, box-shadow, and background. You can modify the elements you supply as containers.

            Configure buttons layout

            Depending on where you want these buttons to appear on your website, you can lay out the buttons in a horizontal or vertical stack. You can also customize the buttons with different colors and shapes.

            To override the default style settings for your page, use a style object inside the Buttons component. Read more about how to customize your payment buttons in the style section of the JavaScript SDK reference page.

            Integrate 3D Secure

            To trigger 3D Secure authentication, pass the verification method in the Create order payload. The verification method can be a contingencies parameter with SCA_ALWAYS or SCA_WHEN_REQUIRED:

            • Pass SCA_ALWAYS to trigger an authentication for every transaction.
            • Pass SCA_WHEN_REQUIRED to trigger an authentication only when required by a regional compliance mandate such as PSD2. 3D Secure is supported only in countries with a PSD2 compliance mandate.
            3

            Integrate back end

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

            Back-end process
            1. Your app shows a customer the available payment methods on the server side.
            2. Your app creates an order on the back end by making a call to the Create Orders v2 API endpoint.
            3. 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

            1. Generate a PayPal-Auth-Assertion header

            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.

              1// Node.js
              2
              3
              4function encodeObjectToBase64(object) {
              5 const objectString = JSON.stringify(object);
              6 return Buffer
              7 .from(objectString)
              8 .toString("base64");
              9}
              10
              11
              12const clientId = "CLIENT-ID";
              13const sellerPayerId = "SELLER-PAYER-ID"; // preferred
              14// const sellerEmail = "SELLER-ACCOUNT-EMAIL"; // use instead if payer-id unknown
              15
              16
              17const header = {
              18 alg: "none"
              19};
              20const encodedHeader = encodeObjectToBase64(header);
              21
              22
              23const payload = {
              24 iss: clientId,
              25 payer_id: sellerPayerId
              26 // email: sellerEmail
              27};
              28const encodedPayload = encodeObjectToBase64(payload);
              29
              30
              31const jwt = `${encodedHeader}.${encodedPayload}.`; // json web token
              32console.log(`Paypal-Auth-Assertion=${jwt}`);

              Modify the code:

              • Replace CLIENT-ID with the client ID of the platform or marketplace from the PayPal developer dashboard.
              • Replace SELLER-PAYER-ID with the payer ID or the email of the receiving seller's PayPal account.

              2. Set up your back end

              Set up API endpoints on your server to call the PayPal Orders v2 API.

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

              The following /server/server.js code sample shows how to integrate the back-end code for advanced Checkout. The code adds routes to an Express server to create orders and capture payments using the Orders v2 API.

              Save the server.js file in a folder named /server.

                1// server.js file
                2import express from "express";
                3import fetch from "node-fetch";
                4import "dotenv/config";
                5
                6
                7const {
                8 BN_CODE,
                9 PAYPAL_CLIENT_ID,
                10 PAYPAL_CLIENT_SECRET,
                11 PORT = 8888
                12} = process.env;
                13const base = "https://api-m.sandbox.paypal.com";
                14const app = express();
                15
                16
                17app.set("view engine", "ejs");
                18app.set("views", "./server/views");
                19app.use(express.static("client"));
                20
                21
                22// parse post params sent in body in json format
                23app.use(express.json());
                24
                25
                26const SELLER_PAYER_ID = "" //Add Seller Payer ID
                27
                28
                29/**
                30 * Generate an OAuth 2.0 access token for authenticating with PayPal REST APIs.
                31 * @see https://developer.paypal.com/api/rest/authentication/
                32 */
                33const generateAccessToken = async () => {
                34 try {
                35 if (!PAYPAL_CLIENT_ID || !PAYPAL_CLIENT_SECRET) {
                36 throw new Error("MISSING_API_CREDENTIALS");
                37 }
                38 const auth = Buffer.from(
                39 PAYPAL_CLIENT_ID + ":" + PAYPAL_CLIENT_SECRET,
                40 ).toString("base64");
                41 //const bn_code = BN_CODE;
                42 const response = await fetch(`${base}/v1/oauth2/token`, {
                43 method: "POST",
                44 body: "grant_type=client_credentials",
                45 headers: {
                46 Authorization: `Basic ${auth}`,
                47 "PayPal-Partner-Attribution-Id": BN_CODE,
                48 },
                49 });
                50
                51
                52 const data = await response.json();
                53 return data.access_token;
                54 } catch (error) {
                55 console.error("Failed to generate Access Token:", error);
                56 }
                57};
                58
                59
                60const authassertion = async () => {
                61
                62
                63 function encodeObjectToBase64(object) {
                64 const objectString = JSON.stringify(object);
                65 return Buffer
                66 .from(objectString)
                67 .toString("base64");
                68 }; ]n
                69 const clientId = PAYPAL_CLIENT_ID;
                70 const sellerPayerId = SELLER_PAYER_ID; // preferred
                71 // const sellerEmail = "SELLER-ACCOUNT-EMAIL"; // use instead if payer-id unknown
                72
                73
                74 const header = {
                75 alg: "none"
                76 };
                77 const encodedHeader = encodeObjectToBase64(header);
                78
                79
                80 const payload = {
                81 iss: clientId,
                82 payer_id: sellerPayerId
                83 // email: sellerEmail
                84 };
                85 const encodedPayload = encodeObjectToBase64(payload);
                86
                87
                88 const jwt = `${encodedHeader}.${encodedPayload}.`; // json web token
                89 //console.log(`Paypal-Auth-Assertion=${jwt}`);
                90
                91
                92 return jwt;
                93};
                94
                95
                96
                97/**
                98 * Generate a client token for rendering the hosted card fields.
                99 * @see https://developer.paypal.com/docs/checkout/advanced/integrate/#link-integratebackend
                100 */
                101const generateClientToken = async () => {
                102 const accessToken = await generateAccessToken();
                103 const url = `${base}/v1/identity/generate-token`;
                104 const response = await fetch(url, {
                105 method: "POST",
                106 headers: {
                107 Authorization: `Bearer ${accessToken}`,
                108 "Accept-Language": "en_US",
                109 "Content-Type": "application/json",
                110 "PayPal-Partner-Attribution-Id": BN_CODE,
                111 },
                112 });
                113
                114
                115 return handleResponse(response);
                116};
                117
                118
                119/**
                120 * Create an order to start the transaction.
                121 * @see https://developer.paypal.com/docs/api/orders/v2/#orders_create
                122 */
                123const createOrder = async (cart) => {
                124 // Use the cart information passed from the front-end to calculate the purchase unit details
                125 console.log(
                126 "shopping cart information passed from the frontend createOrder() callback:",
                127 cart,
                128 );
                129
                130
                131 const accessToken = await generateAccessToken();
                132 const auth_assertion = await authassertion();
                133 const url = `${base}/v2/checkout/orders`;
                134 const payload = {
                135 intent: "CAPTURE",
                136 purchase_units: [{
                137 amount: {
                138 currency_code: "USD",
                139 value: "100.00",
                140 },
                141 }, ],
                142 };
                143
                144
                145 const response = await fetch(url, {
                146 headers: {
                147 "Content-Type": "application/json",
                148 Authorization: `Bearer ${accessToken}`,
                149 "PayPal-Partner-Attribution-Id": BN_CODE,
                150 "PayPal-Auth-Assertion": `${auth_assertion}`,
                151 // Uncomment one of these to force an error for negative testing (in sandbox mode only). Documentation:
                152 // https://developer.paypal.com/tools/sandbox/negative-testing/request-headers/
                153 // "PayPal-Mock-Response": '{"mock_application_codes": "MISSING_REQUIRED_PARAMETER"}'
                154 // "PayPal-Mock-Response": '{"mock_application_codes": "PERMISSION_DENIED"}'
                155 // "PayPal-Mock-Response": '{"mock_application_codes": "INTERNAL_SERVER_ERROR"}'
                156 },
                157 method: "POST",
                158 body: JSON.stringify(payload),
                159 });
                160
                161
                162 return handleResponse(response);
                163};
                164
                165
                166/**
                167 * Capture payment for the created order to complete the transaction.
                168 * @see https://developer.paypal.com/docs/api/orders/v2/#orders_capture
                169 */
                170const captureOrder = async (orderID) => {
                171 const accessToken = await generateAccessToken();
                172 const auth_assertion = await authassertion();
                173 const url = `${base}/v2/checkout/orders/${orderID}/capture`;
                174
                175
                176 const response = await fetch(url, {
                177 method: "POST",
                178 headers: {
                179 "Content-Type": "application/json",
                180 Authorization: `Bearer ${accessToken}`,
                181 "PayPal-Partner-Attribution-Id": BN_CODE,
                182 "PayPal-Auth-Assertion": `${auth_assertion}`,
                183 // Uncomment one of these to force an error for negative testing (in sandbox mode only). Documentation:
                184 // https://developer.paypal.com/tools/sandbox/negative-testing/request-headers/
                185 // "PayPal-Mock-Response": '{"mock_application_codes": "INSTRUMENT_DECLINED"}'
                186 // "PayPal-Mock-Response": '{"mock_application_codes": "TRANSACTION_REFUSED"}'
                187 // "PayPal-Mock-Response": '{"mock_application_codes": "INTERNAL_SERVER_ERROR"}'
                188 },
                189 });
                190
                191
                192 return handleResponse(response);
                193};
                194
                195
                196async function handleResponse(response) {
                197 try {
                198 const jsonResponse = await response.json();
                199 return {
                200 jsonResponse,
                201 httpStatusCode: response.status,
                202 };
                203 } catch (err) {
                204 const errorMessage = await response.text();
                205 throw new Error(errorMessage);
                206 }
                207}
                208
                209
                210// Render checkout page with client id & unique client token
                211app.get("/", async (req, res) => {
                212 try {
                213 const {
                214 jsonResponse
                215 } = await generateClientToken();
                216 res.render("checkout", {
                217 clientId: PAYPAL_CLIENT_ID,
                218 clientToken: jsonResponse.client_token,
                219 BN_CODE: BN_CODE,
                220 });
                221 } catch (err) {
                222 res.status(500).send(err.message);
                223 }
                224});
                225
                226
                227app.post("/api/orders", async (req, res) => {
                228 try {
                229 // Use the cart information passed from the front-end to calculate the order amount details
                230 const {
                231 cart
                232 } = req.body;
                233 const {
                234 jsonResponse,
                235 httpStatusCode
                236 } = await createOrder(cart);
                237 res.status(httpStatusCode).json(jsonResponse);
                238 } catch (error) {
                239 console.error("Failed to create order:", error);
                240 res.status(500).json({
                241 error: "Failed to create order."
                242 });
                243 }
                244});
                245
                246
                247app.post("/api/orders/:orderID/capture", async (req, res) => {
                248 try {
                249 const {
                250 orderID
                251 } = req.params;
                252 const {
                253 jsonResponse,
                254 httpStatusCode
                255 } = await captureOrder(orderID);
                256 res.status(httpStatusCode).json(jsonResponse);
                257 } catch (error) {
                258 console.error("Failed to create order:", error);
                259 res.status(500).json({
                260 error: "Failed to capture order."
                261 });
                262 }
                263});
                264
                265
                266app.listen(PORT, () => {
                267 console.log(`Node server listening at http://localhost:${PORT}/`);
                268});
                269
                270
                271
                272/** If needed, refund a captured payment to buyer **/
                273const refundCapturedPayment = async (capturedPaymentId) => {
                274 const accessToken = await generateAccessToken();
                275 const url = `${base}/v2/payments/captures/${capturedPaymentId}/refund`;
                276
                277
                278 const response = await fetch(url, {
                279 headers: {
                280 "Content-Type": "application/json",
                281 Authorization: `Bearer ${accessToken}`,
                282 },
                283 method: "POST",
                284 });
                285
                286
                287 return handleResponse(response);
                288};
                289
                290
                291// refundCapturedPayment route
                292app.post("/api/payments/refund", async (req, res) => {
                293 try {
                294 const {
                295 capturedPaymentId
                296 } = req.body;
                297 const {
                298 jsonResponse,
                299 httpStatusCode
                300 } = await refundCapturedPayment(
                301 capturedPaymentId
                302 );
                303 res.status(httpStatusCode).json(jsonResponse);
                304 } catch (error) {
                305 console.error("Failed refund captured payment:", error);
                306 res.status(500).json({
                307 error: "Failed refund captured payment."
                308 });
                309 }
                310});
                4

                Test integration

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

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

                Next steps & customizations

                Add security to your checkout experience, or create customizations for your audience. Before you go live, you should complete the integration checklist.

                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.

                Required
                Integration checklist

                Go through the integration checklist before you go live.

                Security and customizations
                Optional
                Capture payment

                Captures payment for an order.

                Optional
                Subscriptions

                Create subscriptions to bill customers at regular intervals.

                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.