This is the latest version of the Payment Request API (W3C). Information on the previous version is available here.
The Payment Request API is a W3C standard meant to 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 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://sandbox.bluesnap.com
Production:https://ws.bluesnap.com
For example, the Payment Request token request (step 1) should be sent to
- Sandbox:
https://sandbox.bluesnap.com/services/2/payment-fields-tokens
- Production:
https://ws.bluesnap.com/services/2/payment-fields-tokens
Follow the steps below to implement Payment Request.
Step 1: Obtain a token for the session
Step 2: Add the BlueSnap JavaScript file to your checkout form
Step 3: Check if shopper's browser supports Payment Request API
Step 4: Add a div element to your page
Step 5: Activate Payment Request Setup
Step 6: Process the payment using the token
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:
BLUESNAPDOMAINPATH/services/2/payment-fields-tokens/HOSTEDFIELDTOKENID
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/4/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
<div>
element to your pageAdd 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
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.
Property | Type | Required | Description |
---|---|---|---|
details | object | Required | Allows 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.) |
options | object | Optional | Allows 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. |
methodData | array | Optional | Allows 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.
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
Code | Description |
---|---|
10 | Invalid Data |
14040 | token is expired |