Register for this year’s #ChromeDevSummit happening on Nov. 11-12 in San Francisco to learn about the latest features and tools coming to the Web. Request an invite on the Chrome Dev Summit 2019 website

Web based payment apps developer guide

The Payment Request API brought to the
web a native browser-based interface that allows users to enter required
purchase information easier than ever before. The API can also invoke payment
apps that provide various kinds of payment
methods such as
e-money, cryptocurrency, bank transfers, and more.

This document describes how you can implement a web-based payment app using the
Payment Handler API.

Overview

A payment app built with the Payment Handler API includes multiple endpoints
that return specific content to a request. This chart shows example URL mappings
of endpoints used in this document.

path

contents

/

The root path of a web-based payment app that registers a service worker and a
payment handler.

/manifest.json

A web app manifest that defines the web-based payment app.

/payment-manifest.json

A payment method manifest that defines how a payment method acts.

/service-worker.js

JavaScript code that handles a payment request.

/pay

A Payment Method Identifier URL that returns an HTTP header pointing to the
payment method manifest.

/checkout

The actual checkout page exposed to users.

Note: Although code examples in this document include URLs such as
https://bobpay.xyz, they do not necessarily match what is actually implemented
at https://bobpay.xyz. The demo BobPay app's source code can be found in this
GitHub
repository.

Important properties:

default_applications: An array of absolute, fully-qualified URLs that
points to web app manifests where the payment apps are hosted. This array is
expected to reference the development manifest, production manifest, etc.

supported_origins: An array of URLs that points to origins that may host
third-party payment apps implementing the same payment method. Note that a
payment method can be supported by multiple payment apps. Use * to indicate
that any origin can host the third-party payment apps.

Important properties:

icons: Used as the payment app icon. Only Chrome uses these icons; other
browsers may use them as fallback icons if you don't specify them as part of
the payment instrument.

The web app manifest's name property is used as the
payment app name

Name the file manifest.json and place it on the web server, typically in the
root folder, such as https://www.example.com/manifest.json. Point to the file
from the HTML page that identifies it as part of the payment app.

<link rel="manifest" href="/manifest.json">

Chrome downloads this manifest at the time of payment app installation, so the
page that installs the payment app (see next section) should contain this
<link> element.

Install a payment app and set a payment instrument

Include the following JavaScript as part of the page that installs a service
worker.

Register a service worker

