{"metadata":{"image":[],"title":"","description":""},"api":{"url":"","auth":"required","settings":"","results":{"codes":[]},"params":[]},"next":{"description":"","pages":[]},"title":"Payment Request API (W3C)","type":"basic","slug":"payment-request-api","excerpt":"","body":"[block:callout]\n{\n  \"type\": \"warning\",\n  \"body\": \"This is the latest version of the Payment Request API (W3C).  Information on the previous version is available [here](https://developers.bluesnap.com/v8976-Basics/docs/prior-payment-request-api).\"\n}\n[/block]\nThe Payment Request API is a [W3C standard](https://www.w3.org/TR/payment-request/) 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 tokenize shopper’s data obtained from the browser, allowing you to process card payments with our [Payment API](/v8976-JSON/docs) 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.\n\nThis guide covers the following topics: \n* [Try it now](#section-try-it-now)\n* [Supported browsers](#section-supported-browsers)\n* [Prerequisites](#section-prerequisites)\n* [Implementing the Payment Request API](#section-implementing-the-payment-request-api)\n* [Additional topics](#section-additional-topics)\n\n# Try it now\nUse 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:\n* ccn: `4263982640269299`\n* exp: `02/2023`\n* cvv: `837`\n<br />\n[block:html]\n{\n  \"html\": \"<script type=\\\"text/javascript\\\" src=\\\"https://sandbox.bluesnap.com/source/web-sdk/bluesnap.js\\\"></script>\\n\\n\\t<div id=\\\"BlueSnapContainer\\\" data-bluesnap=\\\"PaymentRequestButton\\\" style=\\\"width: 150px; height: 40px;\\\"></div>\\n\\t<div id=\\\"notSupportedMessage\\\" style=\\\"display: none;\\\">\\n\\t\\t<p style=\\\"border: 1px solid #cccbcb; border-radius: 5px; padding: 15px; background-color: #f7f7f7;\\\">Your browser does not support the Payment Request API. Switch to one of the supported browsers to try the button.</p>\\n\\t</div>\\n\\n\\t<script>\\n    // Demo Payment Request Button\\n\\t\\t// Note: This code is for demo purposes only\\n\\t\\tif (bluesnap.isSupported('PaymentRequest')) {\\n      console.log(\\\"This browser supports the Payment Request API\\\"); \\n\\t\\t\\tvar paymentRequest;         bluesnap.paymentRequest('a499396a6431e66745d0f762fbe10c05877a8001b5c4e953ed326e6dfa64eea7_', function(instance) {\\n        paymentRequest = instance;\\n\\n\\t\\t\\t\\tvar details = {\\n\\t\\t\\t\\t\\tdetails: {\\n\\t    \\t\\t\\t\\ttotal: {\\n\\t      \\t\\t\\t\\t\\tlabel: \\\"Total (won't be charged)\\\", \\n\\t      \\t\\t\\t\\t\\tamount: {currency: 'USD', value: '12.00'}\\n\\t    \\t\\t\\t\\t}\\n\\t  \\t\\t\\t\\t}\\n  \\t\\t\\t\\t};\\n        \\n\\t\\t\\t\\tpaymentRequest.init(details, 'PaymentRequestButton').then(function() {\\n\\t\\t\\t\\t\\tpaymentRequest.showButton().then(function(ev) {\\n\\t  \\t\\t\\t\\t\\tconsole.log(\\\"The shopper approved the payment\\\");\\n\\t  \\t\\t\\t\\t\\tev.complete('success').then(function(result) {\\n\\t  \\t\\t\\t\\t\\t\\tlocation.reload();\\n\\t  \\t\\t\\t\\t\\t});\\n\\t\\t\\t\\t\\t})\\n\\t\\t\\t\\t\\t.catch(function(e) {\\n\\t\\t\\t\\t\\t\\tconsole.log(\\\"The shopper closed the payment UI\\\");\\n\\t\\t\\t\\t\\t\\tlocation.reload();\\n\\t\\t\\t\\t\\t});\\n\\t\\t\\t\\t});\\n\\t\\t\\t});\\n\\t\\t}\\n\\t\\telse {\\n      console.log(\\\"This browser does not support the Payment Request API\\\"); \\n      document.getElementById(\\\"BlueSnapContainer\\\").style.display = \\\"none\\\"; \\n\\t\\t\\tdocument.getElementById(\\\"notSupportedMessage\\\").style.display=\\\"block\\\";\\n\\t\\t}\\n\\t</script>\"\n}\n[/block]\n# Supported browsers \nVisit <a href=\"https://caniuse.com/#feat=payment-request\" target=\"_blank\">caniuse.com</a> for the latest list of supported browsers.\n\n# Prerequisites\n* Make sure your website is served over HTTPS.\n* Have a traditional checkout form (that uses [Hosted Payment Fields](/v8976-Tools/docs/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. \n\n<br />\n[block:callout]\n{\n  \"type\": \"success\",\n  \"title\": \"Use Hosted Payment Fields to minimize your PCI burden\",\n  \"body\": \"We recommend using [Hosted Payment Fields](/v8976-Tools/docs/hosted-payment-fields) in addition to the Payment Request API to keep your PCI to the minimum SAQ-A.\"\n}\n[/block]\n# Implementing the Payment Request API\n[block:callout]\n{\n  \"type\": \"warning\",\n  \"title\": \"Insert the domain for either Sandbox or Production\",\n  \"body\": \"In all steps below, replace the `BLUESNAPDOMAINPATH` with the relevant domain for either \\nthe BlueSnap Sandbox or Production environment, as follows:\\n\\nSandbox: `https://sandbox.bluesnap.com`\\nProduction: `https://ws.bluesnap.com`\\n\\nFor example, the Payment Request token request (step 1) should be sent to \\n* Sandbox:  `https://sandbox.bluesnap.com/services/2/payment-fields-tokens`\\n* Production:  `https://ws.bluesnap.com/services/2/payment-fields-tokens`\"\n}\n[/block]\nFollow the steps below to implement Payment Request.\n[Step 1: Obtain a token for the session](#section-step-1-obtain-a-token-for-the-session)\n[Step 2: Add the BlueSnap JavaScript file to your checkout form](#section-step-2-add-the-bluesnap-javascript-file-to-your-checkout-form)\n[Step 3: Check if shopper's browser supports Payment Request API](#section-step-3-check-if-shopper-s-browser-supports-payment-request-api)\n[Step 4: Add a div element to your page](#section-step-4-add-a-div-element-to-your-page)\n[Step 5: Activate Payment Request Setup](#section-step-5-activate-payment-request-setup)\n[Step 6: Process the payment using the token](#section-step-6-process-the-payment-using-the-token)\n\n## Step 1: Obtain a token for the session\nObtain a token for the session by sending a server-to-server POST request to the URL of the relevant environment.  \nObtain the Payment Request token by sending a server-to-server POST request to:       \n`BLUESNAPDOMAINPATH/services/2/payment-fields-tokens`\nThe response provides the token in the location header. For example:\n`BLUESNAPDOMAINPATH/services/2/payment-fields-tokens/HOSTEDFIELDTOKENID`\n[block:callout]\n{\n  \"type\": \"info\",\n  \"title\": \"Notes\",\n  \"body\": \"* The Payment Request token expires after 60 minutes.\\n* You need a BlueSnap account in order to generate an Payment Request token. \\n* If you don't have an account yet, you can sign up for one [here](http://home.bluesnap.com/get-started/).\"\n}\n[/block]\n## Step 2: Add the BlueSnap JavaScript file to your checkout form\nIn your checkout form, call the BlueSnap Payment Request JavaScript file by adding the following script:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"<script type=\\\"text/javascript\\\" src=\\\"BLUESNAPDOMAINPATH/web-sdk/REQUIRED_VERSION/bluesnap.js\\\">\\n</script>\\n// Where REQUIRED_VERSION should be\\n// 4.X.X for a specific version\\n// or 4 for the exact major version \",\n      \"language\": \"html\",\n      \"name\": \"Adding Payment Request Javascript file\"\n    }\n  ]\n}\n[/block]\n## Step 3: Check if shopper's browser supports Payment Request API \n\nMake sure the shopper's browser supports the Payment Request API using \n`bluesnap.paymentRequestSupported();`\n     \nIf the browser supports Payment Request API, create a new PaymentRequest instance by calling `bluesnap.paymentRequestSetup` with sdkRequest (refer to [step 5](#section-step-5-activate-payment-request-setup) 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: \n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"if (!bluesnap.paymentRequestSupported()) {\\n    // Browser doesn't support the Payment Request API\\n    // Set up traditional checkout flow\\n} else {\\n    // follow the instructions in step 4.\\n}\",\n      \"language\": \"javascript\"\n    }\n  ]\n}\n[/block]\n## Step 4: Add a `<div>` element to your page\nAdd the following `<div>` element to your page, which BlueSnap holds the iFrame containing the hidden Payment Request button.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"<div data-bluesnap=\\\"paymentRequestButton\\\"></div>\",\n      \"language\": \"html\"\n    }\n  ]\n}\n[/block]\n## Step 5: Activate Payment Request Setup\nIn order to activate the setup we first need to create and sdkRequest object.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"sdkRequest: {\\n    token: 'TOKEN_STRING', // The token you created in Step 1.                    \\n        methodData: [], // Optional (see: \\\"Deep dive into defining detailsObj\\\" for more details)\\n            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).\\n        \\\"total\\\": {\\n            \\\"label\\\": \\\"Total due\\\",\\n                \\\"amount\\\": {\\n                \\\"currency\\\": \\\"USD\\\",\\n                    \\\"value\\\": \\\"10.00\\\"\\n            }\\n        }\\n    },\\n    options: { }, // Optional (see: \\\"Deep dive into defining detailsObj\\\" for more details)\\n    onEvent: { // this event handler handles all the events arriving from the Payment Request API\\n        canMakePayment: function (promise) {\\n            // Check whether the shopper has saved a supported card using canMakePayment(). \\n            // If they have one present, call showButton() to display the button that BlueSnap provides, \\n            // which, when clicked, shows the payment UI provided by the browser. \\n            // If the shopper doesn't have a supported card, it is recommended to set up a traditional \\n            // checkout flow.                          \\n            promise.then(result => {\\n                // setup was successful \\n                if (result.haveSupportedPaymentMethod) {\\n                    // The shopper has a supported card \\n                    // Show the Payment Request button \\n                    result.showButton();\\n                    //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.\\n                } else {\\n                    // The shopper doesn't have a supported card \\n                    // Set up traditional checkout flow  \\n                }\\n            }).catch(error => {\\n                // setup failed, fallback to traditional checkout flow\\n            });\\n        },\\n        paymentAuthorized: function (data, complete) {\\n            // here you create the transaction Server2Server call Auth / Capture\\n            // transaction result decides transaction Complete Status\\n            const transactionCompleteStatus = 'success'; // receives 'success' or, 'fail' according to the transaction status               \\n\\n            // IT IS MANDATORY TO CALL THE complete() BELOW\\n            complete(transactionCompleteStatus);\\n        },\\n        shippingOptionChange: function (data, updateWith) {\\n            // relevant only if shipping option is true\\n            // see: \\\"Handle shipping address changes for more details \\n            // IT IS MANDATORY TO CALL THE updateWith() IN ONE OF THE VARIATION BELOW\\n            updateWith(details); // or updateWith(); for no change \\n        },\\n        shippingAddressChange: function (data, updateWith) {\\n            // relevant only if shipping option is true\\n            // see: \\\"Handle shipping options changes\\\" for more details \\n            // IT IS MANDATORY TO CALL THE updateWith() IN ONE OF THE VARIATION BELOW\\n            updateWith(details); // or updateWith(); for no change\\n        },\\n        error: function (event) {\\n            // handle error event\\n            /* for example when code = 30 (status is paymentRequestError)\\n            event = {\\n                status: \\\"paymentRequestError\\\", \\n                code: \\\"30\\\", \\n                info: {\\n                    errors: [\\n                        {\\n                            field: \\\"AbortError\\\", \\n                            info: \\\"User closed the Payment Request UI.\\\", \\n                            code: 20\\n                        }    \\n                    ]    \\t\\t\\t\\t\\t\\t    \\n                }\\n            this means user has closed the payment request.     \\t\\t\\t\\t\\t\\t\\n            */\\n\\n        },\\n        warning: function (event) {\\n            // handle warning event\\n        },\\n        complete: function () {\\n            // returned after complete finished.\\n        },\\n    },\\n    threeDS: false, // Optional,\\n                }\",\n      \"language\": \"javascript\"\n    }\n  ]\n}\n[/block]\nAfter creating the event, we activate the setup function using the sdkRequest object we created:  `bluesnap.paymentRequestSetup(sdkRequest);`\n\n## Step 6: Process the payment using the token\nCalling `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](https://developers.bluesnap.com/v8976-Tools/docs/hosted-payment-fields#section-in-the-payment-api). 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.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"// taken from the sdkRequest event handler\\n\\npaymentAuthorized: function (data, complete) {\\n    // here you create the transaction Server2Server call Auth / Capture\\n    // transaction result decides transaction Complete Status\\n    const transactionCompleteStatus = 'success'; // receive 'success', 'fail' or 'unknown' according to the transaction status               \\n    complete(transactionCompleteStatus);\\n},\",\n      \"language\": \"javascript\"\n    }\n  ]\n}\n[/block]\nAfter 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](https://developers.bluesnap.com/v8976-Basics/docs/completing-tokenized-payments)\n\n<a class=\"btn btn-primary\" href=\"#\" role=\"button\">Back to Top</a>\n\n# Additional topics\n## Deep dive into defining detailsObj\nIncluding 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.\n\n| Property | Type | Required | Description |\n| --- | --- | --- | --- |\n| `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](#section-collecting-shipping-information).) |\n| `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. |\n| `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 BlueSnap Console, click here.) |\n\n## detailsObj example\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"let detailsObj = {\\n    details: {\\n        displayItems: [\\n            {\\n                label: 'Subtotal',\\n                amount: { currency: 'USD', value: '100.00' }\\n            },\\n            {\\n                label: 'Tax',\\n                amount: { currency: 'USD', value: '5.50' }\\n            }\\n        ],\\n        total: {\\n            label: 'Total',\\n            amount: { currency: 'USD', value: '105.50' }\\n        },\\n        shippingOptions: [{\\n            id: 'standard',\\n            label: 'Standard shipping',\\n            amount: { currency: 'USD', value: '0.00' },\\n            selected: true // Standard shipping is selected by default in the payment UI\\n        }]\\n    },\\n    options: {\\n        requestPayerEmail: true,\\n        requestPayerName: true,\\n        requestPayerPhone: true,\\n        requestShipping: true\\n    },\\n    methodData: [{\\n        supportedMethods: 'basic-card',\\n        data: {\\n            supportedNetworks: ['mastercard', 'visa'],\\n            supportedTypes: ['debit', 'credit']\\n        }\\n    }]\\n};\",\n      \"language\": \"javascript\"\n    }\n  ]\n}\n[/block]\n## Collecting shipping information\n### Step 1: Tell the browser to collect the shopper's shipping address\n\nIf 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:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"sdkRequest: {\\n    .\\n    .\\n    .\\n    details: {\\n        \\\"total\\\": {\\n            \\\"label\\\": \\\"Total due\\\",\\n                \\\"amount\\\": {\\n                \\\"currency\\\": \\\"USD\\\",\\n                    \\\"value\\\": \\\"10.00\\\"\\n            }\\n        },\\n        // Optional: Include shipping options\\n        shippingOptions: [{\\n            id: 'standard',\\n            label: 'Free shipping',\\n            amount: { currency: 'USD', value: '0.00' },\\n            selected: true // Free shipping is selected by default in the payment UI\\n        }]\\n    },\\n    options: {\\n        // Required: Tell browser to request shipping address \\n        requestShipping: true\\n    },\\n    .\\n    .\\n    .\\n}\",\n      \"language\": \"javascript\"\n    }\n  ]\n}\n[/block]\n\n### Step 2:  Handle shipping address changes\nListen 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\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"sdkRequest: {\\n    .\\n    .\\n    .\\n    onEvent: {\\n        shippingAddressChange: function (data, updateWith) {\\n\\n            if (data.shippingAddress.country !== 'US') {\\n                // Shipping address is invalid since merchant doesn't ship outside the US\\n                // Update payment UI with an error\\n\\n                // IT IS MANDATORY TO CALL THE updateWith() BELOW\\n                updateWith({\\n                    total: {\\n                        label: 'Total',\\n                        amount: { currency: 'USD', value: '100.00' }\\n                    },\\n                    error: 'Can only ship to the US'\\n                });\\n            } else {\\n                // Shipping address is valid\\n                // Fetch shipping options from server...                               \\n                //... return shipping options\\n                // For example: \\n                let availableShippingOptions = [\\n                    {\\n                        id: 'standard',\\n                        label: 'Standard shipping',\\n                        amount: { currency: 'USD', value: '0.00' }\\n                    },\\n                    {\\n                        id: 'express',\\n                        label: 'Express shipping',\\n                        amount: { currency: 'USD', value: '10.00' }\\n                    }\\n                ];\\n\\n                // Update payment UI \\n                updateWith({\\n                    total: {\\n                        label: 'Total',\\n                        amount: { currenty: 'USD', value: '100.00' }\\n                    },\\n                    shippingOptions: availableShippingOptions\\n                });\\n            },\\n        },\\n    },\\n    .\\n    .\\n    .\\n}.\",\n      \"language\": \"javascript\"\n    }\n  ]\n}\n[/block]\n### Step 3: Handle shipping options changes\nListen 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).\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"sdkRequest: {\\n    .\\n    .\\n    .\\n    onEvent: {\\n        shippingAddressChange: function (data, updateWith) {\\n            // Get the ID of the selected shipping option\\n            var selectedId = data.shippingOption;\\n\\n            // Mark option as selected\\n            let selectedShippingOption;\\n            availableShippingOptions.forEach(function (option) {\\n                option.selected = (option.id === selectedId);\\n                if (option.id === selectedId) {\\n                    selectedShippingOption = option;\\n                }\\n            });\\n\\n            // Update display items and total\\n            let subtotal = '100.00';\\n            let shippingPrice = selectedShippingOption.amount.value;\\n            let totalAmount = (Number(subtotal) + Number(shippingPrice)).toFixed(2);\\n            let displayItems = [\\n                {\\n                    label: 'Subtotal',\\n                    amount: { currency: 'USD', value: subtotal }\\n                },\\n                {\\n                    label: selectedShippingOption.label,\\n                    amount: { currency: 'USD', value: shippingPrice }\\n                }\\n            ];\\n            let total = {\\n                label: 'Total',\\n                amount: { currency: 'USD', value: totalAmount }\\n            };\\n\\n            // Update payment UI\\n\\n            // IT IS MANDATORY TO CALL THE updateWith() IN ONE OF THE VARIATION BELOW\\n            updateWith({\\n                displayItems: displayItems,\\n                total: total,\\n                shippingOptions: availableShippingOptions\\n            });\\n        }\\n    },\\n    .\\n    .\\n    .\\n}\",\n      \"language\": \"javascript\"\n    }\n  ]\n}\n[/block]\n## Styling the Payment Request button\nTo 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%.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"sdkRequest: {\\n    .\\n    .\\n    .\\n    onEvent: {\\n        canMakePayment: function (promise) {\\n            promise.then(result => {\\n                if (result.haveSupportedPaymentMethod) {\\n                    let style = {\\n                        theme: 'light', // default is 'dark'\\n                        text: 'Check out' // default is 'Pay now'\\n                    };\\n\\n                    result.showButton(style);\\n                } else {\\n                    // The shopper doesn't have a supported card \\n                    // Set up traditional checkout flow  \\n                }\\n            }).catch(error => {\\n                // setup failed, fallback to traditional checkout flow\\n            });\\n        },\\n    },\\n    .\\n    .\\n    .\\n}\",\n      \"language\": \"javascript\"\n    }\n  ]\n}\n[/block]\nThe images below show a few examples of the Payment Request button. \n*  The left button shows the default appearance. \n*  The right button shows the light theme with the default text.\n[block:image]\n{\n  \"images\": [\n    {\n      \"image\": [\n        \"https://files.readme.io/29bb0a9-buttons.png\",\n        \"buttons.png\",\n        262,\n        43,\n        \"#215aaa\"\n      ],\n      \"caption\": \"\"\n    }\n  ]\n}\n[/block]\n## Using your own button to show the payment UI\nIf 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).\n\nWhen the promise resolves, send the token to your server and process the transaction using the Payment API (click [here](https://developers.bluesnap.com/v8976-Tools/docs/hosted-payment-fields#section-in-the-payment-api) 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.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"sdkRequest: {\\n    .\\n    .\\n    .\\n    onEvent: {\\n        canMakePayment: function (promise) {\\n            promise.then(result => {\\n                if (result.haveSupportedPaymentMethod) {\\n                    document.querySelector('#ID').addEventListener('click', result.show);\\n                } else {\\n                    // The shopper doesn't have a supported card \\n                    // Set up traditional checkout flow  \\n                }\\n            }).catch(error => {\\n                // setup failed, fallback to traditional checkout flow\\n            });\\n        },\\n    },\\n    .\\n    .\\n    .\\n}\",\n      \"language\": \"javascript\",\n      \"name\": \"\"\n    }\n  ]\n}\n[/block]\n## Possible errors\n| Code | Description |\n| --- | --- |\n| 10 | Invalid Data |\n| 14040 | token is expired |\n\n<a class=\"btn btn-primary\" href=\"#\" role=\"button\">Back to Top</a>","updates":[],"order":20,"isReference":false,"hidden":false,"sync_unique":"","link_url":"","link_external":false,"_id":"5e5d3ad3e9eec8004ca0ac6a","project":"57336fd5a6a9c40e00e13a0b","version":{"version":"8976-Basics","version_clean":"8976.0.0-Basics","codename":"Live","is_stable":true,"is_beta":false,"is_hidden":false,"is_deprecated":false,"categories":["5e5d3ad3e9eec8004ca0ac16","5e5d3ad3e9eec8004ca0ac17","5e5d3ad3e9eec8004ca0ac18","5e5d3ad3e9eec8004ca0ac19","5e5d3ad3e9eec8004ca0ac1a","5e5d3ad3e9eec8004ca0ac1b","5beb278ac442ab0213f00990","5e5d3ad3e9eec8004ca0ac1c","5c3f542c12c4ac004bc51718","5e5d3ad3e9eec8004ca0ac1d","5df806ccecebde0059f17225","5dfa8a8f340c2b007418eb8a","5e5d3ad3e9eec8004ca0ac1e","5e597199fd2b0200117c2c1c"],"_id":"5e5d3ad3e9eec8004ca0ac6d","project":"57336fd5a6a9c40e00e13a0b","__v":0,"forked_from":"5c6c238ff7d54800395355a0","createdAt":"2018-04-23T14:36:48.535Z","releaseDate":"2018-04-23T14:36:48.535Z"},"category":{"sync":{"isSync":false,"url":""},"pages":[],"title":"Guides","slug":"topics","order":7,"from_sync":false,"reference":false,"_id":"5e5d3ad3e9eec8004ca0ac1c","version":"5e5d3ad3e9eec8004ca0ac6d","project":"57336fd5a6a9c40e00e13a0b","createdAt":"2015-11-03T20:45:01.593Z","__v":0},"user":"5beb1b96bc2003003ecd645e","createdAt":"2020-01-06T17:05:02.644Z","__v":0,"parentDoc":null}

Payment Request API (W3C)


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 tokenize 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/REQUIRED_VERSION/bluesnap.js">
</script>
// Where REQUIRED_VERSION should be
// 4.X.X for a specific version
// or 4 for the exact major version 

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.

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.

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 BlueSnap Console, 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

Back to Top