📘

Notes

  • This is the latest version of the Payment Request API (W3C). Information on the previous version is available here.
  • If you want to integrate to both Apple Pay and Google Pay, the Payment Request API is the best solution for your needs.

The Payment Request API is a W3C standard meant to eliminate checkout forms by creating a fast, simple, and consistent payment experience for shoppers using supported browsers. Using this solution, BlueSnap securely captures and tokenizes shopper’s data obtained from the browser, allowing you to process card payments with our Payment API while keeping your PCI burden to a minimum. This solution is especially useful for streamlining guest checkouts and minimizing friction for returning shoppers who want to pay with a new card. The shopper selects a saved payment method and confirms the purchase in the payment UI provided by the browser.

This guide covers the following topics:

Try it now

Use a supported browser to try it below. Your card is not charged. If you would rather add a test card, you can use the following:

  • ccn: 4263982640269299
  • exp: 02/2023
  • cvv: 837

Supported browsers

Visit caniuse.com for the latest list of supported browsers.

Prerequisites

  • Make sure your website is served over HTTPS.
  • Have a traditional checkout form (that uses Hosted Payment Fields, for example) that you can display if the shopper is not using a supported browser or if they don't have a supported card.

👍

Use Hosted Payment Fields to minimize your PCI burden

We recommend using Hosted Payment Fields in addition to the Payment Request API to keep your PCI to the minimum SAQ-A.

Implementing the Payment Request API

🚧

Insert the domain for either Sandbox or Production

In all steps below, replace the BLUESNAPDOMAINPATH with the relevant domain for either
the BlueSnap Sandbox or Production environment, as follows:

Sandbox: https://sandpay.bluesnap.com/
Production: https://pay.bluesnap.com/

Follow the steps below to implement Payment Request.

Step 1: Obtain a token for the session

Obtain a token for the session by sending a server-to-server POST request to the URL of the relevant environment.
Obtain the Payment Request token by sending a server-to-server POST request to:

BLUESNAPDOMAINPATH/services/2/payment-fields-tokens

The response provides the token in the location header. For example:

Production: https://ws.bluesnap.com/services/2/payment-fields-tokens/PAYMENTFIELDTOKENID
Sandbox: https://sandbox.bluesnap.com/services/2/payment-fields-tokens/PAYMENTFIELDTOKENID

📘

Notes

  • The Payment Request token expires after 60 minutes.
  • You need a BlueSnap account in order to generate an Payment Request token.
  • If you don't have an account yet, you can sign up for one here.

Step 2: Add the BlueSnap JavaScript file to your checkout form

In your checkout form, call the BlueSnap Payment Request JavaScript file by adding the following script:

<script type="text/javascript" src="BLUESNAPDOMAINPATH/web-sdk/5/bluesnap.js"></script>

Step 3: Check if shopper's browser supports Payment Request API

Make sure the shopper's browser supports the Payment Request API using
bluesnap.paymentRequestSupported();

If the browser supports Payment Request API, create a new PaymentRequest instance by calling bluesnap.paymentRequestSetup with sdkRequest (refer to step 5 for more details). If the browser does NOT support Payment Request API, set up a traditional checkout flow that uses BlueSnap's Hosted Payment Fields. For example:

if (!bluesnap.paymentRequestSupported()) {
    // Browser doesn't support the Payment Request API
    // Set up traditional checkout flow
} else {
    // follow the instructions in step 4.
}

Step 4: Add a <div> element to your page

Add the following <div> element to your page, which BlueSnap holds the iFrame containing the hidden Payment Request button.

<div data-bluesnap="paymentRequestButton"></div>

Step 5: Activate Payment Request Setup

In order to activate the setup we first need to create and sdkRequest object.

