The Payment Request API is a new JavaScript API that provides a cross-browser standard to collect the payment, address, and contact information from a customer that can be used to process an order. It also facilitates the exchange of this information between the browser and the website. The whole idea behind this is to improve the user online shopping experience by making it easy for the user to store payment and contact information in the browser.

Browser Support

The Payment Request API is still in active development and only supported by the last few versions of modern browsers. Before we start making a payment request, we should feature detect to ensure that the API is supported by the browser:

if(window.PaymentRequest) {
    // Yes, we can use the API
} else {
    // No, fallback to checkout page
    window.location.href = '/checkout';
}

Note: You can only use the Payment Request API on sites that are served over https.

PaymentRequest Object

A payment request is always started by creating a new object of PaymentRequest - using the PaymentRequest() constructor. The constructor takes two mandatory parameters and one optional parameter:

  • paymentMethods defines which forms of payment are accepted. For example, you may only accept Visa and MasterCard credit cards.
  • paymentDetails contains the total payment amount due, taxes, shipping cost, display items, etc.
  • options is an optional argument that can be used to request additional details from the user, such as name, email, phone, etc.

Let's create a new payment request with only required parameters:

const paymentMethods = [
    {
        supportedMethods: ['basic-card']
    }
];

const paymentDetails = {
    total: {
        label: 'Total Amount',
        amount: {
            currency: 'USD',
            value: 8.49
        }
    }
};

const paymentRequest = new PaymentRequest(
    paymentMethods,
    paymentDetails
);

Notice the supportedMethods parameter in paymentMethods object. When it is set to basic-card, both debit and credit cards of all networks will be accepted. However, we can limit the supported networks and types of the card. For example, with the following only Visa, MasterCard and Discover credit cards are accepted:

const paymentMethods = [
    {
        supportedMethods: ['basic-card'],
        data: {
            supportedNetworks: ['visa', 'mastercard', 'discover'],
            supportedTypes: ['credit']
        }
    }
];
// ...

Payment Details

The second mandatory parameter that is passed to PaymentRequest constructor is the payment details object. It contains the total of the order and an optional array of display items. The total parameter must contain a label parameter and an amount parameter with currency and value.

You can also add additional display items to provide a high level breakdown of the total:

const paymentDetails = {
    total: {
        label: 'Total Amount',
        amount: {
            currency: 'USD',
            value: 8.49
        }
    },
    displayItems: [
        {
            label: '15% Discount',
            amount: {
                currency: 'USD',
                value: -1.49
            }
        },
        {
            label: 'Tax',
            amount: {
                currency: 'USD',
                value: 0.79
            }
        }
    ]
};

The displayItems parameter is not meant to display a long list of items. Since space is limited for the browser's payment UI on mobile devices, you should use this to display only top-level fields such as subtotal, discount, tax, and shipping cost, etc.

The PaymentRequest API does not perform any calculations. So it is your web application that is responsible for providing the pre-calculated total amount.

Requesting Additional Details

The third optional parameter can be used to request additional information from the user such as name, email address, and phone number:

// ...
const options = {
    requestPayerName: true,
    requestPayerPhone: true,
    requestPayerEmail: true,
};

const paymentRequest = new PaymentRequest(
    paymentMethods,
    paymentDetails,
    options
);

By default, all of these values are false, but adding any of them to options object with a value true will result in an extra step in the payment UI. If the user has already stored these details in the browser, they will be pre-populated.

Display Payment UI

After creating a PaymentRequest object, you must call the show() method to display the payment request UI to the user. The show() method returns a promise that resolves with a PaymentResponse object if the user has successfully filled in the details. If there is an error or the user closes the UI, the promise rejects.

// ...
const paymentRequest = new PaymentRequest(
    paymentMethods,
    paymentDetails,
    options
);

paymentRequest
    .show()
    .then(paymentResponse => {
        // close the payment UI
        paymentResponse.complete()
            .then(() => {
                // TODO: call REST API to process the payment at backend server
                // with the data from `paymentResponse`.
            });
    })
    .catch(err => {
        // user closed the UI or the API threw an error
        console.log('Error:', err);
    });

With the above code, the browser will show the payment UI to the user. Once the user has filled in the details and clicked on the 'Pay' button, you will receive a PaymentResponse object in the show() promise. The payment request UI is closed immediately when you call the PaymentResponse.complete() method. This method returns a new promise so that you can call the backend server with the information collected and process the payment.

Payment Request UI

If you wanted to call the backend server to process the payment while the payment UI is showing a spinner, you can delay the call to complete(). Let us create a mock function for payment processing with the backend server. It takes paymentResponse as a parameter and returns a promise after 1.5 seconds that resolves to a JSON object:

const processPaymentWithServer = (paymentResponse) => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve({ status: true });
        }, 1500);
    });
};
//...
paymentRequest
    .show()
    .then(paymentResponse => {
        processPaymentWithServer(paymentResponse)
            .then(data => {
                if (data.status) {
                    paymentResponse.complete('success');
                } else {
                    paymentResponse.complete('fail');
                }
            });
    })
    .catch(err => {
        console.log('Error:', err);
    });

In the example above, the browser payment UI will show a processing screen until the promise returned by processPaymentWithServer() method is settled. We also used 'success' and 'fail' strings to tell the browser about the outcome of the transaction. The browser will show an error message to the user if you call complete('fail').

Payment Request Abort

If you want to cancel the payment request due to no activity or any other reason, you can use the PaymentRequest.abort() method. It closes the payment request UI immediately and rejects the show() promise.

// ...
setTimeout(() => {
    paymentRequest.abort()
        .then(() => {
            // aborted payment request
            console.log('Payment request aborted due to no activity.');
        })
        .catch(err => {
            // error while aborting
            console.log('abort() Error: ', err);
        });
}, 5000);

Conclusion

That's the end of a quick introduction to JavaScript Payment Request API. It provides a browser-based method to collect customer payment and contact information that can be sent to the backend server to process the payment. The aim is to reduce the number of steps involved in completing an online payment. It makes the whole checkout process smoother by remembering the user's preferred way of paying for goods and services.

If you want to learn more about the Payment Request API, here is a good resource that discusses the main concepts and usage of the API.

✌️ Like this article? Follow @attacomsian on Twitter. You can also follow me on LinkedIn and DEV. Buy me a coffee (cost $3)


Need help to start a new Spring Boot or MEAN stack project? I am available for contract work. Hire me to accomplish your business goals with engineering and design. Let’s talk about your project: hi@attacomsian.com.