🚧

This is the older version of the Payment Request API (W3C). Information on the previous version is available here.

The Payment Request API is a W3C standard that eliminates checkout forms by creating a fast, simple, and consistent payment experience for shoppers using supported browsers. Using this solution, BlueSnap securely captures and tokenizes the 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 will not be charged. If you would rather add a test card, you can use this one: ccn: 4263982640269299, exp: 02/2023, and 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

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:

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

The token is returned in the Location header in the response, as shown below. For more details, click here.

location: https://sandbox.bluesnap.com/services/2/payment-fields-tokens/cfa6cc68b844861a660ac31b7715b7bc704e092500005aebfa3d48e665ecece6_

Back to Top

Step 2: Add the BlueSnap JavaScript file to your page

Add the following BlueSnap JavaScript file to your page and replace {BlueSnap domain} with the relevant domain for Sandbox (https://sandbox.bluesnap.com) or Production (https://ws.bluesnap.com).

<script src="{BlueSnap domain}/source/web-sdk/bluesnap.js"></script>

Step 3: Create the PaymentRequest instance

Check to make sure the shopper's browser supports the Payment Request API using bluesnap.isSupported('PaymentRequest'). If so, create a new PaymentRequest instance by calling bluesnap.paymentRequest with these arguments:

  • token - The token you created in Step 1.
  • function(instance) {...} - A callback function that BlueSnap invokes after the instance is created.

If the Payment Request API is not supported, it is recommended to set up a traditional checkout flow that uses BlueSnap's Hosted Payment Fields.

if (bluesnap.isSupported('PaymentRequest')) {
  // Browser supports the Payment Request API
  // Create a new PaymentRequest instance
  var paymentRequest;
  bluesnap.paymentRequest(token, function(instance) {
    paymentRequest = instance;
  });
} else {
  // Browser doesn't support the Payment Request API
  // Set up traditional checkout flow
}

Back to Top

Step 4: Add a div element to your page

Add the following <div> element to your page that BlueSnap will use to hold the iFrame containing the hidden Payment Request button.

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

Step 5: Initialize the PaymentRequest instance

Initialize paymentRequest by calling its init method with these arguments:

  • detailsObj - The object that holds data about the transaction (such as the transaction total) that will tell the browser what to display in the payment UI, among other things (see Deep dive into defining detailsObj).
  • attValue - The value of the data-bluesnap attribute from Step 4 so BlueSnap will know where to hold the iFrame.
paymentRequest.init({
  details: {
    total: {
      label: 'Total', 
      amount: {currency: 'USD', value: '100.00'}
    }
  }
}, 'PaymentRequest') 
.then(function() {
  // Initialization was successful 
})
.catch(function(err) {
  // Initialization failed
  console.log(err); 
});

Back to Top

Step 6: Show the Payment Request button

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, will show 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.

paymentRequest.canMakePayment()
.then(function(result) {
  if (result) {
    // The shopper has a supported card 
    // Show the Payment Request button 
    paymentRequest.showButton()
    .then(...).catch(...); // Continued in Step 7
  } else {
    // The shopper doesn't have a supported card 
    // Set up traditional checkout flow  
  }
})
.catch(function(err) {
  console.log(err); 
  // Either call showButton() or fallback to traditional checkout flow
});

📘

If you would rather use your own button to show the payment UI:

Click here to learn how.

Step 7: Process the payment using the token

Calling showButton() will display the payment UI to the shopper. The shopper will either close the payment UI (causing the promise to reject) or successfully confirm the purchase (causing the promise to resolve), at which time, you'll need to 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, you'll need to 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.

// Continued from above
paymentRequest.showButton()
.then(function(ev) {
  // The shopper confirmed the purchase 
  // Send ev.token to your server and process the transaction...
      
  // ... Report the transaction result to the browser
  ev.complete('success'); // or ev.complete('fail');
}) 
.catch(function(err) {
  // The shopper closed the payment UI
  console.log(err); 
});

Once the callback function 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, see 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 BlueSnap Console, click here.)

detailsObj example

var 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 will be 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']
      }
    }]
};

Back to Top

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.

paymentRequest.init({
  details: {
    total: {
      label: "Total", 
      amount: {currency: 'USD', value: '100.00'}
    }, 
    // Optional: Include shipping options
    shippingOptions: [{
      id: 'standard', 
      label: 'Free shipping', 
      amount: {currency: 'USD', value: '0.00'}, 
      selected: true // Free shipping will be selected by default in the payment UI
    }]
  }, 
  options: {
    // Required: Tell browser to request shipping address 
    requestShipping: true
  }
}, 'PaymentRequest')
.then(...).catch(...);

Back to Top

Step 2: Handle shipping address changes

Listen to the shippingaddresschange event 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 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 noChange().

paymentRequest.on('shippingaddresschange', function(ev) {
  if (ev.shippingAddress.country !== 'US') {
    // Shipping address is invalid since merchant doesn't ship outside the US
    // Update payment UI with an error
    ev.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: 
    var 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 
    ev.updateWith({
      total: {
        label: 'Total', 
        amount: {currenty: 'USD', value: '100.00'}
      }, 
      shippingOptions: availableShippingOptions
    }); 
  }
});

Back to Top

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. Similiar to the previous step, call updateWith with the updated details (or call noChange if no changes to the payment UI are required).

paymentRequest.on('shippingoptionchange', function(ev) {
  // Get the ID of the selected shipping option
  var selectedId = ev.shippingOption; 
  
  // Mark option as selected
  var selectedShippingOption; 
  availableShippingOptions.forEach(function(option) {
    option.selected = (option.id === selectedId);
    if (option.id === selectedId) {
      selectedShippingOption = option; 
    }
  }); 
  
  // Update display items and total
  var subtotal = '100.00';
  var shippingPrice = selectedShippingOption.amount.value; 
  var totalAmount = (Number(subtotal) + Number(shippingPrice)).toFixed(2);   
  var displayItems = [
    {
      label: 'Subtotal',
      amount: {currency: 'USD', value: subtotal}
    },
    {
      label: selectedShippingOption.label,
      amount: {currency: 'USD', value: shippingPrice}
    }
  ]; 
  var total = {
    label: 'Total', 
    amount: {currency: 'USD', value: totalAmount}
  }; 
  
  // Update payment UI
  ev.updateWith({
    displayItems: displayItems, 
    total: total, 
    shippingOptions: availableShippingOptions
  }); 
});

Back to Top

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

var style = {
  theme: 'light', // default is 'dark'
  text: 'Check out' // default is 'Pay now'
}; 
paymentRequest.showButton(style)
.then(...).catch(...);

The images below show a few examples of the Payment Request button. The left button has the default appearance. The right button has 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 will either close the payment UI (causing the promise to reject) or successfully confirm 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.

paymentRequest.show()
.then(function(ev) {
  // The shopper confirmed the purchase
  // Send ev.token to your server and process the transaction...
      
  // ... Report the transaction result to the browser
  ev.complete('success'); // or ev.complete('fail');
}) 
.catch(function(err) {
  // The shopper closed the payment UI
  console.log(err); 
});

Possible errors

CodeDescription
missing_required_fieldRequired field {...} is missing
invalid_valueThe value {...} for field {...} is invalid

Back to Top