const sdkRequest = {
    token: 'TOKEN_STRING', // The token you created in Step 1.                    
        methodData: [], // Optional (see: "Deep dive into defining detailsObj" for more details)
            details: { // Required, The object that holds data about the transaction (such as the transaction total) that tells the browser what to display in the payment UI, among other things (see: "Deep dive into defining detailsObj" for more details).
        "total": {
            "label": "Total due",
                "amount": {
                "currency": "USD",
                    "value": "10.00"
            }
        }
    },
    options: { }, // Optional (see: "Deep dive into defining detailsObj" for more details)
    onEvent: { // this event handler handles all the events arriving from the Payment Request API
        canMakePayment: function (promise) {
            // Check whether the shopper has saved a supported card using canMakePayment(). 
            // If they have one present, call showButton() to display the button that BlueSnap provides, 
            // which, when clicked, shows the payment UI provided by the browser. 
            // If the shopper doesn't have a supported card, it is recommended to set up a traditional 
            // checkout flow.                          
            promise.then(result => {
                // setup was successful 
                if (result.haveSupportedPaymentMethod) {
                    // The shopper has a supported card 
                    // Show the Payment Request button 
                    result.showButton();
                    //If you would rather use your own button to show the payment UI go refer to "Using your own button to show the payment UI" to learn how.
                } else {
                    // The shopper doesn't have a supported card 
                    // Set up traditional checkout flow  
                }
            }).catch(error => {
                // setup failed, fallback to traditional checkout flow
            });
        },
        paymentAuthorized: function (data, complete) {
            // here you create the transaction Server2Server call Auth / Capture
            // transaction result decides transaction Complete Status
            const transactionCompleteStatus = 'success'; // receives 'success' or, 'fail' according to the transaction status               

            // IT IS MANDATORY TO CALL THE complete() BELOW
            complete(transactionCompleteStatus);
        },
        shippingOptionChange: function (data, updateWith) {
            // relevant only if shipping option is true
            // see: "Handle shipping address changes for more details 
            // IT IS MANDATORY TO CALL THE updateWith() IN ONE OF THE VARIATION BELOW
            updateWith(details); // or updateWith(); for no change 
        },
        shippingAddressChange: function (data, updateWith) {
            // relevant only if shipping option is true
            // see: "Handle shipping options changes" for more details 
            // IT IS MANDATORY TO CALL THE updateWith() IN ONE OF THE VARIATION BELOW
            updateWith(details); // or updateWith(); for no change
        },
        error: function (event) {
            // handle error event
            /* for example when code = 30 (status is paymentRequestError)
            event = {
                status: "paymentRequestError", 
                code: "30", 
                info: {
                    errors: [
                        {
                            field: "AbortError", 
                            info: "User closed the Payment Request UI.", 
                            code: 20
                        }    
                    ]    						    
                }
            this means user has closed the payment request.     						
            */

        },
        warning: function (event) {
            // handle warning event
        },
        complete: function () {
            // returned after complete finished.
        },
    },
    threeDS: false, // Optional,
                }

After creating the event, we activate the setup function using the sdkRequest object we created: bluesnap.paymentRequestSetup(sdkRequest);

Step 6: Process the payment using the token

Calling showButton() displays the payment UI to the shopper. The shopper either closes the payment UI (activating the error event) or successfully confirms the purchase (activating the payment authorized event from the sdkRequest event handler), at which time, you send the token to your server and process the transaction using the Payment API hosted payment fields payment api section. Once you receive the response from BlueSnap, you should communicate the result with the browser by calling complete with either 'success' or 'fail', prompting the browser to close the payment UI or display an error message.

// taken from the sdkRequest event handler

paymentAuthorized: function (data, complete) {
    // here you create the transaction Server2Server call Auth / Capture
    // transaction result decides transaction Complete Status
    const transactionCompleteStatus = 'success'; // receive 'success', 'fail' or 'unknown' according to the transaction status               
    complete(transactionCompleteStatus);
},

After the event triggers, you can then process payments by including the Payment Request API (W3C) token in your API requests. This must be done from your server and requires using your API Credentials. For more information, refer to Completing Tokenized Payments

Back to Top

Additional topics

Deep dive into defining detailsObj

