UNPKG

@paydock/client-sdk

Version:

Paydock client sdk

753 lines (668 loc) 26.5 kB
## Express Wallet Buttons Express Wallet Buttons allow to integrate with E-Wallets in an "express" operational mode, allowing to show the respective button in product or cart pages. The general flow to use the widgets is: 1. Configure your gateway and connect it using Paydock API or Dashboard. 2. Create a container in your site ```html <div id="widget"></div> ``` 3. Initialize the specific WalletButtonExpress, providing your Access Token (preferred) or Public Key, plus required and optional meta parameters for the wallet in use. The general format is: ```js new paydock.{Provider}WalletButtonExpress( "#widget", accessTokenOrPublicKey, gatewayId, gatewaySpecificMeta, ); ``` 4. (optional) If the screen where the button is rendered allows for cart/amount changes, call `setMeta` method to update the meta information. 5. Handle the `onClick` callback, where you should call your server, initialize the wallet charge via `POST v1/charges/wallet` and return the wallet token. 6. Handle the `onPaymentSuccessful`, `onPaymentError` and `onPaymentInReview` (if fraud is applicable) for payment results. ### Supported Providers 1. [Apple Pay](#apple-pay-wallet-button-express) 2. [Paypal](#paypal-wallet-button-express) ### Apple Pay Wallet Button Express A full description of the meta parameters for [ApplePayWalletButtonExpress](#ApplePayWalletButtonExpress) meta parameters can be found [here](#ApplePayWalletMeta). Below you will find a fully working html example. ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h2>Payment using PayDock ApplePayWalletButtonExpress!</h2> <div id="widget"></div> </body> <script src="https://widget.paydock.com/sdk/latest/widget.umd.min.js" ></script> <script> let button = new paydock.ApplePayWalletButtonExpress( "#widget", accessTokenOrPublicKey, gatewayId, { amount_label: 'TOTAL', country: 'AU', currency: 'AUD', amount: 15.5, // merchant_capabilities: ['supports3DS', 'supportsEMV', 'supportsCredit', 'supportsDebit'], // supported_networks: ['visa', 'masterCard', 'amex', 'chinaUnionPay', 'discover', 'interac', 'jcb', 'privateLabel'], // required_billing_contact_fields: ['email', 'name', 'phone', 'postalAddress'], // phone and email do not work according to relevant testing // required_shipping_contact_fields: ['email', 'phone'], // Workaround to pull phone and email from shipping contact instead - does not require additional shipping address information // supported_countries: ["AU"], // style: { // button_type: "buy", // button_style: "black", // }, } ); button.setEnv('sandbox'); button.onUnavailable(function() { console.log("Button not available"); }); button.onError(function(error) { console.log("On Error Callback", error); }); button.onPaymentSuccessful(function(data) { console.log("Payment successful"); console.log(data); }); button.onPaymentError(function(err) { console.log("Payment error"); console.log(err); }); button.onPaymentInReview(function(data) { console.log("The payment is on fraud review"); console.log(data); }); button.onClick(async (data) => { console.log("Button clicked", data); const responseData = await fetch('https://your-server-url/initialize-wallet-charge'); const parsedData = await responseData.json(); return parsedData.resource.data.token; }); button.onCheckoutClose(() => { console.log("Checkout closed"); }); button.load(); </script> </html> ``` ### Apple Pay Wallet Button Express with Shipping A full description of the meta parameters for [ApplePayWalletButtonExpress](#ApplePayWalletButtonExpress) meta parameters can be found [here](#ApplePayWalletMeta). Below you will find a fully working html example. ```html <html> <head> <title>Apple Pay Express test page</title> <style> #inputModal { display: none; position: fixed; left: 0; top: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.5); z-index: 1000; } #inputBox { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); padding: 20px; background: white; border-radius: 5px; box-shadow: 2px 2px 10px rgba(0,0,0,0.5); } </style> </head> <body> <table id='paymentTable'> <tr> <td>Environment:</td> <td> <select id="environment" name="environment"> <option value="sandbox">Sandbox</option> <option value="local">Local</option> </select> </td> </tr> <tr> <td>Access Token / Public Key:</td> <td><input type="text" name="access" id="access" /></td> </tr> <tr> <td>Secret Key:</td> <td><input type="text" name="secretKey" id="secretKey" /></td> </tr> <tr> <td>Gateway Id:</td> <td><input type="text" name="gateway" id="gateway" /></td> </tr> <tr> <td>Wallet Charge - for apple pay we need to add it here because the apple pay popup does not allow us to inject it there but it will be used only on click. So is optional, if not provided it simulates an error when creating B2B wallet charge on click:</td> <td><input type="text" name="token" id="token" /></td> </tr> <tr> <td>Charge id - Used to update the charge with the shipping data/shipping options</td> <td><input type="text" name="chargeId" id="chargeId" /></td> </tr> <tr> <td>Meta:</td> <td><textarea id="meta" name="meta" rows="4" cols="50"></textarea></td> </tr> </table> <div id="button"> <input type="submit" name="event" value="Send" class="button" onclick="return loadButtons()" /> </div> <div id="widget"></div> </body> <script src="https://widget.paydock.com/sdk/latest/widget.umd.min.js"></script> <script type=text/javascript> // Environment enum with API URLs const ENVIRONMENTS = { PRODUCTION: { name: 'production', apiUrl: 'https://api.paydock.com' }, SANDBOX: { name: 'sandbox', apiUrl: 'https://api-sandox.paydock.com' } }; // Function to get API URL based on selected environment function getApiUrl(environmentName) { const env = Object.values(ENVIRONMENTS).find(env => env.name === environmentName); return env ? env.apiUrl : ENVIRONMENTS.SANDBOX.apiUrl; } // Function to get current base URL from selected environment function getBaseUrl() { const selectedEnvironment = document.getElementById("environment").value; return getApiUrl(selectedEnvironment); } async function updateCharge(secretKey, chargeId, updateData) { try { const baseUrl = getBaseUrl(); const response = await fetch(`${baseUrl}/v1/charges/wallet/${chargeId}`, { method: 'PUT', headers: { 'x-user-secret-key': secretKey, 'Content-Type': 'application/json', }, body: JSON.stringify(updateData), }); const data = await response.json(); console.log('Update charge response:', data); return data; } catch (error) { console.error('Error updating charge:', error); } }; const shippingOptions = [ { label: "Test 1", detail: "This is a test 1 shipping methods", amount: 10, id: "randomId1", date_components_range: { start_date_components: { years: 0, months: 0, days: 5, hours: 0, }, end_date_components: { years: 0, months: 0, days: 10, hours: 0, } } }, { label: "Test 2", detail: "This is a test 2 shipping methods", amount: 14, id: "randomId2", date_components_range: { start_date_components: { years: 0, months: 0, days: 6, hours: 0, }, end_date_components: { years: 0, months: 0, days: 12, hours: 0, }, }, }, ]; function loadButtons() { const secretKey = document.getElementById("secretKey").value; if (!secretKey) { alert("Please enter the secret key."); return false; } let button = new paydock.ApplePayWalletButtonExpress( "#widget", document.getElementById("access").value, document.getElementById("gateway").value, JSON.parse(document.getElementById("meta").value), ); button.setEnv(document.getElementById("environment").value); let charge_id; button.onClick(async (data) => { const { token, chargeId } = await getUserInput(); charge_id = chargeId; return token; }); const amount = JSON.parse(document.getElementById('meta').value).amount; button.onUnavailable(function() { console.log("Button unavailable"); }); button.onError(function(error) { console.log("On Error Callback", error); }); button.onPaymentSuccessful(function(data) { console.log("Payment successful"); console.log(data); }); button.onPaymentError(function(err) { console.log("Payment error"); console.log(err); }); button.onPaymentInReview(function(data) { console.log("The payment is on fraud review"); console.log(data); }); button.onCheckoutClose(() => { console.log("Checkout closed"); }); button.onShippingAddressChange(async function(data) { console.log("Shipping address has been updated", data); const defaultOption = shippingOptions[0]; const updateData = { shipping: { ...data.data, amount: defaultOption.amount, method: defaultOption.id, options: shippingOptions, }, amount: amount + defaultOption.amount }; const res = await updateCharge(secretKey, charge_id, updateData); return { token: res.resource.data.token }; }); button.onShippingOptionsChange(async function(data) { console.log("Shipping options have been updated", JSON.stringify(data, null, 2)); const updateData = { shipping: { method: data.data.shipping_option_id, amount: data.data.amount, }, amount: amount + Number(data.data.amount) }; const res = await updateCharge(secretKey, charge_id, updateData); return { token: res.resource.data.token }; }); button.load() document.getElementById("paymentTable").style.display = "none"; document.getElementById("button").style.display = "none"; return true; } function getUserInput(message) { return new Promise((resolve, reject) => { console.log("Simulating B2B call to generate token..."); setTimeout(() => { const token = document.getElementById("token").value; const chargeId = document.getElementById("chargeId").value; if (token && chargeId) return resolve({ token, chargeId }); return reject("No token or Charge Id provided"); }, 2000); }); } document.addEventListener('DOMContentLoaded', () => { // Function to get the value of a query parameter by name function getQueryParam(name) { const urlParams = new URLSearchParams(window.location.search); return urlParams.get(name); } // Function to set input values from URL parameters function setInputValues() { const meta = { apple_pay_capabilities: ['credentials_available', 'credentials_status_unknown', 'credentials_unavailable'], amount_label: 'TOTAL', country: 'AU', currency: 'AUD', amount: 10, shipping_editing_mode: 'available', // available, store_pickup required_shipping_contact_fields: [ 'postalAddress', 'name', 'phone', 'email', ], shipping: { amount: 5, address_line1: "Address Line 1", address_city: "Test Locality", address_state: "NSW", address_country: "Australia", address_country_code: "AU", address_postcode: "3000", contact: { phone: "+61400245562", email: "qa.notifications+appleid@paydock.com", first_name: "QA", last_name: "QA", }, options: shippingOptions, }, // merchant_capabilities: ['supports3DS', 'supportsEMV', 'supportsCredit', 'supportsDebit'], // supported_networks: ['visa', 'masterCard', 'amex', 'chinaUnionPay', 'discover', 'interac', 'jcb', 'privateLabel'], // required_billing_contact_fields: ['email', 'name', 'phone', 'postalAddress', 'phoneticName'], // required_shipping_contact_fields: ['email', 'phone' /*, 'name', 'postalAddress', 'phoneticName'*/], // supported_countries: [], // style: { // button_type: 'continue', // button_style: 'white', // }, }; document.getElementById('meta').value = JSON.stringify(meta, null, 2); } setInputValues(); }); </script> </html> ``` When supporting shipping, the method `onShippingAddressChange` and `onShippingOptionsChange` are required to update the shipping address and options. ```javascript button.onShippingAddressChange(async function(data) { console.log("Shipping address has been updated", data); const updateData = { shipping: { ...data.data, amount: defaultOption.amount, method: defaultOption.id, options: shippingOptions, }, amount: amount + defaultOption.amount }; const res = await updateCharge(secretKey, charge_id, updateData); return { token: res.resource.data.token }; }); button.onShippingOptionsChange(async function(data) { console.log("Shipping options have been updated", data); const updateData = { shipping: { method: data.data.shipping_option_id, amount: data.data.amount, }, amount: amount + Number(data.data.amount) }; const res = await updateCharge(secretKey, charge_id, updateData); return { token: res.resource.data.token }; }); ``` The `updateCharge` method is a custom method to update the charge with the shipping data/shipping options. It is not part of the Paydock SDK and it should use the Paydock's public API to update the charge from the merchant's server. ### Supported Cases #### Injected Shipping Address, non-editable by the customer This is the case where the shipping address is injected by the merchant and is not editable by the customer. The customer can only select the shipping option. The required meta parameters for this case are: - shipping_editing_mode: 'store_pickup' - required_shipping_contact_fields: ['postalAddress'] <-- At least one of the fields is required so the shipping address is shown at Apple Pay. ```javascript meta: { apple_pay_capabilities: ['credentials_available', 'credentials_status_unknown', 'credentials_unavailable'], amount_label: 'TOTAL', country: 'AU', currency: 'AUD', amount: 10, shipping_editing_mode: 'store_pickup', required_shipping_contact_fields: [ 'postalAddress', // At least one of the fields is required so the shipping address is shown at Apple Pay. 'name', 'phone', 'email', ], shipping: { amount: 5, address_line1: "Address Line 1", address_city: "Test Locality", address_state: "NSW", address_country: "Australia", address_country_code: "AU", address_postcode: "3000", contact: { phone: "+61400245562", email: "qa.notifications+appleid@paydock.com", first_name: "QA", last_name: "QA", }, options: [ { label: "Test 1", detail: "This is a test 1 shipping methods", amount: 10, id: "randomId1", date_components_range: { start_date_components: { years: 0, months: 0, days: 5, hours: 0, }, end_date_components: { years: 0, months: 0, days: 10, hours: 0, } } } ], }, } ``` This is the case where the shipping address is injected by the merchant and is editable by the customer. The customer can edit the shipping address and select the shipping option. The required meta parameters for this case are: - shipping_editing_mode: 'available' - required_shipping_contact_fields: ['postalAddress'] <-- At least one of the fields is required so the shipping address is shown at Apple Pay. ```javascript meta: { apple_pay_capabilities: ['credentials_available', 'credentials_status_unknown', 'credentials_unavailable'], amount_label: 'TOTAL', country: 'AU', currency: 'AUD', amount: 10, shipping_editing_mode: 'available', required_shipping_contact_fields: [ 'postalAddress', // At least one of the fields is required so the shipping address is shown at Apple Pay. 'name', 'phone', 'email', ], shipping: { amount: 5, address_line1: "Address Line 1", address_city: "Test Locality", address_state: "NSW", address_country: "Australia", address_country_code: "AU", address_postcode: "3000", contact: { phone: "+61400245562", email: "qa.notifications+appleid@paydock.com", first_name: "QA", last_name: "QA", }, options: [ { label: "Test 1", detail: "This is a test 1 shipping methods", amount: 10, id: "randomId1", date_components_range: { start_date_components: { years: 0, months: 0, days: 5, hours: 0, }, end_date_components: { years: 0, months: 0, days: 10, hours: 0, } } } ], }, } ``` #### Shipping address editable by the customer This is the case where the shipping address is not injected by the merchant and is editable by the customer. The customer can edit the shipping address and select the shipping option. The required meta parameters for this case are: - shipping_editing_mode: 'available' - required_shipping_contact_fields: ['postalAddress'] <-- At least one of the fields is required so the shipping address is shown at Apple Pay. ```javascript meta: { apple_pay_capabilities: ['credentials_available', 'credentials_status_unknown', 'credentials_unavailable'], amount_label: 'TOTAL', country: 'AU', currency: 'AUD', amount: 10, shipping_editing_mode: 'available', required_shipping_contact_fields: [ 'postalAddress', // At least one of the fields is required so the shipping address is shown at Apple Pay. 'name', 'phone', 'email', ], } ``` When the the customer selects an address, the `onShippingAddressChange` method is called with the shipping address data. If the merchant wants to update the charge with the shipping address, it should update the charge using Paydock's public API Update Charge method with the shipping address data. It can include the shipping options in the update data, that will be used to show the shipping options in the Apple Pay popup. ```javascript button.onShippingAddressChange(async function(data) { console.log("Shipping address has been updated", data); const updateData = { shipping: { ...data.data, amount: defaultOption.amount, method: defaultOption.id, options: shippingOptions, }, amount: amount + defaultOption.amount }; }); ``` #### No shipping address This is the case where no shipping address is required at all in the popup (e.g., digital goods, services, or virtual products, or Shipping Address collected separately by the merchant). The "Send to" UI field will not be shown in the Apple Pay sheet, it will be hidden. **Important:** - No shipping address should be provided in the meta object. - Shipping address could be provided in the initial POST `/v1/charges/wallet` endpoint, if collected previously. The required meta parameters for this case are: - `required_shipping_contact_fields`: Only include contact fields if needed (phone, email), but NOT `postalAddress`. ```javascript meta: { "amount_label": "TOTAL", "country": "AU", "currency": "AUD", "amount": 10, "shipping_editing_mode": "available", "required_shipping_contact_fields": ["phone", "email"], "apple_pay_capabilities": ["credentials_available", "credentials_status_unknown", "credentials_unavailable"] } ``` ### Paypal Wallet Button Express A full description of the meta parameters for [PaypalWalletButtonExpress](#PaypalWalletButtonExpress) meta parameters can be found [here](#PaypalWalletMeta). Below you will find a fully working html example. ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h2>Payment using PayDock PaypalWalletButtonExpress!</h2> <div id="widget"></div> </body> <script src="https://widget.paydock.com/sdk/latest/widget.umd.min.js" ></script> <script> let button = new paydock.PaypalWalletButtonExpress( "#widget", accessTokenOrPublicKey, gatewayId, { amount: 15.5, currency: 'AUD', pay_later: false, standalone: false, capture: true, // style: { // layout: 'horizontal', // or 'vertical' // color: 'gold', // or 'blue', 'silver', 'black', 'white' // shape: 'rect', // or 'pill', 'sharp' // borderRadius: 5, // height: 40, // disableMaxWidth: false, // label: 'paypal', // or 'checkout', 'buynow', 'pay', 'installment' // tagline: true, // messages: { // layout: 'text', // or 'flex' // logo: { // type: 'primary', // or 'alternative', 'inline', 'none' // position: 'left', // or 'right', 'top' // }, // text: { // color: 'black', // or 'white', 'monochrome', 'grayscale' // size: 10, // or 11, 12, 13, 14, 15, 16 // align: 'left', // or 'center', 'right' // }, // color: 'blue', // or 'black', 'white', 'white-no-border', 'gray', 'monochrome', 'grayscale' // ratio: '1x1', // or '1x4', '8x1', '20x1' // }, // } } ); button.setEnv('sandbox'); button.onUnavailable(function() { console.log("Button not available"); }); button.onError(function(error) { console.log("On Error Callback", error); }); button.onPaymentSuccessful(function(data) { console.log("Payment successful"); console.log(data); }); button.onPaymentError(function(err) { console.log("Payment error"); console.log(err); }); button.onPaymentInReview(function(data) { console.log("The payment is on fraud review"); console.log(data); }); button.onClick(async (data) => { console.log("Button clicked", data); const responseData = await fetch('https://your-server-url/initialize-wallet-charge'); const parsedData = await responseData.json(); return parsedData.resource.data.token; }); button.onCheckoutClose(() => { console.log("Checkout closed"); }); button.load(); </script> </html> ```