This specification defines capabilities that enable Web applications to handle requests for payment.

The Web Payments Working Group maintains a list of all bug reports that the group has not yet addressed. This draft highlights some of the pending issues that are still to be discussed in the working group. No decision has been taken on the outcome of these issues including whether they are valid. Pull requests with proposed specification text for outstanding issues are strongly encouraged.

Introduction

This specification defines a number of new features to allow web applications to handle requests for payments on behalf of users:

This specification does not address how software built with operating-system specific mechanisms (i.e., "native apps") handle payment requests.

Overview of Handling Payment Requests

In this document we envision the following flow:

  1. An origin requests permission from the user to handle payment requests for a set of supported payment methods. For example, a user visiting a retail or bank site may be prompted to register a payment handler from that origin. The origin establishes the scope of the permission but the origin's capabilities may evolve without requiring additional user consent.
  2. Payment handlers are defined in service worker code.
  3. The {{PaymentManager}} is used to set a list of payment instruments. Each payment instrument provides data to the user agent to improve the user experience of selecting payment credentials:
    • The method and optional capabilities inform the user agent decision whether to display this instrument as a candidate for payment.
    • When the instrument matches what the payee accepts, the user agent may display the name and icon. These provide hints about payment credentials that the user agent will return in the PaymentHandlerResponse if the user selects this instrument.
  4. When the merchant (or other payee) calls the [[payment-request]] method canMakePayment() or show() (e.g., when the user pushes a button on a checkout page), the user agent computes a list of candidate payment handlers, comparing the payment methods accepted by the merchant with those supported by registered payment handlers. For payment methods that support additional filtering, either merchant and payment handler capabilities are compared or CanMakePaymentEvent is used as part of determining whether there is a match.
  5. The user agent displays a set of choices to the user: the registered instruments of the candidate payment handlers. The user agent displays these choices using information (labels and icons) provided at registration or otherwise available from the Web app.
  6. When the user (the payer) selects an instrument, the user agent fires a {{PaymentRequestEvent}} (cf. the user interaction task source) in the service worker whose PaymentManager the instrument was registered with. The {{PaymentRequestEvent}} includes some information from the PaymentRequest (defined in [[!payment-request]]) as well as additional information (e.g., origin and selected instrument).
  7. Once activated, the payment handler performs whatever steps are necessary to handle the payment request, and return an appropriate payment response to the payee. If interaction with the user is necessary, the payment handler can open a window for that purpose.
  8. The user agent receives a response asynchronously once the payment handler has finished handling the request. The response becomes the PaymentResponse (of [[!payment-request]]).

An origin may implement a payment app with more than one service worker and therefore multiple payment handlers may be registered per origin. The handler that is invoked is determined by the selection made by the user of a payment instrument. The service worker which stored the payment instrument with its PaymentManager is the one that will be invoked.

Handling a Payment Request

A payment handler is a Web application that can handle a request for payment on behalf of the user.

The logic of a payment handler is driven by the payment methods that it supports. Some payment methods, such as basic-card expect little to no processing by the payment handler which simply returns payment card details in the response. It is then the job of the payee website to process the payment using the returned data as input.

In contrast, some payment methods, such as a crypto-currency payments or bank originated credit transfers, require that the payment handler initiate processing of the payment. In such cases the payment handler will return a payment reference, endpoint URL or some other data that the payee website can use to determine the outcome of the payment (as opposed to processing the payment itself).

Handling a payment request may include numerous interactions: with the user through a new window or other APIs (such as [[[WebCryptoAPI]]]) or with other services and origins through web requests or other means.

This specification does not address these activities that occur between the payment handler accepting the {{PaymentRequestEvent}} and the payment handler returning a response. All of these activities which may be required to configure the payment handler and handle the payment request, are left to the implementation of the payment handler, including:

Thus, an origin will rely on many other Web technologies defined elsewhere for lifecycle management, security, user authentication, user interaction, and so on.

Structure of a Web Payment App

Architecture of a (Web) payment apps as defined in this specification.
A Web payment app is associated with an origin. Payment handlers respond to {{PaymentRequestEvent}}s. {{PaymentManager}} manage the definition, display, and user selection of {{PaymentInstrument}}s. A {{PaymentInstrument}} supports one or more payment methods.

Relation to Other Types of Payment Apps

This specification does not address how third-party mobile payment apps interact (through proprietary mechanisms) with user agents, or how user agents themselves provide simple payment app functionality.

Different types of payment apps. Payment Handler API is for Web apps.
Payment Handler API enables Web apps to handle payments. Other types of payment apps may use other (proprietary) mechanisms.

Registration

One registers a payment handler with the user agent when assigning the first {{PaymentInstrument}} to it through the {{PaymentInstruments/set()}} method.

Extension to the `ServiceWorkerRegistration` interface

        partial interface ServiceWorkerRegistration {
          [SameObject] readonly attribute PaymentManager paymentManager;
        };
        

The paymentManager attribute exposes payment handler functionality in the service worker.

PaymentManager interface

          [SecureContext, Exposed=(Window,Worker)]
          interface PaymentManager {
            [SameObject] readonly attribute PaymentInstruments instruments;
            attribute DOMString userHint;
            Promise<void> enableDelegations(FrozenArray<PaymentDelegation> delegations);
          };
        

The {{PaymentManager}} is used by payment handlers to manage their associated instruments as well as supported payment methods and delegations.

instruments attribute

This attribute allows manipulation of payment instruments associated with a service worker (and therefore its payment handler). To be a candidate payment handler, a handler must have at least one registered payment instrument to present to the user. That instrument needs to match the payment methods and required capabilities specified by the payment request.

userHint attribute

When displaying payment handler name and icon, the user agent may use this string to improve the user experience. For example, a user hint of "**** 1234" can remind the user that a particular card is available through this payment handler. When a agent displays all payment instruments available through a payment handler, it may cause confusion to display the additional hint.

enableDelegations() method

This method allows a payment handler to asynchronously declare its supported PaymentDelegation list.