Including the following properties within detailsObj allows you to tell the browser exactly what it should display in the payment UI, define what details it should collect from the shopper (such as email and phone), and specify the cards that you support on your website.

PropertyTypeRequiredDescription
detailsobjectRequiredAllows you to define the transaction total, display line items, and shipping options that the browser should display in the payment UI. At a minimum, the total must be included. (To learn how to handle shipping, refer to Collecting shipping information.)
optionsobjectOptionalAllows you to indicate whether email, name, phone, or shipping address should be collected from the shopper in the payment UI. By default, these details are not collected.
methodDataarrayOptionalAllows you to tell the browser which cards are supported on your website (as long as those cards are enabled for your BlueSnap configuration). By default, all cards that are enabled for your BlueSnap configuration are supported. (To learn how to enable or disable certain cards in the Merchant Portal, click here.)

detailsObj example

let detailsObj = {
    details: {
        displayItems: [
            {
                label: 'Subtotal',
                amount: { currency: 'USD', value: '100.00' }
            },
            {
                label: 'Tax',
                amount: { currency: 'USD', value: '5.50' }
            }
        ],
        total: {
            label: 'Total',
            amount: { currency: 'USD', value: '105.50' }
        },
        shippingOptions: [{
            id: 'standard',
            label: 'Standard shipping',
            amount: { currency: 'USD', value: '0.00' },
            selected: true // Standard shipping is selected by default in the payment UI
        }]
    },
    options: {
        requestPayerEmail: true,
        requestPayerName: true,
        requestPayerPhone: true,
        requestShipping: true
    },
    methodData: [{
        supportedMethods: 'basic-card',
        data: {
            supportedNetworks: ['mastercard', 'visa'],
            supportedTypes: ['debit', 'credit']
        }
    }]
};

Collecting shipping information

Step 1: Tell the browser to collect the shopper's shipping address

If you sell physical goods that need to be shipped, tell the browser to collect the shopper's shipping address in the payment UI. Do this by including requestShipping: true within options when you initialize paymentRequest. If the shipping options don't depend on the shopper's address (for example, you offer free, worldwide shipping), you may also want to provide shippingOptions at this time. Here is a snapshot of the sdkRequest object taken from step 5:

sdkRequest: {
    .
    .
    .
    details: {
        "total": {
            "label": "Total due",
                "amount": {
                "currency": "USD",
                    "value": "10.00"
            }
        },
        // Optional: Include shipping options
        shippingOptions: [{
            id: 'standard',
            label: 'Free shipping',
            amount: { currency: 'USD', value: '0.00' },
            selected: true // Free shipping is selected by default in the payment UI
        }]
    },
    options: {
        // Required: Tell browser to request shipping address 
        requestShipping: true
    },
    .
    .
    .
}

Step 2: Handle shipping address changes

Listen to the shippingAddressChange event (sdkRequest.onEvent, Step 5) to know when the shopper selects a shipping address, providing you the opportunity to verify that the address meets your requirements and to determine the available shipping options. To update the payment UI, call updateWith(details) with the available shipping options or an error, depending on whether the shopper's address meets your requirements. If no changes need to be made to the payment UI, call updateWith() for no change

sdkRequest: {
    .
    .
    .
    onEvent: {
        shippingAddressChange: function (data, updateWith) {

            if (data.shippingAddress.country !== 'US') {
                // Shipping address is invalid since merchant doesn't ship outside the US
                // Update payment UI with an error

                // IT IS MANDATORY TO CALL THE updateWith() BELOW
                updateWith({
                    total: {
                        label: 'Total',
                        amount: { currency: 'USD', value: '100.00' }
                    },
                    error: 'Can only ship to the US'
                });
            } else {
                // Shipping address is valid
                // Fetch shipping options from server...                               
                //... return shipping options
                // For example: 
                let availableShippingOptions = [
                    {
                        id: 'standard',
                        label: 'Standard shipping',
                        amount: { currency: 'USD', value: '0.00' }
                    },
                    {
                        id: 'express',
                        label: 'Express shipping',
                        amount: { currency: 'USD', value: '10.00' }
                    }
                ];

                // Update payment UI 
                updateWith({
                    total: {
                        label: 'Total',
                        amount: { currenty: 'USD', value: '100.00' }
                    },
                    shippingOptions: availableShippingOptions
                });
            },
        },
    },
    .
    .
    .
}.

