Digital Wallets for Web

How to implement Digital Wallets on the web through Merchant Warrior.

Apple Pay Prerequisites

Before implementing Apple Pay on the web, several prerequisites must be met.

To register a domain for use with Apple Pay, you must do

  1. Host the domain verification file on your server. You can download the sandbox verification file here. For production, please download it here instead.
  2. Host the file at /.well-known/apple-developer-merchantid-domain-association on your domain(s) that will display the Apple Pay button. Apple will perform the verification automatically. For example, if your domain is https://example.com.au, host the file at https://example.com.au/.well-known/apple-developer-merchantid-domain-association. Make sure the file is publicly accessible or exclusively accessible by Apple Servers listed in Whitelist Apple IP Addresses for Domain Verification.
Google Pay Prerequisites

Before implementing Google Pay™ on the web, several prerequisites must be met.

Merchant Warrior Web SDK

The easiest way to integrate Apple Pay and other wallets is to use the Merchant Warrior Web SDK.

  1. Include the Merchant Warrior Javascript in the head of your HTML document
<script src="https://securetest.merchantwarrior.com/sdk/merchantwarrior.min.js"></script>
  1. Configure your options
const merchantUUID = '12345abcde';
const apiKey = 'abc123';
const options = {
    environment: 'development', // Default value is 'production'
    middleware: '/.well-known/MerchantWarrior.php',
    xcsrfToken: '<Session Verification Data>',
}
  1. Configure your middleware. For security reasons, the JavaScript SDK does not make any calls directly to the Merchant Warrior servers. Rather, these requests are passed through a middleware option. Please see our guide on how to set up your middleware.

  2. Create the Merchant Warrior object. This can be done in one of two ways -

4A. The static MerchantWarrior.create() method will return a promise containing the object pre-populated with your merchant information.

MerchantWarrior.create(merchantUUID, apiKey, options).then((mwarrior) => {
    // Interact with the mwarrior object here
});

4B. The static MerchantWarrior.createWithToken() method can alternatively be used to to create the promise using an accessToken instead of your merchant credentials.

MerchantWarrior.createWithToken(accessToken, options).then((mwarrior) => {
    // Interact with the mwarrior object here
});
  1. Create a payment summary. If the customer details are currently unknown, they can be omitted and supplied later in the process. This summary also allows you to choose which wallets you would like to be made available to compatible devices. The options are googlepay, applepay, and basic-card. basic-card uses whatever interface the browser makes available, and uses card data (such as PAN) directly.

The applePay property is for specific Apple Pay options. It takes an object that has a single string property, buttonText. This property controls what text is displayed on the Apple Pay button. Available options include buy, donate, plain, set-up, book, check-out, subscribe, add-money, contribute, order, reload, rent, support, tip, or top-up.

The googlePay property is for specific Google Pay options. It takes an object with 4 properties; buttonText, buttonLocale, buttonHeight and buttonWidth.

  • The buttonText property has the following available options: book, buy, checkout, donate, order, pay, plain, subscribe.
  • buttonLocale accepts any locale, eg. en or fr
  • buttonHeight and buttonWidth accept dimensions in pixels
const customer = {
    name: 'Jane Doe',
    country: 'AU',
    state: 'QLD',
    city: 'Brisbane',
    address: '345 Ann Street',
    postCode: '4000',
    phone: '61412345678'
}

const transaction = {
      transactionReferenceID: "ref1234",
      storeID: "store123",
      custom1: "one",
      custom2: "two",
      custom3: "three",
      currency: "AUD"
}