PaymentDelegation enum

        enum PaymentDelegation {
          "shippingAddress",
          "payerName",
          "payerPhone",
          "payerEmail"
        };
        
"shippingAddress"
The payment handler will provide shipping address whenever needed.
"payerName"
The payment handler will provide payer's name whenever needed.
"payerPhone"
The payment handler will provide payer's phone whenever needed.
"payerEmail"
The payment handler will provide payer's email whenever needed.

PaymentInstruments interface

          [SecureContext, Exposed=(Window,Worker)]
          interface PaymentInstruments {
            Promise<boolean> delete(DOMString instrumentKey);
            Promise<any> get(DOMString instrumentKey);
            Promise<sequence<DOMString>>  keys();
            Promise<boolean> has(DOMString instrumentKey);
            Promise<void> set(DOMString instrumentKey, PaymentInstrument details);
            Promise<void> clear();
          };
        

The {{PaymentInstruments}} interface represents a collection of payment instruments, each uniquely identified by an instrumentKey. The instrumentKey identifier will be passed to the payment handler to indicate the {{PaymentInstrument}} selected by the user, if any.

delete() method

When called, this method executes the following steps:

  1. Let |p:Promise| be [=a new promise=].
  2. Return |p| and perform the remaining steps in parallel:
  3. If the collection contains a {{PaymentInstrument}} with a matching instrumentKey, remove it from the collection and resolve |p| with true.
  4. Otherwise, resolve |p| with false.

get() method

When called, this method executes the following steps:

  1. Let |p:Promise| be [=a new promise=].
  2. Return |p| and perform the remaining steps in parallel:
  3. If the collection contains a {{PaymentInstrument}} with a matching instrumentKey, resolve |p| with that {{PaymentInstrument}}.
  4. Otherwise, resolve |p| with `undefined`.

keys() method

When called, this method executes the following steps:

  1. Let |p:Promise| be [=a new promise=].
  2. Return |p| and perform the remaining steps in parallel:
  3. Resolve |p| with a Sequence that contains all the instrumentKeys for the {{PaymentInstrument}}s contained in the collection, in original insertion order.

has() method

When called, this method executes the following steps:

  1. Let |p:Promise| be [=a new promise=].
  2. Return |p| and perform the remaining steps in parallel:
  3. If the collection contains a {{PaymentInstrument}} with a matching instrumentKey, resolve |p| with true.
  4. Otherwise, resolve |p| with false.

set() method

When called, this method executes the following steps:

  1. Let registration be the {{PaymentInstrument}}'s associated service worker registration.
  2. If registration has no active worker, then reject a {{Promise}} with an {{"InvalidStateError"}} {{DOMException}} and terminate these steps.
  3. Upon user agent discretion and depending on user consent, optionally return a {{Promise}} rejected with a {{NotAllowedError}}.
  4. If the icons member of details is present, then:
    1. Let convertedIcons be the result of running the convert image objects algorithm passing details.icons as the argument.
    2. If the convertedIcons is an empty Sequence, then return a {{Promise}} rejected with a {{TypeError}}.
    3. Set details.icons to convertedIcons.
  5. Let |p:Promise| be [=a new promise=].
  6. Return |p| and perform the remaining steps in parallel:
  7. If the icons member of details is present, then for each icon in details.icons:
    1. If the user agent wants to display the icon, then:
      1. Let fetchedImage be the result of steps to fetch an image resource passing icon as the argument.
      2. Set icon.[[\fetchedImage]] to fetchedImage.
  8. If the collection contains a {{PaymentInstrument}} with a matching instrumentKey, replace it with the {{PaymentInstrument}} in details.
  9. Otherwise, insert the {{PaymentInstrument}} in details as a new member of the collection and associate it with the key instrumentKey.
  10. Resolve |p|.

clear() method

When called, this method executes the following steps:

  1. Let |p:Promise| be [=a new promise=].
  2. Return |p| and perform the remaining steps in parallel:
  3. Remove all {{PaymentInstrument}}s from the collection and resolve |p|.

PaymentInstrument dictionary

      dictionary PaymentInstrument {
        required DOMString name;
        sequence<ImageObject> icons;
        DOMString method;
        object capabilities;
      };
      
name member
The name member is a string that represents the label for this {{PaymentInstrument}} as it is usually displayed to the user.
icons member
The icons member is an array of image objects that can serve as iconic representations of the payment instrument when presented to the user for selection.
method member
The method member is the payment method identifier of the payment method supported by this instrument.
capabilities member
The capabilities member is a list of payment-method-specific capabilities that this payment handler is capable of supporting for this instrument. For example, for the basic-card payment method, this object will consist of an object with two fields: one for supportedNetworks, and another for supportedTypes.

ImageObject dictionary

      dictionary ImageObject {
          required USVString src;
          DOMString sizes;
          DOMString type;
      };
      
src member
The src member is used to specify the ImageObject's source. It is a URL from which the user agent can fetch the image’s data.
sizes member
The sizes member is used to specify the ImageObject's sizes. It follows the spec of sizes member in HTML link element, which is a string consisting of an unordered set of unique space-separated tokens which are [=ASCII case-insensitive=] that represents the dimensions of an image. Each keyword is either an [=ASCII case-insensitive=] match for the string "any", or a value that consists of two valid non-negative integers that do not have a leading U+0030 DIGIT ZERO (0) character and that are separated by a single U+0078 LATIN SMALL LETTER X or U+0058 LATIN CAPITAL LETTER X character. The keywords represent icon sizes in raw pixels (as opposed to CSS pixels). When multiple image objects are available, a user agent MAY use the value to decide which icon is most suitable for a display context (and ignore any that are inappropriate). The parsing steps for the sizes member MUST follow the parsing steps for HTML link element sizes attribute.
type member
The type member is used to specify the ImageObject's MIME type. It is a hint as to the media type of the image. The purpose of this member is to allow a user agent to ignore images of media types it does not support.

Convert image objects