// Check if service worker is available
if ('serviceWorker' in navigator) {
// Register a service worker
const registration = await navigator.serviceWorker.register(
// A service worker JS file is separate
'service-worker.js'
);

Set a payment instrument

Service worker registration returns a promise. Continue registering a payment
instrument using the payment manager.

// When service worker registration is done,
// you'll receive a service worker registration object
// (https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerRegistration).
// Check if Payment Handler is available
if (!registration.paymentManager) return;
// `userHint` will be an exposed next to the name of the
// payment app. The user hint should be user specific.
// For example, this can be user email address.
// If the user is on a public computer, be sure to clear the user
// specific data after user logs out.
registration.paymentManager.userHint = 'payment-handler user hint';

Set a string that identifies the payment instrument as the first argument. This
can be any arbitrary string, and is used to identify instruments when you want
to update them. Therefore, we recommended that you use an identifier from your
payment app backend.

Set an object containing the payment instrument details
(PaymentInstrument)
as the second argument.

name: Set a string as the instrument name. This is ignored in Chrome,
which uses the web app manifest’s name property, but other browsers may use
it.

icons: Set an array of
ImageObjects
the Payment Request sheet will display. This is ignored in Chrome, which uses
the web app manifest's icons property, but other browsers may use it.

method: A supported payment method identifier.

capabilities: Set the payment method specific parameters as an object.
As of August 2018, basic-card is the only payment method that accepts
capabilities. See the following example.

Optional: Let Chrome install your payment app just-in-time

Chrome includes a feature to find and install a payment app just-in-time (JIT)
when no supported payment apps have yet been installed for a payment request.
Note that this is not a standardized feature, so other browsers may not support
it.

To enable JIT installation for a payment app, it must be listed in
default_applications in a corresponding payment method manifest, and the
payment app's manifest must have a serviceworker section to indicate where to
get the service-worker.js and its registration scope (as well as its name and
icon).

JIT-installable payment apps are presented to the user when the payment request
dialog is shown. After the user chooses an installable payment app and clicks
"Pay", the payment app is installed and then a
PaymentRequestEvent
is sent to it. Note that
CanMakePaymentEvent
will not be sent to a JIT-installable payment app because the event happens
before the payment request dialog is shown.

After the installation, the JIT-installable payment app is set as the supported
payment method. The payment app can update it later as a previously-installed
payment app.

Write a service worker

After the service worker is installed and the payment instrument is set, you are
ready to accept payment requests. Here's an example service-worker.js.

Receive a paymentrequest event and open a window

When a user selects your payment method and presses "Pay", the service worker
receives a paymentrequest event. To receive it, add an event listener.

// `self` is global object in service worker
self.addEventListener('paymentrequest', e => {
// Preserve the event for future use
payment_request_event = e;
// You'll need a polyfill for `PromiseResolver`
// As it's not implemented in Chrome yet.
resolver = new PromiseResolver();
// Pass a promise that resolves when payment is done.
e.respondWith(resolver.promise);
// Open the checkout page.
e.openWindow(checkoutURL).then(client => {
if (client === null) {
// Make sure to reject the promise on failure
resolver.reject('Failed to open window');
}
}).catch(err => {
// Make sure to reject the promise on failure
resolver.reject(err);
});
});

For security reasons, the web pages loaded in the opened window must have valid
https certificates and no mixed contents; otherwise the payment request will be
aborted by Chrome.

Exchange information between service worker and frontend

The service worker needs to exchange messages with the frontend. To do this, add an event listener.

self.addEventListener('message', e => {

The listener opens a window in order to get authorization from the payer to
proceed with payment. To display total price, etc., obtain a message from the
opened window that it is ready, then post the payment info back to it. In this
case, we set payment_app_window_ready to signal that condition.

// Determine a message that tells the service worker that
// the window is ready. In this case `payment_app_window_ready`.
if (e.data === "payment_app_window_ready") {
// If `payment_request_event` is not set, this isn't a message
// preceded by `paymentrequest` event.
if (!payment_request_event) return;

Now send the payment details back. In this case we're only sending the total
of the payment request, but you can pass more details if you like.

Returning payment information

Let's define a message containing https://bobpay.xyz/pay in the methodName
as a signal to proceed. Resolve the original paymentrequest event, preserved
with the payment details.

// This app sets `methodName` to be a sign to proceed.
if (e.data.methodName === methodName) {
// Resolve with the information passed from frontend.
// This information will be passed back to the Payment Request
// a merchant has invoked as a resolution of .show() method.
resolver.resolve(e.data);
} else {
resolver.reject(e.data);
}
});

Write frontend code

The checkout frontend will be the actual interface that users will interact
with. Here's a quick sample.

let client;
navigator.serviceWorker.addEventListener('message', e => {
client = e.source;
});
navigator.serviceWorker.controller.postMessage('payment_app_window_ready');
onPay() {
if (!client) return;
const response = {
methodName: 'https://payment-handler-example.firebaseapp.com/pay',
details: { id: '123456' }
};
client.postMessage(response);
// Chrome will close all windows in the scope of the service worker
// after the service worker responds to the 'paymentrequest' event.
}
onCancel() {
if (!client) return;
client.postMessage('The payment request is cancelled by user');
// Chrome will close all windows in the scope of the service worker
// after the service worker responds to the 'paymentrequest' event.
}

Again, let's examine this code.

Listen to service worker messages and let it know the window is ready

Let the service worker know that it is ready to receive information by sending a
message. Don't forget to listen for its message as well.

// Listen to messages from service worker
navigator.serviceWorker.addEventListener('message', e => {
// Preserve service worker object for later use
client = e.source;
// You can do whatever you want by handling `e.data` as well.
});
// Send `payment_app_window_ready` as a sign that the window is ready.
navigator.serviceWorker.controller.postMessage('payment_app_window_ready');

Invoke the Pay operation

You'll probably want to authenticate the user and let them authorize payment,
although that process is outside the scope of this document. Once the user
indicates they want to make the payment, send a message to the service worker
with the information required for merchant to proceed.

Summary

The Payment Handler API is an emerging web standard that enables a website to
act as a payment app by integration into a website that uses the Payment Request
API. By implementing these related specifications, you can produce a clean,
browser-based UI that makes user purchases as smooth and seamless as a native
app.

Feedback

Was this page helpful?

Yes

What was the best thing about this page?

It helped me complete my goal(s)

Thank you for the feedback. If you have specific ideas on how to improve this page, please
create an issue.

It had the information I needed

Thank you for the feedback. If you have specific ideas on how to improve this page, please
create an issue.

It had accurate information

Thank you for the feedback. If you have specific ideas on how to improve this page, please
create an issue.

It was easy to read

Thank you for the feedback. If you have specific ideas on how to improve this page, please
create an issue.

Something else

Thank you for the feedback. If you have specific ideas on how to improve this page, please
create an issue.

No

What was the worst thing about this page?

It didn't help me complete my goal(s)

Thank you for the feedback. If you have specific ideas on how to improve this page, please
create an issue.

It was missing information I needed

Thank you for the feedback. If you have specific ideas on how to improve this page, please
create an issue.

It had inaccurate information

Thank you for the feedback. If you have specific ideas on how to improve this page, please
create an issue.

It was hard to read

Thank you for the feedback. If you have specific ideas on how to improve this page, please
create an issue.

Something else

Thank you for the feedback. If you have specific ideas on how to improve this page, please
create an issue.