The Payment Request Button solution (also known as a Wallet Button) provides a single integration for Apple Pay and Google Pay.
With our Payment Request Button solution, also known as a Wallet Button, you can provide a single integration where shoppers see either a Google Pay or Apple Pay button, depending on the device and the browser specifics. If neither option is available, nothing is shown.
This guide covers the following topics:
Supported browsers
Google Pay
- Supports Google Chrome, Mozilla Firefox, Apple Safari, Microsoft Edge, Opera, and UCWeb UC Browser.
- For supported countries, refer here.
- For more information about Google Pay, refer here.
Apple Pay
- Browsers:
- Worldwide (except China): iOS 10 and later, macOS 10.12 and later (Safari)
- China: iOS 11.2 and later (Not available in macOS)
- For supported countries, refer here.
- For more information about Apple Pay support, refer here.
Prerequisites
-
Verify your domain with Apple Pay. A requirement for both development and production. Refer to Verify your domain for more details.
-
In the BlueSnap Merchant Portal, ensure that the Google Pay and Apple Pay payment methods are enabled.
-
Application must be served over HTTPS. A requirement for both development and production.
-
You must have a BlueSnap account. If you don't have an account yet, you can sign up for one here.
-
By integrating Google Pay, you agree to Google’s terms and conditions. For more details, refer here.
Implementing the Payment Request Button
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
For example, the Payment Request Button token request (in step 1) should be sent to:
https://sandpay.bluesnap.com/services/2/payment-fields-tokens
on Sandbox
ORhttps://pay.bluesnap.com/services/2/payment-fields-tokens
on Production
Follow the steps below to implement Payment Request.
Step 1: Obtain the Payment Request Button token for the session
Obtain the Payment Request Button token by sending a server-to-server POST request to:
Production: https://ws.bluesnap.com/services/2/payment-fields-tokens
Sandbox: https://sandbox.bluesnap.com/services/2/payment-fields-tokens
Important
Be sure to include the intended URL for the environment you are choosing to use. Including a URL that does not match the environment it is being used in will prevent the solution from working correctly.
The response provides the token in the location header. For example:
Production: https://ws.bluesnap.com/services/2/payment-fields-tokens/12345abcde
Sandbox: https://sandbox.bluesnap.com/services/2/payment-fields-tokens/12345abcde
Note
The Payment Request Button token expires after 60 minutes.
Step 2: Add the BlueSnap JavaScript file to your checkout form
In your checkout form, call the BlueSnap Payment Request Button JavaScript file by adding the following script:
<script type="text/javascript" src="BLUESNAPDOMAINPATH/web-sdk/5/bluesnap.js"></script>
Step 3: Add a div element to your page
Add the following div
element to your page. BlueSnap uses this to create the Payment Request Buttons.
<div data-bluesnap="walletButton"></div>
Step 4: Activate the Payment Request Button setup
To activate the setup, you must create the sdk request object.
SdkRequest = {
token: 'TOKEN_STRING', // The token you created in Step 1.
googlePay: true, // OPTIONAL default is true.
applePay: false, // OPTIONAL default is true.
paymentData: {
currencyCode: 'USD', // The three-letter ISO 4217 currency code for the payment.
countryCode: 'US', // The merchant’s two-letter ISO 3166 country code.
total: {
label: 'My Total',
amount: '123.45',
} },
// Use black buttons on white or light backgrounds to provide contrast.
// Use white buttons on dark or colorful backgrounds.
theme: 'white', // OPTIONAL black or white default is 'black'
emailRequired: true, // OPTIONAL default false
phoneRequired: true, // OPTIONAL default false
fullBilling: true, // OPTIONAL default false
shippingRequired: true, // OPTIONAL default false
shippingOptions: [ // OPTIONAL, MANDATORY if shippingRequired: true
{ // first item is the default selection
identifier: "FREE",
detail: "A free of charge",
label: "my label",
amount: "12.23"
},
{
identifier: "EXPRESS",
detail: "A costly one",
label: "my second label",
amount: "45.1"
}
]
},
onEvent: { // this event handler will handle all the events arriving from the Payment Request Button
paymentAuthorized: function (success, error) {
// here you will create the transaction Server2Server call Auth / Capture
// transaction result will decide which function to activate
// according to the transaction result status we will activate either success() or error()
if (transactionResultStatus === 'success') {
success();
} else {
error('here you will write a msg to the shopper')
}
// Note: IT IS MANDATORY TO CALL ONE OF THE FUNCTIONS.
},
shippingOptionChange: function (shippingOptionsData, oldData, update) {
// shippingOptionsData = {identifier}
// oldData = {currencyCode, countryCode, total, shippingOptions}
// relevant only if shipping option is true
// IT IS MANDATORY TO CALL update() IN ONE OF THE VARIATION BELOW
update({ total, shippingOptions, error }); // or update({}); for no change
},
shippingAddressChange: function (shippingAddressData, oldData, update) {
// shippingAddressData = {administrativeArea, countryCode, locality, postalCode}
// oldData = {currencyCode, countryCode, total, shippingOptions}
// relevant only if shippingRequired is true
// IT IS MANDATORY TO CALL update() IN ONE OF THE VARIATION BELOW
update({ total, shippingOptions, error }); // or update({}); for no change
},
error: function (event) {
// handle error event
/* for example when code = 40 (Can't use requested Wallet/s, no payment option is available)
event = {
status: "No payment method available",
code: "40",
info: {
errors: [
"Can't use requested Wallet/s, no payment option is available."
]
}
*/
},
warning: function (event) {
// handle warning event
},
},
}
- For more information, refer to:
paymentData
objecttotal
objectshippingOption
object
After you create the object, activate the setup function using the sdk request object you created.
bluesnap.walletButtonSetup(sdkRequest);
The relevant buttons will now be displayed as applicable inside the element container.
Step 5: Process the payment using the token
When the shopper confirms the purchase (activating the payment authorized event from the sdkRequest
event handler), you must send the token to your server and process the transaction using the Payment API Hosted Payment Fields.
After you receive the response, you must communicate the result to the browser using either success
or error
functions. This prompts the browser to close the payment UI or display an error message.
// taken from the sdkRequest event handler
paymentAuthorized: function (success, error) {
// here you will create the transaction Server2Server call Auth/Capture
// transaction result will decide which function to activate
// according to the transaction result status we will activate either success() or error()
if (transactionResultStatus === 'success') {
success();
} else {
error('here you will write a msg to the shopper')
}
// Please Note: IT IS MANDATORY TO CALL ONE OF THE FUNCTIONS.
},
After the event triggers, you can then process payments by including the Payment Request Button 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.
Object details
sdkRequest
object
sdkRequest
objectProperty | Type | Required | Description |
---|---|---|---|
token | string | Required | Obtain the Payment Request Button token for the session |
googlePay | boolean | optional | Indicate if you want to activate the Google Pay button. Default = true . |
applePay | boolean | optional | Indicate if you want to activate the Apple Pay button. Default = true . |
paymentData | PaymentData | Required | |
onEvent | OnEvent | Required |
paymentData
object
paymentData
objectThe following properties within the paymentData
object let you tell the browser exactly what it should display in the wallet UI and define what details it should collect from the shopper (such as email and phone).
Property | Type | Required | Description |
---|---|---|---|
currencyCode | string | Required | ISO 4217 alphabetic currency code. |
countryCode | string | Required | ISO 3166-1 alpha-2 country code where the transaction is processed. This is required for merchants based in European Economic Area (EEA) countries. |
total | Total | Required | An object defining the amount of the transaction and how it should be displayed in the wallet). |
theme | string | optional | white or 'black. Default = black`. |
shippingRequired | boolean | optional | Set to true to request shipping. Default = false . |
shippingOptions | ShippingOption | Required if shippingRequired = true | List of objects containing the data of your shipping options that will be displayed in the wallet. Note: The first item in the list is selected by default. |
emailRequired | boolean | optional | Set to true to request an email address. |
phoneRequired | boolean | optional | Set to true to request a phone number. |
fullBilling | boolean | optional | Set to true to request full billing from the shopper. |
For a minimal payment data use:
let paymentData = {
currencyCode: 'USD',
countryCode: 'US',
total: {
label: 'My final total',
amount: '123.45',
}
};
total
object
total
objectProperty | Type | Required | Description |
---|---|---|---|
amount | string | Required | Total monetary value of the transaction with an optional decimal precision of two decimal places. Note: The format of the string should follow the regex /^[0-9]+(.[0-9][0-9])?$/ |
label | string | Required | Custom label for the total price within the display items. |
let total = {
label: 'My final total',
amount: '123.45'
};
shippingOption
object
shippingOption
objectIf you sell physical goods that need to be shipped, tell the browser to collect the shopper's shipping address in the payment UI by including shippingRequired: true
within PaymentData
object when you initialize sdkRequest
. If the shipping options depend on the shopper's address, you may also want to provide shippingOptions at this time.
Property | Type | Required | Description |
---|---|---|---|
identifier | string | Required | An identifier that is used later if shippingOptionChanged callback was provided. |
amount | string | Required | An amount to display regarding the shipping option cost. |
label | string | Required | The label to be displayed as the option. |
detail | string | Required | Detail to display. |
let shippingOptions = [
{
identifier: 'shipping-001',
detail: 'A free of charge shipping',
label: '10 business days',
amount: '0.00',
},
{
identifier: 'shipping-002',
detail: 'An express shipping',
label: '3 business days',
amount: '10.00',
},
];
onEvent
object
onEvent
objectProperty | Type | Required | Description |
---|---|---|---|
paymentAuthorized | Function(success, error) | Required | |
shippingOptionChange | Function(shippingOptionsData, oldData, update) | optional | Handle shipping options changes |
shippingAddressChange | Function(shippingAddressData, oldData, update) | optional | Handle shipping address changes |
error | Function(event) | Required | |
warning | Function(event) | optional |
paymentAuthorized
property
paymentAuthorized
propertyHere you create the transaction Server2Server call Auth/Capture.
The transaction result determines which function to activate.
Based on the transaction result status, activate either success()
or error()
.
.
.
.
if (transactionResultStatus === 'success') {
success();
} else {
error('here you will write a msg to the shopper')
}
.
.
.
Note: IT IS MANDATORY TO CALL ONE OF THE FUNCTIONS.
error
property
error
propertyHandle error event, for example when code = 40.
event = {
status: 'No payment method available',
code: '40',
info: {
errors: [
"Can't use requested Wallet/s, no payment option is available."
]
}
}
warning
property
warning
propertyevent = {
status: 'Invalid Data',
code: '15',
info: {
warnings: [
'shippingRequired was set to true but shippingOptions was not provided'
]
}
}
Collecting shipping information
Step 1: Handle shipping address changes
Listen to the shippingAddressChange
event sdkRequest.onEvent
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 update({total, shippingOptions, error})
with total, 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 update({})
for no change
Property | Type |
---|---|
shippingAddressData | shippingAddressData object |
oldData | oldData object |
update | update function |
shippingAddressData
Object
shippingAddressData
ObjectProperty | Type | Description |
---|---|---|
administrativeArea | string | country sub division, such as state or province |
countryCode | string | ISO 3166-1 alpha-2 country code where the transaction is processed. This is required for merchants based in European Economic Area (EEA) countries. |
locality | string | city, town, neighborhood, suburb |
postalCode | string | zip code |
oldData
object
oldData
objectProperty | Type | Description |
---|---|---|
currencyCode | string | ISO 4217 alphabetic currency code. |
countryCode | string | ISO 3166-1 alpha-2 country code where the transaction is processed. This is required for merchants based in European Economic Area (EEA) countries. |
total | Total object | |
shippingOptions | shippingOption object | List of objects containing the data of your shipping options that will be displayed in the wallet. Note: The first item in the list is selected by default. |
update
function
update
functionProperty | Type |
---|---|
total | Total object |
shippingOptions | shippingOption object |
error | updateError object |
updateError
object
updateError
objectProperty | Type |
---|---|
reason | type of string can only get either 'SHIPPING_ADDRESS_INVALID' or 'SHIPPING_ADDRESS_UNSERVICEABLE' |
message | string |
sdkRequest: {
//...
onEvent: {
shippingAddressChange: function (
shippingAddressData,
oldData,
update
) {
if (shippingAddressData.countryCode !== '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 update() BELOW
update({
error: {
message: 'Can only ship to the US',
reason: 'SHIPPING_ADDRESS_INVALID '
}
});
} else {
// Shipping address is valid
// Fetch shipping options...
//... return shipping options
// For example:
let shippingOptions = [
{
// first item is the default selection
identifier: 'FREE',
detail: 'A free of charge',
label: 'my label',
amount: '12.23'
},
{
identifier: 'EXPRESS',
detail: 'A costly one',
label: 'my second label',
amount: '45.1'
}
];
// Update payment UI
update({
total: {
label: 'My final total',
amount: '123.45'
},
shippingOptions: shippingOptions
});
}
}
}
//...
}
Step 2: 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.
Similar to the previous step, call update({total, shippingOptions, error})
with the updated details
(or call update({})
if no changes to the payment UI are required).
Property | Type |
---|---|
shippingOptionsData | shippingOptionsData object |
oldData | oldData object |
update | update function |
shippingOptionsData
object
shippingOptionsData
objectProperty | Type |
---|---|
identifier | string |
sdkRequest: {
//...
onEvent: {
shippingOptionChange: function (
shippingOptionsData,
oldData,
update
) {
// Get the ID of the selected shipping option
let selectedId = shippingOptionsData.identifier;
// extract selected option
let selectedShippingOption;
oldData.shippingOptions.forEach(function (option) {
if (option.identifier === selectedId) {
selectedShippingOption = option;
}
});
// Update display items and total
let subtotal = '100.00'; // total without shipping
let shippingPrice = selectedShippingOption.amount;
let totalAmount = (
Number(subtotal) + Number(shippingPrice)
).toFixed(2);
let total = {
label: 'My final total',
amount: totalAmount.toString()
};
// Update payment UI
// IT IS MANDATORY TO CALL THE update() IN ONE OF THE VARIATION BELOW
update({
total: total
});
}
}
//...
}
Error codes
Code | Description |
---|---|
10 | Invalid Data |
20 | Inner Error |
40 | Can't use the requested wallet(s), no payment option is available |
14040 | Token is expired |
14041 | Could not find the token |
Other codes are the standard HTTP codes | Server Error |