Payment Request Button

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

  1. Verify your domain with Apple Pay. A requirement for both development and production. Refer to Verify your domain for more details.

  2. In the BlueSnap Merchant Portal, ensure that the Google Pay and Apple Pay payment methods are enabled.

  3. Application must be served over HTTPS. A requirement for both development and production.

  4. You must have a BlueSnap account. If you don't have an account yet, you can sign up for one here.

  5. 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
    OR
  • https://pay.bluesnap.com/services/2/payment-fields-tokens on Production

Follow the steps below to implement Payment Request.

  1. Obtain the Payment Request Button token for the session

  2. Add the BlueSnap JavaScript file to your checkout form

  3. Add a div element to your page

  4. Activate Payment Request Button Setup

  5. Process the payment using the token

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

        },

    },

}

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

PropertyTypeRequiredDescription
tokenstringRequiredObtain the Payment Request Button token for the session
googlePaybooleanoptionalIndicate if you want to activate the Google Pay button. Default = true.
applePaybooleanoptionalIndicate if you want to activate the Apple Pay button. Default = true.
paymentDataPaymentDataRequired 
onEventOnEventRequired 

paymentData object

The 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).

PropertyTypeRequiredDescription
currencyCodestringRequiredISO 4217 alphabetic currency code.
countryCodestringRequiredISO 3166-1 alpha-2 country code where the transaction is processed. This is required for merchants based in European Economic Area (EEA) countries.
totalTotalRequiredAn object defining the amount of the transaction and how it should be displayed in the wallet).
themestringoptionalwhite or 'black. Default = black`.
shippingRequiredbooleanoptionalSet to true to request shipping. Default = false.
shippingOptionsShippingOptionRequired if shippingRequired = trueList 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.
emailRequiredbooleanoptionalSet to true to request an email address.
phoneRequiredbooleanoptionalSet to true to request a phone number.
fullBillingbooleanoptionalSet 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

PropertyTypeRequiredDescription
amountstringRequiredTotal 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])?$/
labelstringRequiredCustom label for the total price within the display items.
let total = {
  label: 'My final total',
  amount: '123.45'
};

shippingOption object

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

PropertyTypeRequiredDescription
identifierstringRequiredAn identifier that is used later if shippingOptionChanged callback was provided.
amountstringRequiredAn amount to display regarding the shipping option cost.
labelstringRequiredThe label to be displayed as the option.
detailstringRequiredDetail 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

PropertyTypeRequiredDescription
paymentAuthorizedFunction(success, error)Required
shippingOptionChangeFunction(shippingOptionsData, oldData, update)optionalHandle shipping options changes
shippingAddressChangeFunction(shippingAddressData, oldData, update)optionalHandle shipping address changes
errorFunction(event)Required 
warningFunction(event)optional 

paymentAuthorized property

Here 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

Handle 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

event = {
  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

PropertyType
shippingAddressDatashippingAddressData object
oldDataoldData object
updateupdate function

shippingAddressData Object

PropertyTypeDescription
administrativeAreastringcountry sub division, such as state or province
countryCodestringISO 3166-1 alpha-2 country code where the transaction is processed. This is required for merchants based in European Economic Area (EEA) countries.
localitystringcity, town, neighborhood, suburb
postalCodestringzip code

oldData object

PropertyTypeDescription
currencyCodestringISO 4217 alphabetic currency code.
countryCodestringISO 3166-1 alpha-2 country code where the transaction is processed. This is required for merchants based in European Economic Area (EEA) countries.
totalTotal object 
shippingOptionsshippingOption objectList 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

PropertyType
totalTotal object
shippingOptionsshippingOption object
errorupdateError object

updateError object

PropertyType
reasontype of string can only get either 'SHIPPING_ADDRESS_INVALID' or 'SHIPPING_ADDRESS_UNSERVICEABLE'
messagestring
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).

PropertyType
shippingOptionsDatashippingOptionsData object
oldDataoldData object
updateupdate function

shippingOptionsData object

PropertyType
identifierstring
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

CodeDescription
10Invalid Data
20Inner Error
40Can't use the requested wallet(s), no payment option is available
14040Token is expired
14041Could not find the token
Other codes are the standard HTTP codesServer Error