Integrate card payments

PayPal Checkout plus customized card fields

SDKCurrentAdvancedLast updated: September 30th 2021, @ 5:56:39 pm


Know before you code

  • Advanced credit and debit cards requires that your business account be evaluated and approved by PayPal. You'll complete this process when you onboard in Step 1.
  • Complete the steps in Get started to get the following sandbox account information from the Developer Dashboard:

    • Your sandbox account login information
    • Your access token
  • (UK merchants) Credit is a regulated activity in the UK. Before integrating a PayPal Credit button, you must be authorized to act as a credit broker and have a credit agreement with PayPal. For more information, contact business customer support through paypal.com or by calling 0800 358 7929.

  • This client-side and server-side integration uses the following:

1. Enable your account

Before you can accept card payments on your website, you must request advanced debit and credit card processing for your sandbox business account. The process includes completing information about your business before PayPal can approve you.

For this step, you'll request the feature for your sandbox business account as you're building your integration on the sandbox. Sandbox requests are automatically approved so you can build and test your integration. Before you test and go live, you'll request the feature for your merchant account in the production environment.

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

Tip: When prompted for required data like a phone number for the sandbox business request, enter any number that fits the required format. Since this is a sandbox request, the data doesn't have to be factual.

2. Generate client token

A client token is required to uniquely identify your buyer.

The following request generates a client token that you'll use for data-client-token when you integrate the JavaScript SDK script in Step 3.

Copy the following code and modify it.

Sample client token request

curl -X POST https://api-m.sandbox.paypal.com/v1/identity/generate-token \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <ACCESS-TOKEN>' \
-H 'Accept-Language: en_US' \

Modify the code

  1. Copy the sample request code.
  2. Change <ACCESS_TOKEN> to your access token.

Sample client token response

{
  "client_token": "eyJicmFpbnRyZWUiOnsiYXV0aG9yaXphdGlvbkZpbmdlcnByaW50IjoiYjA0MWE2M2JlMTM4M2NlZGUxZTI3OWFlNDlhMWIyNzZlY2FjOTYzOWU2NjlhMGIzODQyYTdkMTY3NzcwYmY0OHxtZXJjaGFudF9pZD1yd3dua3FnMnhnNTZobTJuJnB1YmxpY19rZXk9czlic3BuaGtxMmYzaDk0NCZjcmVhdGVkX2F0PTIwMTgtMTEtMTRUMTE6MTg6MDAuMTU3WiIsInZlcnNpb24iOiIzLXBheXBhbCJ9LCJwYXlwYWwiOnsiYWNjZXNzVG9rZW4iOiJBMjFBQUhNVExyMmctVDlhSTJacUZHUmlFZ0ZFZGRHTGwxTzRlX0lvdk9ESVg2Q3pSdW5BVy02TzI2MjdiWUJ2cDNjQ0FNWi1lTFBNc2NDWnN0bDUyNHJyUGhUQklJNlBBIn19",
  "expires_in": 3600
}

A successful response contains a client token.

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

3. Add JavaScript SDK and card form

To accept payments on your website, add the PayPal JavaScript SDK code with card form elements to your checkout page.

Sample JavaScript SDK code

This fully-styled sample code adds payment buttons and card fields to your website that capture the payment immediately.

<html>
<head>

  <meta charset="utf-8"/>

  <!-- Optimal rendering on mobile devices. -->
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <!-- Optimal Internet Explorer compatibility -->
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />

  <!-- Sample CSS styles for demo purposes. You can override these styles to match your web page's branding. -->
  <link rel="stylesheet" type="text/css" href="https://www.paypalobjects.com/webstatic/en_US/developer/docs/css/cardfields.css"/>

</head>
<body>