const summary = {
    total: {
        amount: '25.00',
        label: 'A Product',
        pending: false
    },
    transaction: transaction, // optional
    customer: customer,
    style: 'dark', // Optional - default dark
    applePay: {
      buttonText: 'buy',

      requiredBillingContactFields: ['email', 'name', 'phone', 'postalAddress'], //The fields of billing information the user must provide to process the transaction.
      infoUpdate: true,
      infoUpdatePreference: 'shippingAddress',
      requiredShippingContactFields: ['email', 'name', 'phone', 'postalAddress'], //The fields of shipping information the user must provide to fulfill the order.
      shippingMethods: [ //The list of shipping methods available for a payment request.
              { label: "shipping method 1", detail: "Arrive in 7 days", amount: "5.00", identifier: "shipping_method1"},
              { label: "shipping method 2", detail: "Arrive in 14 days", amount: "2.00", identifier: "shipping_method2"}
      ],
      shippingType: 'shipping', //An optional value that indicates how to ship purchased items.
      supportedCountries: ['AU'], //it doesn't work in DEV, it might work in Production or some region?
      onshippingcontactselected: function(event) {
          console.log("onshippingcontactselected");
          console.log(event.shippingContact);
          // refer https://developer.apple.com/documentation/apple_pay_on_the_web/applepayshippingcontactupdate
          // "Please update the total amount according to the logic associated with the selected shipping address. eg -
          let ApplePayShippingContactUpdate  =  { 
              newTotal: { 
                  label: "New Label1", 
                  amount: '25.00' 
              }
          } // Merchant Warrior would use new total amount

          return ApplePayShippingContactUpdate;
      },
      onshippingmethodselected: function(event) {
          console.log("onshippingmethodselected");
          console.log(event.shippingMethod);
          // refer https://developer.apple.com/documentation/apple_pay_on_the_web/applepayshippingcontactupdate
          // Please update the total amount according to the logic associated with the selected shipping method. eg -
          let ApplePayShippingContactUpdate = {
              newTotal: {
                  label: "New Label2", 
                  amount: '30.00'
              }
          } // Merchant Warrior would use new total amount

          return ApplePayShippingContactUpdate;
      }
    },
    googlePay: {
      buttonText: 'buy',
      buttonLocale: 'en',
      buttonHeight: '45px',
      buttonWidth: '200px',
      allowedAuthMethods: ["PAN_ONLY", "CRYPTOGRAM_3DS"],
      threeDS: true,
      billingAddressRequired: true, //Set to true if you require a billing address. A billing address should only be requested if it's required to process the transaction.
      //Additional data requests can increase friction in the checkout process and lead to a lower conversion rate.
      infoUpdate: true,
      infoUpdatePreference: 'shippingAddress',
      billingAddressParameters: {
          phoneNumberRequired: true, //Set to true if a phone number is required to process the transaction.
          format: 'MIN' //Billing address format required to complete the transaction.
          //MIN: Name, country code, and postal code (default).
          //FULL: Name, street address, locality, region, country code, and postal code.
      },
      emailRequired: true, //Set to true to request an email address.
      shippingAddressRequired: true, //Set to true to request a full shipping address.
      shippingAddressParameters: { //If shippingAddressRequired is set to true, specify shipping address restrictions.
          allowedCountryCodes: [], //ISO 3166-1 alpha-2 country code values of the countries where shipping is allowed. If this object isn't specified, all shipping address countries are allowed.
          phoneNumberRequired: true, //Set to true if a phone number is required for the provided shipping address.
      },
      onPaymentDataChanged: function(onPaymentDataChanged) {
          //refer https://developers.google.com/pay/api/web/reference/request-objects#PaymentDataRequestUpdate
          //Please update the total amount according to the logic associated with the selected shipping address, eg
          let updatedPaymentDataRequest = {
              newTransactionInfo: {
                  currencyCode: 'AUD',
                  totalPrice: '51.00',
                  totalPriceStatus: 'FINAL',
                  totalPriceLabel: 'New Label',
              },
          }

          return updatedPaymentDataRequest;
      }
    },
    wallets: [
        'googlepay',
        'applepay',
    ],
}
  1. Create and mount a payment request
    mwarrior.paymentRequest(summary).then((element) => {
        element.mount('payment-buttons'); // This will attach the buttons to the HTML element with an id of 'payment-buttons'
    });
  1. Handle events.

The two primary events

  • payment-complete, which will trigger when the payment process has completed. It has 3 arguments, status which is a boolean value indicating whether the payment was successful, response which contains a JSON object detailing the response from the Merchant Warrior servers, and errors which contains any Javascript error details. Whether a payment is successful or not, the button that triggered it will still be reusable and should be manually removed when finished with.

  • request-customer, which will trigger near the start of the payment flow if a customer is not included in the initial payment summary. It has two parameters - resolve, and reject. These are connected to an internal promise. Calling the resolve parameter with a valid customer will allow the payment request to proceed, and calling the reject parameter will end the payment and trigger the payment-complete event.

    mwarrior.on('payment-complete', (status, response, errors) => {
        if (status) {
            document.getElementById('payment-buttons').innerHTML = '';
            // Payment successful!
        } else {
            // Payment failed
        }
    });

    mwarrior.on('request-customer', (resolve, reject) => {
        resolve(customer);
    });
  1. Put it all together. An example of this code in action can be found here
const merchantUUID = '12345abcde';
const apiKey = 'abc123';
const options = {
    environment: 'development', // Default value is 'production'
}

const transaction = {
      transactionReferenceID: "ref1234",
      storeID: "store123",
      custom1: "one",
      custom2: "two",
      custom3: "three",
      currency: "AUD"
}

const summary = {
    total: {
        amount: '25.00',
        label: 'A Product',
        pending: false
    },
    transaction: transaction, // optional
    customer: customer,
    style: 'dark', // Optional - default dark
    applePay: {
      buttonText: 'buy'
    },
    googlePay: {
      buttonText: 'buy',
      buttonLocale: 'en',
      buttonHeight: '45px',
      buttonWidth: '100px'
    },
    wallets: [
        'googlepay',
        'applepay',
    ],
}

MerchantWarrior.create(merchantUUID, apiKey, options).then((mwarrior) => {
    mwarrior.paymentRequest(summary).then((element) => {
        element.mount('payment-buttons'); // This will attach the buttons to the HTML element with an id of 'payment-buttons'
    });

    mwarrior.on('payment-complete', (status, response, errors) => {
        if (status) {
            document.getElementById('payment-buttons').innerHTML = '';
            // Payment successful!
        } else {
            // Payment failed
        }
    });

    mwarrior.on('request-customer', (resolve, reject) => {
        const customer = {
            name: 'Jane Doe',
            country: 'AU',
            state: 'QLD',
            city: 'Brisbane',
            address: '345 Ann Street',
            postCode: '4000',
            phone: '61412345678'
        }
        resolve(customer);
});
Manual Integration

To integrate manually, follow the instructions provided by Apple and Google, and include the final token in a Digital Wallet processCard request.