When this algorithm with inputImages parameter is invoked, the user agent must run the following steps:

  1. Let outputImages be an empty Sequence of ImageObject.
  2. For each image in inputImages:
    1. If image.type is not a valid MIME type string or the value of type is not a supported media format, then return an empty Sequence of ImageObject.
    2. If image.sizes is not a valid value, then return an empty Sequence of ImageObject.
    3. Let url be the result of parsing image.src with the context object's relevant settings object's [=environment settings object/api base url=].
    4. If url is failure, then return an empty Sequence of ImageObject.
    5. If url's [=url/scheme=] is not "https", then return an empty Sequence of ImageObject.
    6. Set image.src to url.
    7. Append image to outputImages
  3. Return outputImages.

According to the step 2.3, it is also possible to use the relative url for image.src. The following examples illustrate how relative URL resolution works in different execution contexts.

        <-- In this example, code is located in https://www.example.com/bobpay/index.html -->
        <script>

        const instrumentKey = "c8126178-3bba-4d09-8f00-0771bcfd3b11";
        navigator.serviceWorker.register("/register/sw.js");
        const registration = await navigator.serviceWorker.ready;
        await registration.paymentManager.paymentInstruments.set({
          instrumentKey,
          {
            name: "My Bob Pay Account: john@example.com",
            method: "https://bobpay.com",
            icons: [{
              src: "icon/lowres.webp",
              sizes: "48x48",
              type: "image/webp"
            }]
          });

        const storedInstrument =
          await registration.paymentManager.paymentInstruments.get(instrumentKey);

        // storedInstrument.icons[0].src == "https://www.example.com/bobpay/icon/lowres.webp";

        </script>
      

        // In this example, code is located in https://www.example.com/register/sw.js

        const instrumentKey = "c8126178-3bba-4d09-8f00-0771bcfd3b11";
        await self.registration.paymentManager.paymentInstruments.set({
          instrumentKey,
          {
            name: "My Bob Pay Account: john@example.com",
            method: "https://bobpay.com",
            icons: [{
              src: "../bobpay/icon/lowres.webp",
              sizes: "48x48",
              type: "image/webp"
            }]
          });

        const storedInstrument =
          await registration.paymentManager.paymentInstruments.get(instrumentKey);

        // storedInstrument.icons[0].src == "https://www.example.com/bobpay/icon/lowres.webp";
      

Registration Example

The following example shows how to register a payment handler:

        button.addEventListener("click", async() => {
          if (!window.PaymentManager) {
            return; // not supported, so bail out.
          }

          navigator.serviceWorker.register("/sw.js");
          const registration = await navigator.serviceWorker.ready;
          await registration.paymentManager.enableDelegations(
            ['shippingAddress', 'payerName']);

          // Excellent, we got it! Let's now set up the user's cards.
          await addInstruments(registration);
        }, { once: true });

        function addInstruments(registration) {
          return Promise.all([
            registration.paymentManager.instruments.set(
              "dc2de27a-ca5e-4fbd-883e-b6ded6c69d4f",
              {
                name: "Visa ending ****4756",
                method: "basic-card",
                capabilities: {
                  supportedNetworks: ["visa"],
                  supportedTypes: ["credit"]
                }
              }),

            registration.paymentManager.instruments.set(
              "c8126178-3bba-4d09-8f00-0771bcfd3b11",
              {
                name: "My Bob Pay Account: john@example.com",
                method: "https://bobpay.com"
              }),

            registration.paymentManager.instruments.set(
              "new-card",
              {
                name: "Add new credit/debit card to ExampleApp",
                method: "basic-card",
                capabilities: {
                  supportedNetworks:
                    ["visa", "mastercard", "amex", "discover"],
                  supportedTypes: ["credit", "debit", "prepaid"]
                }
              }),
            ]);
          };
     

Can make payment

If the payment handler supports CanMakePaymentEvent, the user agent may use it to help with filtering of the available payment handlers.

Implementations may impose a timeout for developers to respond to the CanMakePaymentEvent. If the timeout expires, then the implementation will behave as if {{CanMakePaymentEvent/respondWith()}} was called with `false`.

Extension to `ServiceWorkerGlobalScope`

        partial interface ServiceWorkerGlobalScope {
          attribute EventHandler oncanmakepayment;
        };
        

oncanmakepayment attribute

The {{ServiceWorkerGlobalScope/oncanmakepayment}} attribute is an event handler whose corresponding event handler event type is "canmakepayment".

The CanMakePaymentEvent

The CanMakePaymentEvent is used to check whether the payment handler is able to respond to a payment request.

          [Exposed=ServiceWorker]
          interface CanMakePaymentEvent : ExtendableEvent {
            constructor(DOMString type, optional CanMakePaymentEventInit eventInitDict = {});
            readonly attribute USVString topOrigin;
            readonly attribute USVString paymentRequestOrigin;
            readonly attribute FrozenArray<PaymentMethodData> methodData;
            void respondWith(Promise<boolean> canMakePaymentResponse);
          };
        

The topOrigin, paymentRequestOrigin, methodData, and modifiers members share their definitions with those defined for {{PaymentRequestEvent}}.

respondWith() method

This method is used by the payment handler to indicate whether it can respond to a payment request.

CanMakePaymentEventInit dictionary

            dictionary CanMakePaymentEventInit : ExtendableEventInit {
              USVString topOrigin;
              USVString paymentRequestOrigin;
              sequence<PaymentMethodData> methodData;
            };
          

The topOrigin, paymentRequestOrigin, and methodData members share their definitions with those defined for {{PaymentRequestEvent}}.

Handling a CanMakePaymentEvent

Upon receiving a PaymentRequest, the user agent MUST run the following steps:

  1. If user agent settings prohibit usage of CanMakePaymentEvent (e.g., in private browsing mode), terminate these steps.
  2. Let registration be a {{ServiceWorkerRegistration}}.
  3. If registration is not found, terminate these steps.
  4. Fire Functional Event "canmakepayment" using CanMakePaymentEvent on registration with the following properties:

    topOrigin
    the [=serialization of an origin=] of the top level payee web page.
    paymentRequestOrigin
    the [=serialization of an origin=] of the context where PaymentRequest was initialized.
    methodData
    The result of executing the MethodData Population Algorithm.
    modifiers
    The result of executing the Modifiers Population Algorithm.

Example of handling the CanMakePaymentEvent

This example shows how to write a service worker that listens to the CanMakePaymentEvent. When a CanMakePaymentEvent is received, the service worker always returns true.

          self.addEventListener("canmakepayment", function(e) {
            e.respondWith(true);
          });
        

Filtering of Payment Instruments

Given a PaymentMethodData and a {{PaymentInstrument}} that match on payment method identifier, this algorithm returns true if this instrument can be used for payment:

  1. Let instrument be the given {{PaymentInstrument}}.
  2. Let methodName be the payment method identifier string specified in the PaymentMethodData.
  3. Let methodData be the payment method specific data of PaymentMethodData.
  4. Let paymentHandlerOrigin be the origin of the {{ServiceWorkerRegistration}} scope URL of the payment handler with this instrument.
  5. Let paymentMethodManifest be the ingested and parsed payment method manifest for the methodName.
  6. If methodName is a standardized payment method identifier or is a URL-based payment method identifier with the "*" string supported origins in paymentMethodManifest, filter based on capabilities:
    1. For each key in methodData:
      1. If the intersection of methodData[key] and instrument.capabilities[key] is empty, return `false`.
    2. Otherwise, return true.
  7. Otherwise, if the URL-based payment method identifier methodName has the same origin as paymentHandlerOrigin, fire the CanMakePaymentEvent in the payment handler and return the result.
  8. Otherwise, if supported origins in paymentMethodManifest is an ordered set of [=url/origin=] that contains the paymentHandlerOrigin, fire the CanMakePaymentEvent in the payment handler and return the result.
  9. Otherwise, return `false`.

How to specify capabilities

Example of how a payment handler should provide the list of all its active cards to the browser.

          await navigator.serviceWorker.register("/pw/app.js");
          const registration = await navigator.serviceWorker.ready;
          registration.paymentManager.userHint = "(Visa ****1111)";
          await registration.paymentManager.instruments.set(
            "12345",
            {
              name: "Visa ****1111",
              icons: [{
                src: "/pay/visa.png",
                sizes: "32x32",
                type: "image/png",
              }],
              method: "basic-card",
              capabilities: {
                supportedNetworks: ["visa"],
                supportedTypes: ["credit"],
              },
            });
          

In this case, new PaymentRequest([{supportedMethods: "basic-card"}], shoppingCart).canMakePayment() should return true because there's an active card in the payment handler. Note that new PaymentRequest([{supportedMethods: "basic-card", data: {supportedTypes: ["debit"]}}], shoppingCart).canMakePayment() would return `false` because of mismatch in supportedTypes in this example.

Invocation

Once the user has selected an Instrument, the user agent fires a {{PaymentRequestEvent}} and uses the subsequent PaymentHandlerResponse to create a PaymentReponse for [[!payment-request]].

Payment Request API supports delegation of responsibility to manage an abort to a payment app. There is a proposal to add a paymentRequestAborted event to the Payment Handler interface. The event will have a respondWith method that takes a boolean parameter indicating if the paymentRequest has been successfully aborted.

Extension to ServiceWorkerGlobalScope

This specification extends the ServiceWorkerGlobalScope interface.

        partial interface ServiceWorkerGlobalScope {
          attribute EventHandler onpaymentrequest;
        };
        

onpaymentrequest attribute

The onpaymentrequest attribute is an event handler whose corresponding event handler event type is {{PaymentRequestEvent}}.

The PaymentRequestDetailsUpdate

The PaymentRequestDetailsUpdate contains the updated total (optionally with modifiers and shipping options) and possible errors resulting from user selection of a payment method, a shipping address, or a shipping option within a payment handler.

        dictionary PaymentRequestDetailsUpdate {
          DOMString error;
          PaymentCurrencyAmount total;
          FrozenArray<PaymentDetailsModifier> modifiers;
          FrozenArray<PaymentShippingOption> shippingOptions;
          object paymentMethodErrors;
          AddressErrors shippingAddressErrors;
        };
        

error member

A human readable string that explains why the user selected payment method, shipping address or shipping option cannot be used.

total member

Updated total based on the changed payment method, shipping address, or shipping option. The total can change, for example, because the billing address of the payment method selected by the user changes the Value Added Tax (VAT); Or because the shipping option/address selected/provided by the user changes the shipping cost.

modifiers member

Updated modifiers based on the changed payment method, shipping address, or shipping option. For example, if the overall total has increased by €1.00 based on the billing or shipping address, then the totals specified in each of the modifiers should also increase by €1.00.

shippingOptions member

Updated shippingOptions based on the changed shipping address. For example, it is possible that express shipping is more expensive or unavailable for the user provided country.

paymentMethodErrors member

Validation errors for the payment method, if any.

shippingAddressErrors member

Validation errors for the shipping address, if any.

The PaymentRequestEvent

The PaymentRequestEvent represents the data and methods available to a Payment Handler after selection by the user. The user agent communicates a subset of data available from the PaymentRequest to the Payment Handler.

        [Exposed=ServiceWorker]
        interface PaymentRequestEvent : ExtendableEvent {
          constructor(DOMString type, optional PaymentRequestEventInit eventInitDict = {});
          readonly attribute USVString topOrigin;
          readonly attribute USVString paymentRequestOrigin;
          readonly attribute DOMString paymentRequestId;
          readonly attribute FrozenArray<PaymentMethodData> methodData;
          readonly attribute object total;
          readonly attribute FrozenArray<PaymentDetailsModifier> modifiers;
          readonly attribute DOMString instrumentKey;
          readonly attribute boolean requestBillingAddress;
          readonly attribute object? paymentOptions;
          readonly attribute FrozenArray<PaymentShippingOption>? shippingOptions;
          Promise<WindowClient?> openWindow(USVString url);
          Promise<PaymentRequestDetailsUpdate?> changePaymentMethod(DOMString methodName, optional object? methodDetails = null);
          Promise<PaymentRequestDetailsUpdate?> changeShippingAddress(optional AddressInit shippingAddress = {});
          Promise<PaymentRequestDetailsUpdate?> changeShippingOption(DOMString shippingOption);
          void respondWith(Promise<PaymentHandlerResponse> handlerResponsePromise);
        };
        

topOrigin attribute

Returns a string that indicates the origin of the top level payee web page. This attribute is initialized by Handling a PaymentRequestEvent.

paymentRequestOrigin attribute

Returns a string that indicates the origin where a PaymentRequest was initialized. When a PaymentRequest is initialized in the topOrigin, the attributes have the same value, otherwise the attributes have different values. For example, when a PaymentRequest is initialized within an iframe from an origin other than topOrigin, the value of this attribute is the origin of the iframe. This attribute is initialized by Handling a PaymentRequestEvent.

paymentRequestId attribute

When getting, the paymentRequestId attribute returns the [[\details]].id from the PaymentRequest that corresponds to this {{PaymentRequestEvent}}.

methodData attribute

This attribute contains PaymentMethodData dictionaries containing the payment method identifiers for the payment methods that the web site accepts and any associated payment method specific data. It is populated from the PaymentRequest using the MethodData Population Algorithm defined below.

total attribute

This attribute indicates the total amount being requested for payment. It is of type PaymentCurrencyAmount dictionary as defined in [[payment-request]], and initialized with a copy of the total field of the PaymentDetailsInit provided when the corresponding PaymentRequest object was instantiated.

modifiers attribute

This sequence of PaymentDetailsModifier dictionaries contains modifiers for particular payment method identifiers (e.g., if the payment amount or currency type varies based on a per-payment-method basis). It is populated from the PaymentRequest using the Modifiers Population Algorithm defined below.

instrumentKey attribute

This attribute indicates the {{PaymentInstrument}} selected by the user. It corresponds to the instrumentKey provided to the PaymentManager.instruments interface during registration. An empty string means that the user did not choose a specific {{PaymentInstrument}}.

requestBillingAddress attribute

The value of PaymentOptions.requestBillingAddress in the PaymentRequest.

paymentOptions attribute

The value of PaymentOptions in the PaymentRequest. Available only when shippingAddress and/or any subset of payer's contact information are requested.

shippingOptions attribute

The value of ShippingOptions in the PaymentDetailsInit dictionary of the corresponding PaymentRequest.(PaymentDetailsInit inherits ShippingOptions from PaymentDetailsBase). Available only when shipping address is requested.

openWindow() method

This method is used by the payment handler to show a window to the user. When called, it runs the open window algorithm.

changePaymentMethod() method

This method is used by the payment handler to get updated total given such payment method details as the billing address. When called, it runs the change payment method algorithm.

changeShippingAddress() method

This method is used by the payment handler to get updated payment details given the shippingAddress. When called, it runs the change payment details algorithm.

changeShippingOption() method

This method is used by the payment handler to get updated payment details given the shippingOption identifier. When called, it runs the change payment details algorithm.

respondWith() method

This method is used by the payment handler to provide a PaymentHandlerResponse when the payment successfully completes. When called, it runs the Respond to PaymentRequest Algorithm with |event| and handlerResponsePromise as arguments.

Should payment apps receive user data stored in the user agent upon explicit consent from the user? The payment app could request permission either at installation or when the payment app is first invoked.

PaymentRequestEventInit dictionary

            dictionary PaymentRequestEventInit : ExtendableEventInit {
              USVString topOrigin;
              USVString paymentRequestOrigin;
              DOMString paymentRequestId;
              sequence<PaymentMethodData> methodData;
              PaymentCurrencyAmount total;
              sequence<PaymentDetailsModifier> modifiers;
              DOMString instrumentKey;
              PaymentOptions paymentOptions;
              sequence<PaymentShippingOption> shippingOptions;
            };
          

The topOrigin, paymentRequestOrigin, paymentRequestId, methodData, total, modifiers, instrumentKey, paymentOptions, and shippingOptions members share their definitions with those defined for {{PaymentRequestEvent}}

MethodData Population Algorithm

To initialize the value of the methodData, the user agent MUST perform the following steps or their equivalent:

  1. Set registeredMethods to an empty set.
  2. For each {{PaymentInstrument}} instrument in the payment handler's PaymentManager.instruments, add the value of instrument.method to registeredMethods.
  3. Create a new empty Sequence.
  4. Set dataList to the newly created Sequence.
  5. For each item in PaymentRequest@[[\methodData]] in the corresponding payment request, perform the following steps:
    1. Set inData to the item under consideration.
    2. Set commonMethods to the set intersection of inData.supportedMethods and registeredMethods.
    3. If commonMethods is empty, skip the remaining substeps and move on to the next item (if any).
    4. Create a new PaymentMethodData object.
    5. Set outData to the newly created PaymentMethodData.
    6. Set outData.supportedMethods to a list containing the members of commonMethods.
    7. Set outData.data to a copy of inData.data.
    8. Append outData to dataList.
  6. Set methodData to dataList.

Modifiers Population Algorithm

To initialize the value of the modifiers, the user agent MUST perform the following steps or their equivalent:

  1. Set registeredMethods to an empty set.
  2. For each {{PaymentInstrument}} instrument in the payment handler's PaymentManager.instruments, add the value of instrument.method to registeredMethods.
  3. Create a new empty Sequence.
  4. Set modifierList to the newly created Sequence.
  5. For each item in PaymentRequest@[[\paymentDetails]].modifiers in the corresponding payment request, perform the following steps:
    1. Set inModifier to the item under consideration.
    2. Set commonMethods to the set intersection of inModifier.supportedMethods and registeredMethods.
    3. If commonMethods is empty, skip the remaining substeps and move on to the next item (if any).
    4. Create a new PaymentDetailsModifier object.
    5. Set outModifier to the newly created PaymentDetailsModifier.
    6. Set outModifier.supportedMethods to a list containing the members of commonMethods.
    7. Set outModifier.total to a copy of inModifier.total.
    8. Append outModifier to modifierList.
  6. Set modifiers to modifierList.

Internal Slots

Instances of {{PaymentRequestEvent}} are created with the internal slots in the following table:

Internal Slot Default Value Description (non-normative)
[[\windowClient]] null The currently active WindowClient. This is set if a payment handler is currently showing a window to the user. Otherwise, it is null.
[[\fetchedImage]] undefined This value is a result of steps to fetch an image resource or a fallback image provided by the user agent.
[[\respondWithCalled]] false YAHO

Handling a PaymentRequestEvent

Upon receiving a PaymentRequest by way of PaymentRequest.show() and subsequent user selection of a payment instrument, the user agent MUST run the following steps:

  1. Let registration be the {{ServiceWorkerRegistration}} corresponding to the {{PaymentInstrument}} selected by the user.
  2. If registration is not found, reject the {{Promise}} that was created by PaymentRequest.show() with an {{"InvalidStateError"}} {{DOMException}} and terminate these steps.
  3. Fire Functional Event "paymentrequest" using {{PaymentRequestEvent}} on registration with the following properties:

    {{PaymentRequestEvent/topOrigin}}
    the [=serialization of an origin=] of the top level payee web page.
    paymentRequestOrigin
    the [=serialization of an origin=] of the context where PaymentRequest was initialized.
    {{PaymentRequestEvent/methodData}}
    The result of executing the MethodData Population Algorithm.
    modifiers
    The result of executing the Modifiers Population Algorithm.
    {{PaymentRequestEvent/total}}
    A copy of the total field on the PaymentDetailsInit from the corresponding PaymentRequest.
    paymentRequestId
    The [[\details]].id from the PaymentRequest.
    instrumentKey
    The instrumentKey of the selected {{PaymentInstrument}}, or the empty string if none was selected.
    paymentOptions
    A copy of the paymentOptions dictionary passed to the constructor of the corresponding PaymentRequest.
    shippingOptions
    A copy of the shippingOptions field on the PaymentDetailsInit from the corresponding PaymentRequest.

    Then run the following steps in parallel, with dispatchedEvent:

    1. Wait for all of the promises in the extend lifetime promises of dispatchedEvent to resolve.
    2. If the payment handler has not provided a PaymentHandlerResponse, reject the {{Promise}} that was created by PaymentRequest.show() with an {{"OperationError"}} {{DOMException}}.

Windows

An invoked payment handler may or may not need to display information about itself or request user input. Some examples of potential payment handler display include:

A payment handler that requires visual display and user interaction, may call openWindow() to display a page to the user.

Since user agents know that this method is connected to the {{PaymentRequestEvent}}, they SHOULD render the window in a way that is consistent with the flow and not confusing to the user. The resulting window client is bound to the tab/window that initiated the PaymentRequest. A single payment handler SHOULD NOT be allowed to open more than one client window using this method.

Open Window Algorithm

This algorithm resembles the Open Window Algorithm in the Service Workers specification.

Should we refer to the Service Workers specification instead of copying their steps?

  1. Let |event| be this {{PaymentRequestEvent}}.
  2. If |event|'s {{Event/isTrusted}} attribute is false, return a {{Promise}} rejected with a {{"InvalidStateError"}} {{DOMException}}.
  3. Let request be the PaymentRequest that triggered this {{PaymentRequestEvent}}.
  4. Let url be the result of parsing the url argument.
  5. If the url parsing throws an exception, return a {{Promise}} rejected with that exception.
  6. If url is about:blank, return a {{Promise}} rejected with a {{TypeError}}.
  7. If url's origin is not the same as the service worker's origin associated with the payment handler, return a {{Promise}} resolved with null.
  8. If this algorithm is not triggered by user activation, return a {{Promise}} rejected with a "{{NotAllowedError}}" {{DOMException}}.
  9. Let promise be a new {{Promise}}.
  10. Return promise and perform the remaining steps in parallel:
  11. If |event|.[[\windowClient]] is not null, then:
    1. If |event|.[[\windowClient]].visibilityState is not "unloaded", reject promise with an {{"InvalidStateError"}} {{DOMException}} and abort these steps.
  12. Let newContext be a new top-level browsing context.
  13. Navigate newContext to url, with exceptions enabled and replacement enabled.
  14. If the navigation throws an exception, reject promise with that exception and abort these steps.
  15. If the origin of newContext is not the same as the service worker client origin associated with the payment handler, then:
    1. Resolve promise with null.
    2. Abort these steps.
  16. Let client be the result of running the create window client algorithm with newContext as the argument.
  17. Set |event|.[[\windowClient]] to client.
  18. Resolve promise with client.

Example of handling the {{PaymentRequestEvent}}

This example shows how to write a service worker that listens to the {{PaymentRequestEvent}}. When a {{PaymentRequestEvent}} is received, the service worker opens a window to interact with the user.

      async function getPaymentResponseFromWindow() {
        return new Promise((resolve, reject) => {
          self.addEventListener("message", listener = e => {
            self.removeEventListener("message", listener);
            if (!e.data || !e.data.methodName) {
              reject();
              return;
            }
            resolve(e.data);
          });
        });
      }

      self.addEventListener("paymentrequest", e => {
        e.respondWith((async() => {
          // Open a new window for providing payment UI to user.
          const windowClient = await e.openWindow("payment_ui.html");

          // Send data to the opened window.
          windowClient.postMessage({
            total: e.total,
            modifiers: e.modifiers
          });

          // Wait for a payment response from the opened window.
          return await getPaymentResponseFromWindow();
        })());
      });
      

Using the simple scheme described above, a trivial HTML page that is loaded into the payment handler window to implement the basic card scheme might look like the following:

<form id="form">
<table>
  <tr><th>Cardholder Name:</th><td><input name="cardholderName"></td></tr>
  <tr><th>Card Number:</th><td><input name="cardNumber"></td></tr>
  <tr><th>Expiration Month:</th><td><input name="expiryMonth"></td></tr>
  <tr><th>Expiration Year:</th><td><input name="expiryYear"></td></tr>
  <tr><th>Security Code:</th><td><input name="cardSecurityCode"></td></tr>
  <tr><th></th><td><input type="submit" value="Pay"></td></tr>
</table>
</form>

<script>
navigator.serviceWorker.addEventListener("message", e => {
  /* Note: message sent from payment app is available in e.data */
});

document.getElementById("form").addEventListener("submit", e => {
  /* See https://w3c.github.io/payment-method-basic-card/#basiccardresponse-dictionary */
  const basicCardResponse = {};
  ["cardholderName", "cardNumber", "expiryMonth", "expiryYear", "cardSecurityCode"]
  .forEach(field => {
    basicCardResponse[field] = form.elements[field].value;
  });

  /* See https://w3c.github.io/payment-handler/#paymenthandlerresponse-dictionary */
  const paymentAppResponse = {
    methodName: "basic-card",
    details: basicCardResponse
  };

  navigator.serviceWorker.controller.postMessage(paymentAppResponse);
  window.close();
});
</script>
      

Response

PaymentHandlerResponse dictionary

The PaymentHandlerResponse is conveyed using the following dictionary:
          dictionary PaymentHandlerResponse {
          DOMString methodName;
          object details;
          DOMString? payerName;
          DOMString? payerEmail;
          DOMString? payerPhone;
          AddressInit shippingAddress;
          DOMString? shippingOption;
          };
        

methodName attribute

The payment method identifier for the payment method that the user selected to fulfil the transaction.

details attribute

A JSON-serializable object that provides a payment method specific message used by the merchant to process the transaction and determine successful fund transfer.

The user agent receives a successful response from the payment handler through resolution of the Promise provided to the {{PaymentRequestEvent/respondWith}} function of the corresponding {{PaymentRequestEvent}} interface. The application is expected to resolve the Promise with a PaymentHandlerResponse instance containing the payment response. In case of user cancellation or error, the application may signal failure by rejecting the Promise.

If the Promise is rejected, the user agent MUST run the payment app failure algorithm. The exact details of this algorithm are left to implementers. Acceptable behaviors include, but are not limited to:

  • Letting the user try again, with the same payment handler or with a different one.
  • Rejecting the Promise that was created by PaymentRequest.show().

payerName attribute

The user provided payer's name.

payerEmail attribute

The user provided payer's email.

payerPhone attribute

The user provided payer's phone number.

shippingAddress attribute

The user provided shipping address.

shippingOption attribute

The identifier of the user selected shipping option.

Change Payment Method Algorithm

When this algorithm is invoked with methodName and methodDetails parameters, the user agent MUST run the following steps:

  1. Run the payment method changed algorithm with PaymentMethodChangeEvent |event| constructed using the given methodName and methodDetails parameters.
  2. If |event|.updateWith(detailsPromise) is not run, return null.
  3. If |event|.updateWith(detailsPromise) throws, rethrow the error.
  4. If |event|.updateWith(detailsPromise) times out (optional), throw {{"InvalidStateError"}} {{DOMException}}.
  5. Construct and return a PaymentRequestDetailsUpdate from the detailsPromise in |event|.updateWith(detailsPromise).

Change Payment Details Algorithm

When this algorithm is invoked with shippingAddress or shippingOption the user agent MUST run the following steps:

  1. Run the PaymentRequest updated algorithm with PaymentRequestUpdateEvent |event| constructed using the updated details (shippingAddress or shippingOption).
  2. If |event|.updateWith(detailsPromise) is not run, return null.
  3. If |event|.updateWith(detailsPromise) throws, rethrow the error.
  4. If |event|.updateWith(detailsPromise) times out (optional), throw {{"InvalidStateError"}} {{DOMException}}.
  5. Construct and return a PaymentRequestDetailsUpdate from the detailsPromise in |event|.updateWith(detailsPromise).

Respond to PaymentRequest Algorithm

When this algorithm is invoked with |event| and handlerResponsePromise parameters, the user agent MUST run the following steps:

  1. If |event|'s {{Event/isTrusted}} is false, then throw an "InvalidStateError" {{DOMException}} and abort these steps.
  2. If |event|'s [=Event/dispatch flag=] is unset, then throw an {{"InvalidStateError"}} {{DOMException}} and abort these steps.
  3. If |event|.[[\respondWithCalled]] is true, throw an {{"InvalidStateError"}} {{DOMException}} and abort these steps.
  4. Set |event|.[[\respondWithCalled]] to true.
  5. Set the |event|'s [=Event/stop propagation flag=] and event's [=Event/stop immediate propagation flag=].
  6. Add handlerResponsePromise to the |event|'s extend lifetime promises
  7. Increment the |event|'s pending promises count by one.
  8. Upon rejection of handlerResponsePromise:
    1. Run the payment app failure algorithm and terminate these steps.
  9. Upon fulfillment of handlerResponsePromise:
    1. Let handlerResponse be |value| converted to an IDL value {{PaymentHandlerResponse}}. If this throws an exception, run the payment app failure algorithm and terminate these steps.
    2. Validate that all required members exist in handlerResponse and are well formed.
      1. If handlerResponse.methodName is not present or not set to one of the values from |event|.methodData, run the payment app failure algorithm and terminate these steps.
      2. If handlerResponse.details is not present or not JSON-serializable, run the payment app failure algorithm and terminate these steps.
      3. Let shippingRequired be the requestShipping value of the associated PaymentRequest's paymentOptions. If shippingRequired and handlerResponse.shippingAddress is not present, run the payment app failure algorithm and terminate these steps.
      4. If shippingRequired and handlerResponse.shippingOption is not present or not set to one of shipping options identifiers from |event|.shippingOptions, run the payment app failure algorithm and terminate these steps.
      5. Let payerNameRequired be the requestPayerName value of the associated PaymentRequest's paymentOptions. If payerNameRequired and handlerResponse.payerName is not present, run the payment app failure algorithm and terminate these steps.
      6. Let payerEmailRequired be the requestPayerEmail value of the associated PaymentRequest's paymentOptions. If payerEmailRequired and handlerResponse.payerEmail is not present, run the payment app failure algorithm and terminate these steps.
      7. Let payerPhoneRequired be the requestPayerPhone value of the associated PaymentRequest's paymentOptions. If payerPhoneRequired and handlerResponse.payerPhone is not present, run the payment app failure algorithm and terminate these steps.
    3. Serialize required members of handlerResponse ( methodName and details are always required; shippingAddress and shippingOption are required when shippingRequired is true; payerName, payerEmail, and payerPhone are required when payerNameRequired, payerEmailRequired, and payerPhoneRequired are true, respectively.):
        For each memberin handlerResponseLet serializeMemberbe the result of StructuredSerializewith handlerResponse.member. Rethrow any exceptions.
    4. The user agent MUST run the user accepts the payment request algorithm as defined in [[!payment-request]], replacing steps 9-15 with these steps or their equivalent.
      1. Deserialize serialized members:
          For each serializeMemberlet memberbe the result of StructuredDeserializewith serializeMember. Rethrow any exceptions.
      2. If any exception occurs in the above step, then run the payment app failure algorithm and terminate these steps.
      3. Assign methodName to associated PaymentRequest's response.methodName.
      4. Assign details to associated PaymentReqeust's response.details.
      5. If shippingRequired, then set the shippingAddress attribute of associated PaymentReqeust's response to shippingAddress. Otherwise, set it to null.
      6. If shippingRequired, then set the shippingOption attribute of associated PaymentReqeust's response to shippingOption. Otherwise, set it to null.
      7. If payerNameRequired, then set the payerName attribute of associated PaymentReqeust's response to payerName. Otherwise, set it to null.
      8. If payerEmailRequired, then set the payerEmail attribute of associated PaymentReqeust's response to payerEmail. Otherwise, set it to null.
      9. If payerPhoneRequired, then set the payerPhone attribute of associated PaymentReqeust's response to payerPhone. Otherwise, set it to null.
  10. Upon fulfillment or upon rejection of handlerResponsePromise, queue a microtask to perform the following steps:
    1. Decrement the |event|'s pending promises count by one.
    2. Let registration be the context object's relevant global object's associated service worker's containing service worker registration.
    3. If registration’s uninstalling flag is set, invoke Try Clear Registration with registration.
    4. If registration is not null, invoke Try Activate with registration.

The following example shows how to respond to a payment request:

      paymentRequestEvent.respondWith(new Promise(function(accept,reject) {
        /* ... processing may occur here ... */
        accept({
          methodName: "basic-card",
          details: {
            cardHolderName:   "John Smith",
            cardNumber:       "1232343451234",
            expiryMonth:      "12",
            expiryYear :      "2020",
            cardSecurityCode: "123"
           },
          shippingAddress: {
            addressLine: [
              "1875 Explorer St #1000",
            ],
            city: "Reston",
            country: "US",
            dependentLocality: "",
            organization: "",
            phone: "+15555555555",
            postalCode: "20190",
            recipient: "John Smith",
            region: "VA",
            sortingCode: ""
          },
          shippingOption: "express",
          payerEmail: "john.smith@gmail.com",
        });
      }));
          

[[!payment-request]] defines an ID that parties in the ecosystem (including payment app providers and payees) can use for reconciliation after network or other failures.

Security and Privacy Considerations

Information about the User Environment

User Consent to Install a Payment Handler

User Consent before Payment

User Awareness about Sharing Data Cross-Origin

Secure Communications

Authorized Payment Apps

Supported Origin

Data Validation

Private Browsing Mode

Payment Handler Display Considerations

When ordering payment handlers and payment instruments, the user agent is expected to honor user preferences over other preferences. User agents are expected to permit manual configuration options, such as setting a preferred payment handler or instrument display order for an origin, or for all origins.

User experience details are left to implementers.

Dependencies

This specification relies on several other underlying specifications.

Payment Request API
The terms payment method, PaymentRequest, PaymentResponse, supportedMethods, PaymentCurrencyAmount, paymentDetailsModifier, paymentDetailsInit, paymentDetailsBase, PaymentMethodData, PaymentOptions, PaymentShippingOption, AddressInit, AddressErrors, PaymentMethodChangeEvent, PaymentRequestUpdateEvent, ID, canMakePayment(), show(), updateWith(detailsPromise), user accepts the payment request algorithm, payment method changed algorithm, PaymentRequest updated algorithm, and JSON-serializable are defined by the Payment Request API specification [[!payment-request]].
ECMAScript
The terms internal slot and JSON.stringify are defined by [[!ECMASCRIPT]].
Payment Method Manifest
The terms payment method manifest, ingest payment method manifest, parsed payment method manifest, and supported origins are defined by the Payment Method Manifest specification [[!payment-method-manifest]].
Basic Card Payment
The terms basic-card, supportedNetworks, and supportedTypes are defined in [[!payment-method-basic-card]].
Service Workers
The terms service worker, service worker registration, active worker, service worker client, ServiceWorkerRegistration, ServiceWorkerGlobalScope, fire functional event, extend lifetime promises,pending promises count, containing service worker registration, uninstalling flag, Try Clear Registration, Try Activate, ExtendableEvent, ExtendableEventInit, and scope URL are defined in [[!SERVICE-WORKERS]].

There is only one class of product that can claim conformance to this specification: a user agent.

User agents MAY implement algorithms given in this specification in any way desired, so long as the end result is indistinguishable from the result that would be obtained by the specification's algorithms.

User agents MAY impose implementation-specific limits on otherwise unconstrained inputs, e.g., to prevent denial of service attacks, to guard against running out of memory, or to work around platform-specific limitations. When an input exceeds implementation-specific limit, the user agent MUST throw, or, in the context of a promise, reject with, a {{TypeError}} optionally informing the developer of how a particular input exceeded an implementation-specific limit.