<!-- JavaScript SDK -->
 <script src="https://www.paypal.com/sdk/js?components=buttons,hosted-fields&client-id=<YOUR-CLIENT-ID>" data-client-token="<YOUR-CLIENT-TOKEN>"></script>

   <!-- Buttons container -->
   <table border="0" align="center" valign="top" bgcolor="#FFFFFF" style="width: 39%">
     <tr>
       <td colspan="2">
         <div id="paypal-button-container"></div>
       </td>
     </tr>
     <tr><td colspan="2">&nbsp;</td></tr>
   </table>

   <div align="center"> or </div>

   <!-- Advanced credit and debit card payments form -->
   <div class="card_container">
     <form id="card-form">

       <label for="card-number">Card Number</label><div id="card-number" class="card_field"></div>
       <div>
         <label for="expiration-date">Expiration Date</label>
         <div id="expiration-date" class="card_field"></div>
       </div>
       <div>
         <label for="cvv">CVV</label><div id="cvv" class="card_field"></div>
       </div>
       <label for="card-holder-name">Name on Card</label>
       <input type="text" id="card-holder-name" name="card-holder-name" autocomplete="off" placeholder="card holder name"/>
       <div>
         <label for="card-billing-address-street">Billing Address</label>
         <input type="text" id="card-billing-address-street" name="card-billing-address-street" autocomplete="off" placeholder="street address"/>
       </div>
       <div>
         <label for="card-billing-address-unit">&nbsp;</label>
         <input type="text" id="card-billing-address-unit" name="card-billing-address-unit" autocomplete="off" placeholder="unit"/>
       </div>
       <div>
         <input type="text" id="card-billing-address-city" name="card-billing-address-city" autocomplete="off" placeholder="city"/>
       </div>
       <div>
         <input type="text" id="card-billing-address-state" name="card-billing-address-state" autocomplete="off" placeholder="state"/>
       </div>
       <div>
         <input type="text" id="card-billing-address-zip" name="card-billing-address-zip" autocomplete="off" placeholder="zip / postal code"/>
       </div>
       <div>
         <input type="text" id="card-billing-address-country" name="card-billing-address-country" autocomplete="off" placeholder="country code" />
       </div>
       <br><br>
       <button value="submit" id="submit" class="btn">Pay</button>
     </form>
   </div>

   <!-- Implementation -->
   <script>
     let orderId;

     // Displays PayPal buttons
     paypal.Buttons({
       style: {
         layout: 'horizontal'
       },
        createOrder: function(data, actions) {
           return actions.order.create({
             purchase_units: [{
               amount: {
                 value: "1.00"
               }
             }]
           });
         },
         onApprove: function(data, actions) {
           return actions.order.capture().then(function(details) {
             window.location.href = '/success.html';
           });
         }
     }).render("#paypal-button-container");

     // If this returns false or the card fields aren't visible, see Step #1.
     if (paypal.HostedFields.isEligible()) {

       // Renders card fields
       paypal.HostedFields.render({
         // Call your server to set up the transaction
         createOrder: function () {
           return fetch('/your-server/paypal/order', {
            method: 'post'
          }).then(function(res) {
              return res.json();
          }).then(function(orderData) {
            orderId = orderData.id;
            return orderId;
          });
         },

         styles: {
           '.valid': {
            'color': 'green'
           },
           '.invalid': {
            'color': 'red'
           }
         },

         fields: {
           number: {
             selector: "#card-number",
             placeholder: "4111 1111 1111 1111"
           },
           cvv: {
             selector: "#cvv",
             placeholder: "123"
           },
           expirationDate: {
             selector: "#expiration-date",
             placeholder: "MM/YY"
           }
         }
       }).then(function (cardFields) {
         document.querySelector("#card-form").addEventListener('submit', (event) => {
           event.preventDefault();

           cardFields.submit({
             // Cardholder's first and last name
             cardholderName: document.getElementById('card-holder-name').value,
             // Billing Address
             billingAddress: {
               // Street address, line 1
               streetAddress: document.getElementById('card-billing-address-street').value,
               // Street address, line 2 (Ex: Unit, Apartment, etc.)
               extendedAddress: document.getElementById('card-billing-address-unit').value,
               // State
               region: document.getElementById('card-billing-address-state').value,
               // City
               locality: document.getElementById('card-billing-address-city').value,
               // Postal Code
               postalCode: document.getElementById('card-billing-address-zip').value,
               // Country Code
               countryCodeAlpha2: document.getElementById('card-billing-address-country').value
             }
           }).then(function () {
             fetch('/your-server/api/order/' + orderId + '/capture/', {
               method: 'post'
             }).then(function(res) {
                return res.json();
             }).then(function (orderData) {
                // Three cases to handle:
                //   (1) Recoverable INSTRUMENT_DECLINED -> call actions.restart()
                //   (2) Other non-recoverable errors -> Show a failure message
                //   (3) Successful transaction -> Show confirmation or thank you

                // This example reads a v2/checkout/orders capture response, propagated from the server
                // You could use a different API or structure for your 'orderData'
                var errorDetail = Array.isArray(orderData.details) && orderData.details[0];

                if (errorDetail && errorDetail.issue === 'INSTRUMENT_DECLINED') {
                  return actions.restart(); // Recoverable state, per:
                  // https://developer.paypal.com/docs/checkout/integration-features/funding-failure/
                }

                if (errorDetail) {
                    var msg = 'Sorry, your transaction could not be processed.';
                    if (errorDetail.description) msg += '\n\n' + errorDetail.description;
                    if (orderData.debug_id) msg += ' (' + orderData.debug_id + ')';
                    return alert(msg); // Show a failure message
                }

                // Show a success message or redirect
                alert('Transaction completed!');
             })
          }).catch(function (err) {
            alert('Payment could not be captured! ' + JSON.stringify(err))
          });
         });
       });
     } else {
       // Hides card fields if the merchant isn't eligible
       document.querySelector("#card-form").style = 'display: none';
     }
   </script>

   </body>
   </html>