Step 3: Handle shipping options changes

Listen to the shippingOptionChange event to know when the shopper selects a shipping option, providing you the opportunity to mark the shipping option as selected and to update the total and display items. Similar to the previous step, call updateWith(details) with the updated details (or call updateWith() if no changes to the payment UI are required).

sdkRequest: {
    .
    .
    .
    onEvent: {
        shippingAddressChange: function (data, updateWith) {
            // Get the ID of the selected shipping option
            var selectedId = data.shippingOption;

            // Mark option as selected
            let selectedShippingOption;
            availableShippingOptions.forEach(function (option) {
                option.selected = (option.id === selectedId);
                if (option.id === selectedId) {
                    selectedShippingOption = option;
                }
            });

            // Update display items and total
            let subtotal = '100.00';
            let shippingPrice = selectedShippingOption.amount.value;
            let totalAmount = (Number(subtotal) + Number(shippingPrice)).toFixed(2);
            let displayItems = [
                {
                    label: 'Subtotal',
                    amount: { currency: 'USD', value: subtotal }
                },
                {
                    label: selectedShippingOption.label,
                    amount: { currency: 'USD', value: shippingPrice }
                }
            ];
            let total = {
                label: 'Total',
                amount: { currency: 'USD', value: totalAmount }
            };

            // Update payment UI

            // IT IS MANDATORY TO CALL THE updateWith() IN ONE OF THE VARIATION BELOW
            updateWith({
                displayItems: displayItems,
                total: total,
                shippingOptions: availableShippingOptions
            });
        }
    },
    .
    .
    .
}

Styling the Payment Request button

To style the Payment Request button, create a style object using the following properties and pass it to showButton(). To adjust the height and width, style the div element from Step 4. The button's minimum height is 32px, its maximum height is 64px, and its width is always 100%.

sdkRequest: {
    .
    .
    .
    onEvent: {
        canMakePayment: function (promise) {
            promise.then(result => {
                if (result.haveSupportedPaymentMethod) {
                    let style = {
                        theme: 'light', // default is 'dark'
                        text: 'Check out' // default is 'Pay now'
                    };

                    result.showButton(style);
                } else {
                    // The shopper doesn't have a supported card 
                    // Set up traditional checkout flow  
                }
            }).catch(error => {
                // setup failed, fallback to traditional checkout flow
            });
        },
    },
    .
    .
    .
}

The images below show a few examples of the Payment Request button.

  • The left button shows the default appearance.
  • The right button shows the light theme with the default text.
262

Using your own button to show the payment UI

If you would rather use your own button to show the payment UI, use show() (instead of showButton()). This method shows the payment UI to the shopper and returns a promise. The shopper either closes the payment UI (causing the promise to reject) or successfully confirms the purchase (causing the promise to resolve).

When the promise resolves, send the token to your server and process the transaction using the Payment API (click here for code samples). Once you receive the response from BlueSnap, communicate the result with the browser by calling complete with either 'success' or 'fail', prompting the browser to close the payment UI or display an error message.

sdkRequest: {
    .
    .
    .
    onEvent: {
        canMakePayment: function (promise) {
            promise.then(result => {
                if (result.haveSupportedPaymentMethod) {
                    document.querySelector('#ID').addEventListener('click', result.show);
                } else {
                    // The shopper doesn't have a supported card 
                    // Set up traditional checkout flow  
                }
            }).catch(error => {
                // setup failed, fallback to traditional checkout flow
            });
        },
    },
    .
    .
    .
}

Possible errors

CodeDescription
10Invalid Data
14040token is expired

Back to Top