Guides and reports
Online payments using the new Web Payment APIs
By Deepu K Sasidharan, Developer Advocate, Adyen
ThePayment Request APIand thePayment Handler APIare a set of new W3C web standard being introduced to make payments easier on a website. It is aimed at standardizing and providing a consistent user experience for payments for both end-users and for merchants.
Today we will look at what exactly the APIs are, what are its advantages and how we can use them in a web application.
What is Payment Request API
The Payment Request API provides a set of APIs to capture payment details on a website. It can collect payment credentials, like credit card details, as well as shipping and contact information from the payer through a quick and easy user interface. As of writing, the Payment Request API by default only supports card-based payment methods, like credit, debit, and prepaid cards on the web (Except on Safari which supports only Apple Pay). On mobile, cards and URL-based payment methods like Google Pay, Samsung Pay, Apple Pay, and Alipay are supported as well.
It also providesInterfacesandDictionariesto show and manage the payment request.
This is currently in the W3C candidate stage and is alreadysupportedby evergreen browsers like Chrome, Opera, Safari, and Edge. Firefox supports it in its nightly builds.
Note: This is absolutely not recommended for production use with Adyen as the Payment Request API is quite new and capturing credit card data via this is still not as secure as using theWeb componentsprovided by Adyen which securely encrypts the data. I'm not encrypting card details, which is only possible if you are PCI compliant and your Adyen account has the necessary roles. In the future, once the Payment Handler API becomes widely implemented by browsers, this could change and Adyen might start providing official support for this. We will see more about that later in the post.
Now, let's quickly look at the advantages
Advantages of Payment Request API
The Payment Request API is meant to reduce the number of steps needed to complete an online payment. It has many advantages like
Faster purchases: End users can make purchases faster as they only need to input payment details (for example credit card details) once. They can even select from all their previous payment details. This will work across devices within the same browser and websites that use the Payment Request API.
Consistent user experience: As the payment form is controlled by the browser the user experience will be consistent across websites using the Payment Request API. This means the browser can control the user experience and tailor it to the user, like localizing it according to the user's preferred language configured in the browser.
Accessibility: As the browser controls the input elements of the payment form, it can assure consistent keyboard and screen reader accessibility on every website without developers needing to do anything. A browser could also adjust the font size or color contrast of the payment form, making it more comfortable for the user to make a payment.
Payment method management: Users can manage their payment details, like credit cards and shipping addresses, directly in the browser. A browser can also sync these "credentials" across devices, making it easy for users to jump from desktop to mobile and back again when buying things. This also lets users select from multiple payment methods and addresses they have saved in the browser.
Consistent error handling: The browser can check the validity of card numbers, and can tell the user if a card has expired (or is about to expire). The browser can automatically suggest which card to use based on past usage patterns or restrictions from the merchant (e.g, "we only accept Visa or Mastercard"), or allow the user to say which is their default/favorite card.
Example application
So let's put together a quick sample to see how this works. Of course, I'm going to try to make this work with Adyen as the PSP, because you still need someone to process the payments, the Payment Request API only takes care of capturing payment details.
You can find the complete source code for this examplehere. I'm not going to focus on the backend as I'm using a simplified version of the NodeJS backend fromthis example.
So let's assume we have built a NodeJS express web application following the above-mentioned tutorial. Now we can focus on just the client-side JavaScript part on theadyenImplementation.jsfile.
First, we will call a function when the checkout button is clicked
<a onclick="checkout()">
<p class="button">Checkout</p>
</a>
Let's create this function where all our logic will reside. I'm using an async function so that I can use await on Promises. We first call an API to get details of supported payment methods from Adyen, this will get us details of supported credit cards. We then compare it with cards supported by Payment Request API and build supported payment methods based on the intersection. There are also a couple of helper functions to build the shopping cart summary and to call API endpoints.
async function checkout() {
try {
const adyenPaymentMethods = await callServer("/api/getPaymentMethods");
// create a new payment request
const request = new PaymentRequest(buildSupportedPaymentMethodData(adyenPaymentMethods), buildShoppingCartDetails());
// payment logic goes here
} catch (error) {
console.error(error);
alert(`Error occurred: ${error.message}`);
}
return false;
}
function buildShoppingCartDetails() {
// Hardcoded for demo purposes:
return {
id: "order-123",
displayItems: [
{ label: "Sunglasses", amount: { currency: "EUR", value: "5.00" } },
{ label: "Headphones", amount: { currency: "EUR", value: "5.00" } },
],
total: { label: "Total", amount: { currency: "EUR", value: "10.00" } },
};
}
function buildSupportedPaymentMethodData(adyenPaymentMethods) {
return [
{
supportedMethods: "basic-card",
data: {
supportedNetworks: getSupportedNetworksFromAdyen(adyenPaymentMethods),
supportedTypes: ["credit"],
},
},
];
}
// compare supported cards between Adyen and Payment Request API and get the intersection
function getSupportedNetworksFromAdyen(adyenPaymentMethods) {
const supportedByPaymentAPI = ["amex", "cartebancaire", "diners", "discover", "jcb", "mc", "mir", "unionpay", "visa"];
// filter supported credit cards
const supportedByAdyen = adyenPaymentMethods.paymentMethods.filter((v) => v.type === "scheme")[0].brands;
// get only the intersection between supportedByPaymentAPI and supportedByAdyen
return supportedByPaymentAPI.reduce((acc, curr) => (supportedByAdyen.includes(curr) ? [...acc, fixMasterCard(curr)] : acc), []);
}
// Mastercard id is not same for Adyen and Payment Request API
function fixMasterCard(v) {
return v === "mc" ? "mastercard" : v;
}
// Calls your server endpoints
async function callServer(url, data) {
const res = await fetch(url, {
method: "POST",
body: data ? JSON.stringify(data) : "",
headers: {
"Content-Type": "application/json",
},
});
return await res.json();
}
Now we can invoke the payment capture sheet of the browser withrequest.show()and call the Adyen (PSP) payment API with the credit card data obtained from the payment sheet. We just process the PSP API response and handle success and failure cases.
async function checkout() {
try {
const adyenPaymentMethods = await callServer("/api/getPaymentMethods");
// create a new payment request
const request = new PaymentRequest(buildSupportedPaymentMethodData(adyenPaymentMethods), buildShoppingCartDetails());
// show payment sheet
const payment = await request.show();
// Here we would process the payment.
const response = await callServer("/api/initiatePayment", {
// This works only for PCI compliant credit card payments.
// For non PCI compliant payments the data needs to be encrypted with something like https://github.com/Adyen/adyen-cse-web
// But encrypting data here is not secure as a malicious script may be able to access the data in memory here
paymentMethod: {
type: "scheme",
number: payment.details.cardNumber,
expiryMonth: payment.details.expiryMonth,
expiryYear: payment.details.expiryYear,
holderName: payment.details.cardholderName,
cvc: payment.details.cardSecurityCode,
},
});
// Handle the response code
switch (response.resultCode) {
case "Authorised":
await payment.complete("success");
window.location.href = "/result/success";
break;
case "Pending":
case "Received":
await payment.complete("unknown");
window.location.href = "/result/pending";
break;
case "Refused":
await payment.complete("fail");
window.location.href = "/result/failed";
break;
default:
await payment.complete("fail");
window.location.href = "/result/error";
break;
}
} catch (error) {
// ...
}
return false;
}
When running the code we should see a payment sheet like below
You can try this example by following the below steps. The actual payment will fail if your Adyen account is not PCI compliant, that's ok you will still be able to see how the Payment Request API works.
# Clone this repo
$ git clone https://github.com/deepu105/payment-request-api-example
$ cd payment-request-api-example
# Install dependencies
$ npm install
# create a file named `.env` with values `API_KEY="your_Adyen_API_key"` and `MERCHANT_ACCOUNT="your_Adyen_merchant_account"`
$ vi .env
# start application
$ npm start
That is it, there are more advanced use cases that can be handled using the API and you can find some of themhere.
What is Payment Handler API
ThePayment Handler APIbuilds upon Payment Request API and lets a web application add new payment providers using service workers so that more payment methods are available through the Payment Request API.Hereare a list of demos showing different possibilities.
This is currently in the W3C draft stage and is so far only has support in Chrome.
Advantages of Payment Request API
This is more interesting for payment service providers, like Adyen, as we will be able to provide our own payment methods via the standard Payment Request API. We could even provide our own secure fields this way for example. This could lead the way for different payment methods to provide a consistent experience across sites and adds to the advantages we saw in the previous section
Example application
We can easily build on the above sample. Let's use the demo Payment Handler called BobPay. First, head over to itswebsiteand install the service worker by clicking on "Install BobPay Web Payment App". Now on our return array in methodbuildSupportedPaymentMethodDataadd the following
{
supportedMethods: "https://bobpay.xyz/pay",
}
Now afterrequest.show()in methodcheckoutadd the below handling for the new payment method
const payment = await request.show();
// This payment method handles everything and returns a final result
if (payment.details.bobpay_token_id) {
await payment.complete("success");
window.location.href = "/result/success";
return false;
}
It should appear on the payment sheet like below
And that's it we now have a new payment method available on the Payment Request API
Conclusion
While the Web Payment APIs and not mature enough to replace the client-side components provided by PSPs, I do see a bright future once the APIs are implemented by all major browsers, especially the Payment Handler APIs. When this happens it would be beneficial for merchants, end-users, and PSPs alike as there will be more consistency in user experience for end-users and standardized implementation for merchants and PSPs. At Adyen, we will be closely watching developments in this space to see how we can provide a better user experience using this in the future.
References
- web.dev
Cover image credit: Photo byrupixen.comonUnsplash
Technical careers at Adyen
We are on the lookout for talented engineers and technical people to help us build the infrastructure of global commerce!
Check out developer vacanciesFresh insights, straight to your inbox
By submitting your information you confirm that you have read Adyen's Privacy Policy and agree to the use of your data in all Adyen communications.