Modify the code

  1. Copy the sample JavaScript SDK code and paste it into the code for your checkout page.
  2. Replace YOUR-CLIENT-ID with your client ID and YOUR-CLIENT-TOKEN with the client token that you generated in Step 2.

    • The components=buttons,hosted-fields parameter displays PayPal buttons and card fields component.
    • Card fields requires a data-client-token attribute containing your generated client token.
  3. Modify the createOrder function within the paypal.HostedFields.render() function to call your server and retrieve an Order ID (or EC token) using the Orders REST API or your existing PayPal API integration.
  4. The CSS file in the <head> section is a sample for demo purposes. Instead, you should use styles that match your web site's branding.

Tip:

The JavaScript SDK has configuration that you can override, including currency, intent, and other attributes.

If you process payments that require Strong Customer Authentication (SCA), you must provide additional context about the transaction with payment indicators.

Payment processor codes

Payment processors return the following 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 represents the processor response codes that are returned in the response of authorization and capture calls:

"processor_response": {
    "avs_code": "Y",
    "cvv_code": "S",
    "payment_advice_code": "",
    "response_code": "0000"
}

If an external payment processor declines a transaction, PayPal returns a HTTP 201 Created status code and a status of DECLINED in the capture status.

See the Orders API response_code object to get the processor response code for the non-PayPal payment processor errors.

4. Capture order

If you're not redirecting your buyer to a review page after a successful approval, make sure you have logic in your server-side code that can immediately capture the order when a buyer pays with a credit or debit card. Server-side code keeps you from exposing your access token on the client.

curl -v -X POST https://api-m.sandbox.paypal.com/v2/checkout/orders/<ORDER-ID>/capture \
-H "Content-Type: application/json" \
-H 'Authorization: Bearer <ACCESS-TOKEN>' \
-H 'Accept-Language: en_US' \
  1. Copy the sample request code.
  2. Change <ORDER-ID> to the Order ID (or EC token) that you used to set up the transaction in your createOrder function.
  3. Change <ACCESS_TOKEN> to your access token.

See also

You can move on to styling your card fields and testing a purchase.

5. Style card fields

Style the card fields to align with your brand using the CSS properties supported by this integration.

Next steps

  • Set up server-side code to ensure cart completion when a customer uses an alternative payment method. Alternative payment methods include bank accounts, wallets, and local payment methods.
  • (Europe) Implement 3D Secure
  • Test and go live with this integration.

    • (Required.) Complete production onboarding to be eligible to process cards with your live PayPal account.

See also

Requirements

API response handling

Reference