Payment Request API

This specification standardizes an API to allow merchants (i.e. web sites selling physical or digital goods) to utilize one or more payment methods with minimal integration. User agents (e.g., browsers) facilitate the payment flow between merchant and user.

The working group maintains a list of all bug reports that the group has not yet addressed. Pull requests with proposed specification text for outstanding issues are strongly encouraged.

The working group will demonstrate implementation experience by producing an implementation report. The report will show two or more independent implementations passing each mandatory test in the test suite (i.e., each test corresponds to a MUST requirement of the specification).

There has been no change in dependencies on other workings groups during the development of this specification.

Changes since last publication

Substantive changes to the Payment Request API since the 9 July 2018 version are as follows. The complete list of changes, including all editorial changes, is viewable in the commit history. Key set of changes are viewable in the Changelog.

Introduction

This specification describes an API that allows user agents (e.g., browsers) to act as an intermediary between three parties in a transaction:

The details of how to fulfill a payment request for a given payment method is an implementation detail of a payment handler. Concretely, each payment handler defines:

Steps to check if a payment can be made:
How a payment handler determines whether it, or the user, can potentially "make a payment" is also an implementation detail of a payment handler. For an example, see the can make payment algorithm of [[[?payment-method-basic-card]]].
Steps to respond to a payment request:
Steps that return an object or dictionary that a merchant uses to process or validate the transaction. The structure of this object is specific to each payment method. For an example of such an object, see the {{BasicCardResponse}} dictionary of [[[?payment-method-basic-card]]].
Steps for when a user changes payment method (optional)

Steps that describe how to handle the user changing payment method or monetary instrument (e.g., from a debit card to a credit card) that results in a dictionary or {{object}} or null.

This API also enables web sites to take advantage of more secure payment schemes (e.g., tokenization and system-level authentication) that are not possible with standard JavaScript libraries. This has the potential to reduce liability for the merchant and helps protect sensitive user information.

Goals and scope

The following are out of scope for this specification:

Examples of usage

In order to use the API, the developer needs to provide and keep track of a number of key pieces of information. These bits of information are passed to the {{PaymentRequest}} constructor as arguments, and subsequently used to update the payment request being displayed to the user. Namely, these bits of information are:

Once a {{PaymentRequest}} is constructed, it's presented to the end user via the {{PaymentRequest/show()}} method. The {{PaymentRequest/show()}} returns a promise that, once the user confirms request for payment, results in a {{PaymentResponse}}.

Declaring multiple ways of paying

When constructing a new {{PaymentRequest}}, a merchant uses the first argument (|methodData|) to list the different ways a user can pay for things (e.g., credit cards, Apple Pay, Google Pay, etc.). More specifically, the |methodData| sequence contains PaymentMethodData dictionaries containing the payment method identifiers for the payment methods that the merchant accepts and any associated payment method specific data (e.g., which credit card networks are supported).

          const methodData = [
            {
              supportedMethods: "basic-card",
              data: {
                supportedNetworks: ["visa", "mastercard"],
              },
            },
            {
              supportedMethods: "https://example.com/bobpay",
              data: {
                merchantIdentifier: "XXXX",
                bobPaySpecificField: true,
              },
            },
          ];
        

Describing what is being paid for

When constructing a new {{PaymentRequest}}, a merchant uses the second argument of the constructor (|details|) to provide the details of the transaction that the user is being asked to complete. This includes the total of the order and, optionally, some line items that can provide a detailed breakdown of what is being paid for.

          const details = {
            id: "super-store-order-123-12312",
            displayItems: [
              {
                label: "Sub-total",
                amount: { currency: "USD", value: "55.00" },
              },
              {
                label: "Sales Tax",
                amount: { currency: "USD", value: "5.00" },
                type: "tax"
              },
            ],
            total: {
              label: "Total due",
              // The total is USD$65.00 here because we need to
              // add shipping (below). The selected shipping
              // costs USD$5.00.
              amount: { currency: "USD", value: "65.00" },
            },
          };
        

Adding shipping options

Here we see an example of how to add two shipping options to the |details|.

          const shippingOptions = [
            {
              id: "standard",
              label: "🚛 Ground Shipping (2 days)",
              amount: { currency: "USD", value: "5.00" },
              selected: true,
            },
            {
              id: "drone",
              label: "🚀 Drone Express (2 hours)",
              amount: { currency: "USD", value: "25.00" }
            },
          ];
          Object.assign(details, { shippingOptions });
        

Conditional modifications to payment request

Here we see how to add a processing fee for using a card on a particular network. Notice that it requires recalculating the total.

          // Certain cards incur a $3.00 processing fee.
          const cardFee = {
            label: "Card processing fee",
            amount: { currency: "USD", value: "3.00" },
          };

          // Modifiers apply when the user chooses to pay with
          // a card.
          const modifiers = [
            {
              additionalDisplayItems: [cardFee],
              supportedMethods: "basic-card",
              total: {
                label: "Total due",
                amount: { currency: "USD", value: "68.00" },
              },
              data: {
                supportedNetworks: networks,
              },
            },
          ];
          Object.assign(details, { modifiers });
        

Requesting specific information from the end user

Some financial transactions require a user to provide specific information in order for a merchant to fulfill a purchase (e.g., the user's shipping address, in case a physical good needs to be shipped). To request this information, a merchant can pass a third optional argument (|options|) to the {{PaymentRequest}} constructor indicating what information they require. When the payment request is shown, the user agent will request this information from the end user and return it to the merchant when the user accepts the payment request.

          const options = {
            requestPayerEmail: false,
            requestPayerName: true,
            requestPayerPhone: false,
            requestShipping: true,
          }
        

Constructing a PaymentRequest

Having gathered all the prerequisite bits of information, we can now construct a {{PaymentRequest}} and request that the browser present it to the user:

          async function doPaymentRequest() {
            try {
              const request = new PaymentRequest(methodData, details, options);
              // See below for a detailed example of handling these events
              request.onshippingaddresschange = ev => ev.updateWith(details);
              request.onshippingoptionchange = ev => ev.updateWith(details);
              const response = await request.show();
              await validateResponse(response);
            } catch (err) {
              // AbortError, SecurityError
              console.error(err);
            }
          }
          async function validateResponse(response) {
            try {
              const errors = await checkAllValuesAreGood(response);
              if (errors.length) {
                await response.retry(errors);
                return validateResponse(response);
              }
              await response.complete("success");
            } catch (err) {
              // Something went wrong...
              await response.complete("fail");
            }
          }
          // Must be called as a result of a click
          // or some explicit user action.
          doPaymentRequest();
        

Handling events and updating the payment request

Prior to the user accepting to make payment, the site is given an opportunity to update the payment request in response to user input. This can include, for example, providing additional shipping options (or modifying their cost), removing items that cannot ship to a particular address, etc.

          const request = new PaymentRequest(methodData, details, options);
          // Async update to details
          request.onshippingaddresschange = ev => {
            ev.updateWith(checkShipping(request));
          };
          // Sync update to the total
          request.onshippingoptionchange = ev => {
            // selected shipping option
            const { shippingOption } = request;
            const newTotal = {
              currency: "USD",
              label: "Total due",
              value: calculateNewTotal(shippingOption),
            };
            ev.updateWith({ total: newTotal });
          };
          async function checkShipping(request) {
            try {
              const json = request.shippingAddress.toJSON();

              await ensureCanShipTo(json);
              const { shippingOptions, total } = await calculateShipping(json);

              return { shippingOptions, total };
            } catch (err) {
              return { error: `Sorry! we can't ship to your address.` };
            }
          }
        

Fine-grained error reporting

A developer can use the {{PaymentDetailsUpdate/shippingAddressErrors}} member of the {{PaymentDetailsUpdate}} dictionary to indicate that there are validation errors with specific attributes of a {{PaymentAddress}}. The {{PaymentDetailsUpdate/shippingAddressErrors}} member is a {{AddressErrors}} dictionary, whose members specifically demarcate the fields of a physical address that are erroneous while also providing helpful error messages to be displayed to the end user.

          request.onshippingaddresschange = ev => {
            ev.updateWith(validateAddress(request.shippingAddress));
          };
          function validateAddress(shippingAddress) {
            const error = "Can't ship to this address.";
            const shippingAddressErrors = {
              city: "FarmVille is not a real place.",
              postalCode: "Unknown postal code for your country.",
            };
            // Empty shippingOptions implies that we can't ship
            // to this address.
            const shippingOptions = [];
            return { error, shippingAddressErrors, shippingOptions };
          }
        

POSTing payment response back to a server

It's expected that data in a {{PaymentResponse}} will be POSTed back to a server for processing. To make this as easy as possible, {{PaymentResponse}} can use the [=default toJSON steps=] (i.e., `.toJSON()`) to serializes the object directly into JSON. This makes it trivial to POST the resulting JSON back to a server using the [[[fetch]]]:

          async function doPaymentRequest() {
            const payRequest = new PaymentRequest(methodData, details, options);
            const payResponse = await payRequest.show();
            let result = "";
            try {
              const httpResponse = await fetch("/process-payment", {
                method: "POST",
                headers: { "Content-Type": "application/json" },
                body: payResponse.toJSON(),
              });
              result = httpResponse.ok ? "success" : "fail";
            } catch (err) {
              console.error(err);
              result = "fail";
            }
            await payResponse.complete(result);
          }
          doPaymentRequest();
        

Using with cross-origin iframes

To indicate that a cross-origin [^iframe^] is allowed to invoke the payment request API, the [^iframe/allow^] attribute along with the "payment" keyword can be specified on the [^iframe^] element.

          <iframe
            src="https://cross-origing.example"
            allow="payment">
          </iframe>
        

If the [^iframe^] will be navigated across multiple origins that support the Payment Request API, then one can set [^iframe/allow^] to `"payment *"`. The [[[permissions-policy]]] specification provides further details and examples.

PaymentRequest interface

        [SecureContext, Exposed=Window]
        interface PaymentRequest : EventTarget {
          constructor(
            sequence<PaymentMethodData> methodData,
            PaymentDetailsInit details,
            optional PaymentOptions options = {}
          );
          [NewObject]
          Promise<PaymentResponse> show(optional Promise<PaymentDetailsUpdate> detailsPromise);
          [NewObject]
          Promise<undefined> abort();
          [NewObject]
          Promise<boolean> canMakePayment();

          readonly attribute DOMString id;
          readonly attribute PaymentAddress? shippingAddress;
          readonly attribute DOMString? shippingOption;
          readonly attribute PaymentShippingType? shippingType;

          attribute EventHandler onshippingaddresschange;
          attribute EventHandler onshippingoptionchange;
          attribute EventHandler onpaymentmethodchange;
        };
      

A developer creates a {{PaymentRequest}} to make a payment request. This is typically associated with the user initiating a payment process (e.g., by activating a "Buy," "Purchase," or "Checkout" button on a web site, selecting a "Power Up" in an interactive game, or paying at a kiosk in a parking structure). The {{PaymentRequest}} allows developers to exchange information with the user agent while the user is providing input (up to the point of user approval or denial of the payment request).

The {{PaymentRequest/shippingAddress}}, {{PaymentRequest/shippingOption}}, and {{PaymentRequest/shippingType}} attributes are populated during processing if the {{PaymentOptions/requestShipping}} member is set.

A |request|'s payment-relevant browsing context is that {{PaymentRequest}}'s [=relevant global object=]'s browsing context's top-level browsing context. Every payment-relevant browsing context has a payment request is showing boolean, which prevents showing more than one payment UI at a time.

The payment request is showing boolean simply prevents more than one payment UI being shown in a single browser tab. However, a payment handler can restrict the user agent to showing only one payment UI across all browser windows and tabs. Other payment handlers might allow showing a payment UI across disparate browser tabs.

Constructor

The {{PaymentRequest}} is constructed using the supplied sequence of PaymentMethodData |methodData| including any payment method specific {{PaymentMethodData/data}}, the PaymentDetailsInit |details|, and the {{PaymentOptions}} |options|.

The PaymentRequest(|methodData|, |details|, |options|) constructor MUST act as follows:

  1. If the current settings object's [=environment settings object / responsible document=] is not allowed to use the "[=payment-permission|payment=]" permission, then [=exception/throw=] a {{"SecurityError"}} {{DOMException}}.
  2. Establish the request's id:
    1. If |details|.{{PaymentDetailsInit/id}} is missing, add an {{PaymentDetailsInit/id}} member to |details| and set its value to a UUID [[RFC4122]].
  3. Let |serializedMethodData| be an empty list.
  4. Process payment methods:
    1. If the length of the |methodData| sequence is zero, then [=exception/throw=] a {{TypeError}}, optionally informing the developer that at least one payment method is required.
    2. Let |seenPMIs:Set| be the empty [=set=].
    3. For each |paymentMethod| of |methodData|:
      1. Run the steps to validate a payment method identifier with |paymentMethod|.{{PaymentMethodData/supportedMethods}}. If it returns false, then throw a {{RangeError}} exception. Optionally, inform the developer that the payment method identifier is invalid.
      2. Let |pmi| be the result of parsing |paymentMethod|.{{PaymentMethodData/supportedMethods}} with [=basic URL parser=]:
        1. If failure, set |pmi| to |paymentMethod|.{{PaymentMethodData/supportedMethods}}.
      3. If |seenPMIs| [=set/contain|contains=] |pmi| throw a {{RangeError}} {{DOMException}} optionally letting the developer this [=payment method identifier=] is a duplicate.
      4. [=set/Append=] |pmi| to |seenPMIs|.
      5. If the {{PaymentMethodData/data}} member of |paymentMethod| is missing, let |serializedData| be null. Otherwise, let |serializedData| be the result of JSON-serializing |paymentMethod|.{{PaymentMethodData/data}} into a string. Rethrow any exceptions.
      6. If |serializedData| is not null, and if required by the specification that defines the |paymentMethod|.{{PaymentMethodData/supportedMethods}}:
        1. Let |object| be the result of JSON-parsing |serializedData|.
        2. [=converted to an IDL value|Convert=] |object| to an IDL value of the type specified by the specification that defines the |paymentMethod|.{{PaymentMethodData/supportedMethods}} (e.g., {{BasicCardRequest}} in the case of [[[?payment-method-basic-card]]]). Rethrow any exceptions.

          This step assures that any IDL type conversion errors are caught as early as possible.

      7. Add the tuple (|paymentMethod|.{{PaymentMethodData/supportedMethods}}, |serializedData|) to |serializedMethodData|.
  5. Process the total:
    1. Check and canonicalize total amount |details|.{{PaymentDetailsInit/total}}.{{PaymentItem/amount}}. Rethrow any exceptions.
  6. If the {{PaymentDetailsBase/displayItems}} member of |details| is present, then for each |item| in |details|.{{PaymentDetailsBase/displayItems}}:
    1. Check and canonicalize amount |item|.{{PaymentItem/amount}}. Rethrow any exceptions.
  7. Let |selectedShippingOption| be null.
  8. If the {{PaymentOptions/requestShipping}} member of |options| is present and set to true, process shipping options:
    1. Let |options:PaymentShippingOption| be an empty sequence<{{PaymentShippingOption}}>.
    2. If the {{PaymentDetailsBase/shippingOptions}} member of |details| is present, then:
      1. Let |seenIDs| be an empty set.
      2. For each |option:PaymentShippingOption| in |details|.{{PaymentDetailsBase/shippingOptions}}:
        1. Check and canonicalize amount |item|.{{PaymentItem/amount}}. Rethrow any exceptions.
        2. If |seenIDs| contains |option|.{{PaymentShippingOption/id}}, then throw a {{TypeError}}. Optionally, inform the developer that shipping option IDs must be unique.
        3. Otherwise, append |option|.{{PaymentShippingOption/id}} to |seenIDs|.
        4. If |option|.{{PaymentShippingOption/selected}} is true, then set |selectedShippingOption| to |option|.{{PaymentShippingOption/id}}.
    3. Set |details|.{{PaymentDetailsBase/shippingOptions}} to |options|.
  9. Let |serializedModifierData| be an empty list.
  10. Process payment details modifiers:
    1. Let |modifiers| be an empty sequence<{{PaymentDetailsModifier}}>.
    2. If the {{PaymentDetailsBase/modifiers}} member of |details| is present, then:
      1. Set |modifiers| to |details|.{{PaymentDetailsBase/modifiers}}.
      2. For each |modifier| of |modifiers|:
        1. If the {{PaymentDetailsModifier/total}} member of |modifier| is present, then:
          1. Check and canonicalize total amount |modifier|.{{PaymentDetailsModifier/total}}.{{PaymentItem/amount}}. Rethrow any exceptions.
        2. If the {{PaymentDetailsModifier/additionalDisplayItems}} member of |modifier| is present, then for each |item| of |modifier|.{{PaymentDetailsModifier/additionalDisplayItems}}:
          1. Check and canonicalize amount |item|.{{PaymentItem/amount}}. Rethrow any exceptions.
        3. If the {{PaymentDetailsModifier/data}} member of |modifier| is missing, let |serializedData| be null. Otherwise, let |serializedData| be the result of JSON-serializing |modifier|.{{PaymentDetailsModifier/data}} into a string. Rethrow any exceptions.
        4. Add the tuple (|modifier|.{{PaymentDetailsModifier/supportedMethods}}, |serializedData|) to |serializedModifierData|.
        5. Remove the {{PaymentDetailsModifier/data}} member of |modifier|, if it is present.
    3. Set |details|.{{PaymentDetailsBase/modifiers}} to |modifiers|.
  11. Let |request:PaymentRequest| be a new {{PaymentRequest}}.
  12. Set |request|.{{PaymentRequest/[[handler]]}} to `null`.
  13. Set |request|.{{PaymentRequest/[[options]]}} to |options|.
  14. Set |request|.{{PaymentRequest/[[state]]}} to "[=state/created=]".
  15. Set |request|.{{PaymentRequest/[[updating]]}} to false.
  16. Set |request|.{{PaymentRequest/[[details]]}} to |details|.
  17. Set |request|.{{PaymentRequest/[[serializedModifierData]]}} to |serializedModifierData|.
  18. Set |request|.{{PaymentRequest/[[serializedMethodData]]}} to |serializedMethodData|.
  19. Set |request|.{{PaymentRequest/[[response]]}} to null.
  20. Set the value of |request|'s {{PaymentRequest/shippingOption}} attribute to |selectedShippingOption|.
  21. Set the value of the {{PaymentRequest/shippingAddress}} attribute on |request| to null.
  22. If |options|.{{PaymentOptions/requestShipping}} is set to true, then set the value of the {{PaymentRequest/shippingType}} attribute on |request| to |options|.{{PaymentOptions/shippingType}}. Otherwise, set it to null.
  23. Return |request|.

id attribute

When getting, the {{PaymentRequest/id}} attribute returns this {{PaymentRequest}}'s {{PaymentRequest/[[details]]}}.{{PaymentDetailsInit/id}}.

For auditing and reconciliation purposes, a merchant can associate a unique identifier for each transaction with the {{PaymentDetailsInit/id}} attribute.

show() method

The {{PaymentRequest/show()}} method is called when a developer wants to begin user interaction for the payment request. The {{PaymentRequest/show()}} method returns a {{Promise}} that will be resolved when the user accepts the payment request. Some kind of user interface will be presented to the user to facilitate the payment request after the {{PaymentRequest/show()}} method returns.

Each payment handler controls what happens when multiple browsing context simultaneously call the {{PaymentRequest/show()}} method. For instance, some payment handlers will allow multiple payment UIs to be shown in different browser tabs/windows. Other payment handlers might only allow a single payment UI to be shown for the entire user agent.

The show(optional |detailsPromise|) method MUST act as follows:

  1. Let |request:PaymentRequest| be [=this=].
  2. If the [=relevant global object=] of [=request=] does not have [=transient activation=], return [=a promise rejected with=] with a {{"NotAllowedError"}} {{DOMException}}.
  3. [=Consume user activation=] of the [=relevant global object=].
  4. Let |document| be |request|'s [=relevant global object=]'s [=associated `Document`=].
  5. If |document| is not [=Document/fully active=], then return a promise rejected with an {{"AbortError"}} {{DOMException}}.
  6. Optionally, if the user agent wishes to disallow the call to {{PaymentRequest/show()}} to protect the user, then return a promise rejected with a {{"SecurityError"}} {{DOMException}}. For example, the user agent may limit the rate at which a page can call {{PaymentRequest/show()}}, as described in section .

  7. If |request|.{{PaymentRequest/[[state]]}} is not "[=state/created=]" then return a promise rejected with an {{"InvalidStateError"}} {{DOMException}}.
  8. If the user agent's payment request is showing boolean is true, then:
    1. Set |request|.{{PaymentRequest/[[state]]}} to "[=state/closed=]".
    2. Return a promise rejected with an {{"AbortError"}} {{DOMException}}.
  9. Set |request|.{{PaymentRequest/[[state]]}} to "[=state/interactive=]".
  10. Let |acceptPromise:Promise| be a new promise.
  11. Set |request|.{{PaymentRequest/[[acceptPromise]]}} to |acceptPromise|.
  12. Optionally:

    1. Reject |acceptPromise| with an {{"AbortError"}} {{DOMException}}.
    2. Set |request|.{{PaymentRequest/[[state]]}} to "[=state/closed=]".
    3. Return |acceptPromise|.

    This allows the user agent to act as if the user had immediately [=user aborts the payment request|aborted the payment request=], at its discretion. For example, in "private browsing" modes or similar, user agents might take advantage of this step.

  13. Set |request|'s payment-relevant browsing context's payment request is showing boolean to true.
  14. Return |acceptPromise| and perform the remaining steps in parallel.
  15. Let |handlers:list| be an empty list.
  16. For each |paymentMethod| tuple in |request|.{{PaymentRequest/[[serializedMethodData]]}}:
    1. Let |identifier| be the first element in the |paymentMethod| tuple.
    2. Let |data| be the result of JSON-parsing the second element in the |paymentMethod| tuple.
    3. If required by the specification that defines the |identifier|, then [=converted to an IDL value|convert=] |data| to an IDL value of the type specified there. Otherwise, [=converted to an IDL value|convert=] to {{object}}.
    4. If conversion results in an exception |error|:
      1. Set |request|.{{PaymentRequest/[[state]]}} to "[=state/closed=]".
      2. Reject |acceptPromise| with |error|.
      3. Set |request|'s payment-relevant browsing context's payment request is showing boolean to false.
      4. Terminate this algorithm.
    5. Let |registeredHandlers| be a list of registered payment handlers for the payment method |identifier|.
    6. For each |handler| in |registeredHandlers|:
      1. Let |canMakePayment| be the result of running |handler|'s steps to check if a payment can be made with |data|.
      2. If |canMakePayment| is true, then append |handler| to |handlers|.
  17. If |handlers| is empty, then:
    1. Set |request|.{{PaymentRequest/[[state]]}} to "[=state/closed=]".
    2. Reject |acceptPromise| with {{"NotSupportedError"}} {{DOMException}}.
    3. Set |request|'s payment-relevant browsing context's payment request is showing boolean to false.
    4. Terminate this algorithm.
  18. Present a user interface that will allow the user to interact with the |handlers|. The user agent SHOULD prioritize the user's preference when presenting payment methods.

  19. If |detailsPromise| was passed, then:
    1. Run the update a PaymentRequest's details algorithm with |detailsPromise|, |request|, and null.
    2. Wait for the |detailsPromise| to settle.

      Based on how the |detailsPromise| settles, the update a PaymentRequest's details algorithm determines how the payment UI behaves. That is, upon rejection of the |detailsPromise|, the payment request aborts. Otherwise, upon fulfillment |detailsPromise|, the user agent re-enables the payment request UI and the payment flow can continue.

  20. Set |request|.{{PaymentRequest/[[handler]]}} be the payment handler selected by the end-user.
  21. Let |modifiers:list| be an empty list.
  22. For each |tuple| in {{PaymentRequest/[[serializedModifierData]]}}:
    1. If the first element of |tuple| (a PMI) matches the payment method identifier of |request|.{{PaymentRequest/[[handler]]}}, then append the second element of |tuple| (the serialized method data) to |modifiers|.
  23. Pass the [=converted to an IDL value|converted=] second element in the |paymentMethod| tuple and |modifiers|. Optionally, the user agent SHOULD send the appropriate data from |request| to the user-selected payment handler in order to guide the user through the payment process. This includes the various attributes and other internal slots of |request| (some MAY be excluded for privacy reasons where appropriate).

    Handling of multiple applicable modifiers in the {{PaymentRequest/[[serializedModifierData]]}} internal slot is payment handler specific and beyond the scope of this specification. Nevertheless, it is RECOMMENDED that payment handlers use a "last one wins" approach with items in the {{PaymentRequest/[[serializedModifierData]]}} list: that is to say, an item at the end of the list always takes precedence over any item at the beginning of the list (see example below).

    The |acceptPromise| will later be resolved or rejected by either the user accepts the payment request algorithm or the user aborts the payment request algorithm, which are triggered through interaction with the user interface.

    If |document| stops being [=Document/fully active=] while the user interface is being shown, or no longer is by the time this step is reached, then:

    1. Close down the user interface.
    2. Set |request|'s payment-relevant browsing context's payment request is showing boolean to false.

abort() method

The {{PaymentRequest/abort()}} method is called if a developer wishes to tell the user agent to abort the payment |request| and to tear down any user interface that might be shown. The {{PaymentRequest/abort()}} can only be called after the {{PaymentRequest/show()}} method has been called (see states) and before this instance's {{PaymentRequest/[[acceptPromise]]}} has been resolved. For example, developers might choose to do this if the goods they are selling are only available for a limited amount of time. If the user does not accept the payment request within the allowed time period, then the request will be aborted.

A user agent might not always be able to abort a request. For example, if the user agent has delegated responsibility for the request to another app. In this situation, {{PaymentRequest/abort()}} will reject the returned {{Promise}}.

See also the algorithm when the user aborts the payment request.

The {{PaymentRequest/abort()}} method MUST act as follows:

  1. Let |request:PaymentRequest| be [=this=].
  2. If |request|.{{PaymentRequest/[[response]]}} is not null, and |request|.{{PaymentRequest/[[response]]}}.{{PaymentResponse/[[retryPromise]]}} is not null, return a promise rejected with an {{"InvalidStateError"}} {{DOMException}}.
  3. If the value of |request|.{{PaymentRequest/[[state]]}} is not "[=state/interactive=]" then return a promise rejected with an {{"InvalidStateError"}} {{DOMException}}.
  4. Let |promise:Promise| be a new promise.
  5. Return |promise| and perform the remaining steps in parallel.
  6. Try to abort the current user interaction with the payment handler and close down any remaining user interface.
  7. Queue a task on the user interaction task source to perform the following steps:
    1. If it is not possible to abort the current user interaction, then reject |promise| with {{"InvalidStateError"}} {{DOMException}} and abort these steps.
    2. Set |request|.{{PaymentRequest/[[state]]}} to "[=state/closed=]".
    3. Reject the promise |request|.{{PaymentRequest/[[acceptPromise]]}} with an {{"AbortError"}} {{DOMException}}.
    4. Resolve |promise| with undefined.

canMakePayment() method

The {{PaymentRequest/canMakePayment()}} method can be used by the developer to determine if the user agent has support for one of the desired payment methods. See [[[#canmakepayment-protections]]].

A true result from {{PaymentRequest/canMakePayment()}} does not imply that the user has a provisioned instrument ready for payment.

The {{PaymentRequest/canMakePayment()}} method MUST run the can make payment algorithm.

shippingAddress attribute

A {{PaymentRequest}}'s {{PaymentRequest/shippingAddress}} attribute is populated when the user provides a shipping address. It is null by default. When a user provides a shipping address, the shipping address changed algorithm runs.

shippingType attribute

A {{PaymentRequest}}'s {{PaymentRequest/shippingType}} attribute is the type of shipping used to fulfill the transaction. Its value is either a {{PaymentShippingType}} enum value, or null if none is provided by the developer during [=PaymentRequest.PaymentRequest()|construction=] (see {{PaymentOptions}}'s {{PaymentOptions/shippingType}} member).

onshippingaddresschange attribute

A {{PaymentRequest}}'s {{PaymentRequest/onshippingaddresschange}} attribute is an {{EventHandler}} for a {{PaymentRequestUpdateEvent}} named shippingaddresschange.

shippingOption attribute

A {{PaymentRequest}}'s {{PaymentRequest/shippingOption}} attribute is populated when the user chooses a shipping option. It is null by default. When a user chooses a shipping option, the shipping option changed algorithm runs.

onshippingoptionchange attribute

A {{PaymentRequest}}'s {{PaymentRequest/onshippingoptionchange}} attribute is an {{EventHandler}} for a {{PaymentRequestUpdateEvent}} named shippingoptionchange.

onpaymentmethodchange attribute

A {{PaymentRequest}}'s {{PaymentRequest/onpaymentmethodchange}} attribute is an {{EventHandler}} for a {{PaymentMethodChangeEvent}} named "paymentmethodchange".

Internal Slots

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

Internal Slot Description (non-normative)
[[\serializedMethodData]] The methodData supplied to the constructor, but represented as tuples containing supported methods and a string or null for data (instead of the original object form).
[[\serializedModifierData]] A list containing the serialized string form of each {{PaymentDetailsModifier/data}} member for each corresponding item in the sequence {{PaymentRequest/[[details]]}}.{{PaymentDetailsBase/modifier}}, or null if no such member was present.
[[\details]] The current {{PaymentDetailsBase}} for the payment request initially supplied to the constructor and then updated with calls to {{PaymentRequestUpdateEvent/updateWith()}}. Note that all {{PaymentDetailsModifier/data}} members of {{PaymentDetailsModifier}} instances contained in the {{PaymentDetailsBase/modifiers}} member will be removed, as they are instead stored in serialized form in the {{PaymentRequest/[[serializedModifierData]]}} internal slot.
[[\options]] The {{PaymentOptions}} supplied to the constructor.
[[\state]]

The current state of the payment request, which transitions from:

"created"
The payment request is constructed and has not been presented to the user.
"interactive"
The payment request is being presented to the user.
"closed"
The payment request completed.

The state transitions are illustrated in the figure below:

The constructor sets the initial state to "[=state/created=]". The {{PaymentRequest/show()}} method changes the state to "[=state/interactive=]". From there, the {{PaymentRequest/abort()}} method or any other error can send the state to "[=state/closed=]"; similarly, the user accepts the payment request algorithm and user aborts the payment request algorithm will change the state to "[=state/closed=]".
[[\updating]] True if there is a pending {{PaymentRequestUpdateEvent/updateWith()}} call to update the payment request and false otherwise.
[[\acceptPromise]] The pending {{Promise}} created during {{PaymentRequest/show()}} that will be resolved if the user accepts the payment request.
[[\response]] Null, or the {{PaymentResponse}} instantiated by this {{PaymentRequest}}.
[[\handler]] The Payment Handler associated with this {{PaymentRequest}}. Initialized to `null`.

PaymentMethodData dictionary

        dictionary PaymentMethodData {
          required DOMString supportedMethods;
          object data;
        };
      

A PaymentMethodData dictionary is used to indicate a set of supported payment methods and any associated payment method specific data for those methods.

supportedMethods member
A payment method identifier for a payment method that the merchant web site accepts.
data member
An object that provides optional information that might be needed by the supported payment methods. If supplied, it will be JSON-serialized.

The value of supportedMethods was changed from array to string, but the name was left as a plural to maintain compatibility with existing content on the Web.

PaymentCurrencyAmount dictionary

        dictionary PaymentCurrencyAmount {
          required DOMString currency;
          required DOMString value;
        };
      

A {{PaymentCurrencyAmount}} dictionary is used to supply monetary amounts.

currency member

An [[ISO4217]] well-formed 3-letter alphabetic code (i.e., the numeric codes are not supported). Their canonical form is upper case. However, the set of combinations of currency code for which localized currency symbols are available is implementation dependent. Where a localized currency symbol is not available, a user agent SHOULD use U+00A4 (¤) for formatting. User agents MAY format the display of the {{PaymentCurrencyAmount/currency}} member to adhere to OS conventions (e.g., for localization purposes).

User agents implementing this specification enforce [[ISO4217]]'s 3-letter codes format via ECMAScript’s isWellFormedCurrencyCode abstract operation, which is invoked as part of the check and canonicalize amount algorithm. When a code does not adhere to the [[ISO4217]] defined format, a {{RangeError}} is thrown.

Current implementations will therefore allow the use of well-formed currency codes that are not part of the official [[ISO4217]] list (e.g., XBT, XRP, etc.). If the provided code is a currency that the browser knows how to display, then an implementation will generally display the appropriate currency symbol in the user interface (e.g., "USD" is shown as "$", "GBP" is "£", and the non-standard "XBT" could be shown as "Ƀ"). When a code cannot be matched, the specification recommends browsers show a scarab "¤".

Efforts are underway at ISO to account for digital currencies, which may result in an update to the [[ISO4217]] registry or an entirely new registry. The community expects this will resolve ambiguities that have crept in through the use of non-standard 3-letter codes; for example, does "BTC" refer to Bitcoin or to a future Bhutan currency? At the time of publication, it remains unclear what form this evolution will take, or even the time frame in which the work will be completed. The W3C Web Payments Working Group is liaising with ISO so that, in the future, revisions to this specification remain compatible with relevant ISO registries.

value member
A valid decimal monetary value containing a monetary amount.
        {
          "currency": "USD",
          "value": "55.00"
        }
      

Validity checkers

A [=JavaScript string=] is a valid decimal monetary value if it consists of the following [=code points=] in the given order:

  1. Optionally, a single U+002D (-), to indicate that the amount is negative.
  2. One or more [=code points=] in the range U+0030 (0) to U+0039 (9).
  3. Optionally, a single U+002E (.) followed by one or more [=code points=] in the range U+0030 (0) to U+0039 (9).
The following regular expression is an implementation of the above definition.
^-?[0-9]+(\.[0-9]+)?$

To check and canonicalize amount given a {{PaymentCurrencyAmount}} |amount|, run the following steps:

  1. If the result of IsWellFormedCurrencyCode(|amount|.{{PaymentCurrencyAmount/currency}}) is false, then throw a {{RangeError}} exception, optionally informing the developer that the currency is invalid.
  2. If |amount|.{{PaymentCurrencyAmount/value}} is not a valid decimal monetary value, throw a {{TypeError}}, optionally informing the developer that the currency is invalid.
  3. Set |amount|.{{PaymentCurrencyAmount/currency}} to the result of ASCII uppercase |amount|.{{PaymentCurrencyAmount/currency}}.

To check and canonicalize total amount given a {{PaymentCurrencyAmount}} |amount:PaymentCurrencyAmount|, run the following steps:

  1. Check and canonicalize amount |amount|. Rethrow any exceptions.
  2. If the first code point of |amount|.{{PaymentCurrencyAmount/value}} is U+002D (-), then throw a {{TypeError}} optionally informing the developer that a total's value can't be a negative number.

Payment details dictionaries

PaymentDetailsBase dictionary

        dictionary PaymentDetailsBase {
          sequence<PaymentItem> displayItems;
          sequence<PaymentShippingOption> shippingOptions;
          sequence<PaymentDetailsModifier> modifiers;
        };
        
displayItems member
A sequence of {{PaymentItem}} dictionaries contains line items for the payment request that the user agent MAY display.
shippingOptions member

A sequence containing the different shipping options for the user to choose from.

If an item in the sequence has the {{PaymentShippingOption/selected}} member set to true, then this is the shipping option that will be used by default and {{PaymentRequest/shippingOption}} will be set to the {{PaymentShippingOption/id}} of this option without running the shipping option changed algorithm. If more than one item in the sequence has {{PaymentShippingOption/selected}} set to true, then the user agent selects the last one in the sequence.

The {{PaymentDetailsBase/shippingOptions}} member is only used if the {{PaymentRequest}} was constructed with {{PaymentOptions}} and {{PaymentOptions/requestShipping}} set to true.

modifiers member
A sequence of {{PaymentDetailsModifier}} dictionaries that contains modifiers for particular payment method identifiers. For example, it allows you to adjust the total amount based on payment method.

PaymentDetailsInit dictionary

          dictionary PaymentDetailsInit : PaymentDetailsBase {
            DOMString id;
            required PaymentItem total;
          };
        

In addition to the members inherited from the {{PaymentDetailsBase}} dictionary, the following members are part of the PaymentDetailsInit dictionary:

id member
A free-form identifier for this payment request.
total member
A {{PaymentItem}} containing a non-negative total amount for the payment request.

PaymentDetailsUpdate dictionary

          dictionary PaymentDetailsUpdate : PaymentDetailsBase {
            DOMString error;
            PaymentItem total;
            AddressErrors shippingAddressErrors;
            PayerErrors payerErrors;
            object paymentMethodErrors;
          };
        

The {{PaymentDetailsUpdate}} dictionary is used to update the payment request using {{PaymentRequestUpdateEvent/updateWith()}}.

In addition to the members inherited from the {{PaymentDetailsBase}} dictionary, the following members are part of the {{PaymentDetailsUpdate}} dictionary:

error member
A human-readable string that explains why goods cannot be shipped to the chosen shipping address, or any other reason why no shipping options are available. When the payment request is updated using {{PaymentRequestUpdateEvent/updateWith()}}, the {{PaymentDetailsUpdate}} can contain a message in the {{PaymentDetailsUpdate/error}} member that will be displayed to the user if the {{PaymentDetailsUpdate}} indicates that there are no valid {{PaymentDetailsBase/shippingOptions}} (and the {{PaymentRequest}} was constructed with the {{PaymentOptions/requestShipping}} option set to true).
total member
A {{PaymentItem}} containing a non-negative {{PaymentItem/amount}}.

Algorithms in this specification that accept a {{PaymentDetailsUpdate}} dictionary will throw if the {{PaymentDetailsUpdate/total}}.{{PaymentItem/amount}}.{{PaymentCurrencyAmount/value}} is a negative number.

shippingAddressErrors member
Represents validation errors with the shipping address that is associated with the potential event target.
payerErrors member
Validation errors related to the payer details.
paymentMethodErrors member

Payment method specific errors. See, for example, [[[?payment-method-basic-card]]]'s {{BasicCardErrors}}.

PaymentDetailsModifier dictionary

        dictionary PaymentDetailsModifier {
          required DOMString supportedMethods;
          PaymentItem total;
          sequence<PaymentItem> additionalDisplayItems;
          object data;
        };
      

The {{PaymentDetailsModifier}} dictionary provides details that modify the {{PaymentDetailsBase}} based on a payment method identifier. It contains the following members:

supportedMethods member
A payment method identifier. The members of the {{PaymentDetailsModifier}} only apply if the user selects this payment method.
total member
A {{PaymentItem}} value that overrides the {{PaymentDetailsInit/total}} member in the PaymentDetailsInit dictionary for the payment method identifiers of the {{PaymentDetailsModifier/supportedMethods}} member.
additionalDisplayItems member
A sequence of {{PaymentItem}} dictionaries provides additional display items that are appended to the {{PaymentDetailsBase/displayItems}} member in the {{PaymentDetailsBase}} dictionary for the payment method identifiers in the {{PaymentDetailsModifier/supportedMethods}} member. This member is commonly used to add a discount or surcharge line item indicating the reason for the different {{PaymentDetailsModifier/total}} amount for the selected payment method that the user agent MAY display.

It is the developer's responsibility to verify that the {{PaymentDetailsModifier/total}} amount is the sum of the {{PaymentDetailsBase/displayItems}} and the {{PaymentDetailsModifier/additionalDisplayItems}}.

data member
An object that provides optional information that might be needed by the supported payment methods. If supplied, it will be JSON-serialized.

PaymentShippingType enum

        enum PaymentShippingType {
          "shipping",
          "delivery",
          "pickup"
        };
      
"shipping"
This is the default and refers to the address being collected as the destination for shipping.
"delivery"
This refers to the address being collected as the destination for delivery. This is commonly faster than shipping. For example, it might be used for food delivery.
"pickup"
This refers to the address being collected as part of a service pickup. For example, this could be the address for laundry pickup.

PaymentOptions dictionary

        dictionary PaymentOptions {
          boolean requestPayerName = false;
          boolean requestBillingAddress = false;
          boolean requestPayerEmail = false;
          boolean requestPayerPhone = false;
          boolean requestShipping = false;
          PaymentShippingType shippingType = "shipping";
        };
      

The {{PaymentOptions}} dictionary is passed to the {{PaymentRequest}} constructor and provides information about the options desired for the payment request.

requestBillingAddress member
A boolean that indicates whether the user agent SHOULD collect and return the billing address associated with a payment method (e.g., the billing address associated with a credit card). Typically, the user agent will return the billing address as part of the {{PaymentMethodChangeEvent}}'s {{PaymentMethodChangeEvent/methodDetails}}. A merchant can use this information to, for example, calculate tax in certain jurisdictions and update the displayed total. See below for privacy considerations regarding exposing user information.
requestPayerName member
A boolean that indicates whether the user agent SHOULD collect and return the payer's name as part of the payment request. For example, this would be set to true to allow a merchant to make a booking in the payer's name.
requestPayerEmail member
A boolean that indicates whether the user agent SHOULD collect and return the payer's email address as part of the payment request. For example, this would be set to true to allow a merchant to email a receipt.
requestPayerPhone member
A boolean that indicates whether the user agent SHOULD collect and return the payer's phone number as part of the payment request. For example, this would be set to true to allow a merchant to phone a customer with a billing enquiry.
requestShipping member
A boolean that indicates whether the user agent SHOULD collect and return a shipping address as part of the payment request. For example, this would be set to true when physical goods need to be shipped by the merchant to the user. This would be set to false for the purchase of digital goods.
shippingType member
A {{PaymentShippingType}} enum value. Some transactions require an address for delivery but the term "shipping" isn't appropriate. For example, "pizza delivery" not "pizza shipping" and "laundry pickup" not "laundry shipping". If {{PaymentOptions/requestShipping}} is set to true, then the {{PaymentOptions/shippingType}} member can influence the way the user agent presents the user interface for gathering the shipping address.

The {{PaymentOptions/shippingType}} member only affects the user interface for the payment request.

PaymentItem dictionary

        dictionary PaymentItem {
          required DOMString label;
          required PaymentCurrencyAmount amount;
          boolean pending = false;
        };
      

A sequence of one or more {{PaymentItem}} dictionaries is included in the {{PaymentDetailsBase}} dictionary to indicate what the payment request is for and the value asked for.

label member
A human-readable description of the item. The user agent may display this to the user.
amount member
A {{PaymentCurrencyAmount}} containing the monetary amount for the item.
pending member
A boolean. When set to true it means that the {{PaymentItem/amount}} member is not final. This is commonly used to show items such as shipping or tax amounts that depend upon selection of shipping address or shipping option. User agents MAY indicate pending fields in the user interface for the payment request.

Physical addresses

A physical address is composed of the following parts.

Country
The country corresponding to the address.
Address line
The most specific part of the address. It can include, for example, a street name, a house number, apartment number, a rural delivery route, descriptive instructions, or a post office box number.
Region
The top level administrative subdivision of the country. For example, this can be a state, a province, an oblast, or a prefecture.
City
The city/town portion of the address.
Dependent locality
The dependent locality or sublocality within a city. For example, neighborhoods, boroughs, districts, or UK dependent localities.
Postal code
The postal code or ZIP code, also known as PIN code in India.
Sorting code
The sorting code system, such as the CEDEX system used in France.
Organization
The organization, firm, company, or institution at the address.
Recipient
The name of the recipient or contact person at the address.
Phone number
The phone number of the recipient or contact person at the address.

PaymentAddress interface

          [SecureContext, Exposed=(Window)]
          interface PaymentAddress {
            [Default] object toJSON();
            readonly attribute DOMString city;
            readonly attribute DOMString country;
            readonly attribute DOMString dependentLocality;
            readonly attribute DOMString organization;
            readonly attribute DOMString phone;
            readonly attribute DOMString postalCode;
            readonly attribute DOMString recipient;
            readonly attribute DOMString region;
            readonly attribute DOMString sortingCode;
            readonly attribute FrozenArray<DOMString> addressLine;
          };
        

The {{PaymentAddress}} interface represents a physical address.

Internal constructor

The steps to internally construct a `PaymentAddress` with an optional {{AddressInit}} |details:AddressInit| are given by the following algorithm:

  1. Let |address:PaymentAddress| be a new instance of {{PaymentAddress}}.
  2. Set |address|.{{PaymentAddress/[[addressLine]]}} to the empty frozen array, and all other [=PaymentAddress slots|internal slots=] to the empty string.
  3. If |details| was not passed, return |address|.
  4. If |details|.{{AddressInit/country}} is present and not the empty string:
    1. Set |country| the result of strip leading and trailing ASCII whitespace from |details|.{{AddressInit/country}} and performing ASCII uppercase.
    2. If |country| is not a valid [[ISO3166-1]] alpha-2 code, throw a {{RangeError}} exception.
    3. Set |address|.{{PaymentAddress/[[country]]}} to |country|.
  5. Let |cleanAddressLines:list| be an empty list.
  6. If |details|.{{AddressInit/addressLine}} is present, then for each |item| in |details|.{{AddressInit/addressLine}}:
    1. Strip leading and trailing ASCII whitespace from |item| and append the result into |cleanAddressLines|.
  7. Set |address|.{{PaymentAddress/[[addressLine]]}} to a new frozen array created from |cleanAddressLines|.
  8. If |details|.{{AddressInit/region}} is present, strip leading and trailing ASCII whitespace from |details|.{{AddressInit/region}} and set |address|.{{PaymentAddress/[[region]]}} to the result.
  9. If |details|.{{AddressInit/city}} is present, strip leading and trailing ASCII whitespace from |details|.{{AddressInit/city}} and set |address|.{{PaymentAddress/[[city]]}} to the result.
  10. If |details|.{{AddressInit/dependentLocality}} is present, strip leading and trailing ASCII whitespace from |details|.{{AddressInit/dependentLocality}} and set |address|.{{PaymentAddress/[[dependentLocality]]}} to the result.
  11. If |details|.{{AddressInit/postalCode}} is present, strip leading and trailing ASCII whitespace from |details|.{{AddressInit/postalCode}} and set |address|.{{PaymentAddress/[[postalCode]]}} to the result.
  12. If |details|.{{AddressInit/sortingCode}} is present, strip leading and trailing ASCII whitespace from |details|.{{AddressInit/sortingCode}} and set |address|.{{PaymentAddress/[[sortingCode]]}} to the result.
  13. If |details|.{{AddressInit/organization}} is present, strip leading and trailing ASCII whitespace from |details|.{{AddressInit/organization}} and set |address|.{{PaymentAddress/[[organization]]}} to the result.
  14. If |details|.{{AddressInit/recipient}} is present, strip leading and trailing ASCII whitespace from |details|.{{AddressInit/recipient}} and set |address|.{{PaymentAddress/[[recipient]]}} to the result.
  15. If |details|.{{AddressInit/phone}} is present, strip leading and trailing ASCII whitespace from |details|.{{AddressInit/phone}} and set |address|.{{PaymentAddress/[[phone]]}} to the result.
  16. Return |address|.

country attribute

Represents the country of the address. When getting, returns the value of the {{PaymentAddress}}'s {{PaymentAddress/[[country]]}} internal slot.

addressLine attribute

Represents the address line of the address. When getting, returns the value of the {{PaymentAddress}}'s {{PaymentAddress/[[addressLine]]}} internal slot.

region attribute

Represents the region of the address. When getting, returns the value of the {{PaymentAddress}}'s {{PaymentAddress/[[region]]}} internal slot.

city attribute

Represents the city of the address. When getting, returns the value of the {{PaymentAddress}}'s {{PaymentAddress/[[city]]}} internal slot.

dependentLocality attribute

Represents the dependent locality of the address. When getting, returns the value of the {{PaymentAddress}}'s {{PaymentAddress/[[dependentLocality]]}} internal slot.

postalCode attribute

Represents the postal code of the address. When getting, returns the value of the {{PaymentAddress}}'s {{PaymentAddress/[[postalCode]]}} internal slot.

sortingCode attribute

Represents the sorting code of the address. When getting, returns the value of the {{PaymentAddress}}'s {{PaymentAddress/[[sortingCode]]}} internal slot.

organization attribute

Represents the organization of the address. When getting, returns the value of the {{PaymentAddress}}'s {{PaymentAddress/[[organization]]}} internal slot.

recipient attribute

Represents the recipient of the address. When getting, returns the value of the {{PaymentAddress}}'s {{PaymentAddress/[[recipient]]}} internal slot.

phone attribute

Represents the phone number of the address. When getting, returns the value of the {{PaymentAddress}}'s {{PaymentAddress/[[phone]]}} internal slot.

Internal slots

Internal slot Description (non-normative)
[[\country]] A country as an [[ISO3166-1]] alpha-2 code stored in its canonical uppercase form or the empty string. For example, "JP".
[[\addressLine]] A frozen array, possibly of zero length, representing an address line.
[[\region]] A region as a country subdivision name or the empty string, such as "Victoria", representing the state of Victoria in Australia.
[[\city]] A city or the empty string.
[[\dependentLocality]] A dependent locality or the empty string.
[[\postalCode]] A postal code or the empty string.
[[\sortingCode]] A sorting code or the empty string.
[[\organization]] An organization or the empty string.
[[\recipient]] A recipient or the empty string.
[[\phone]] A phone number or the empty string.

AddressInit dictionary

          dictionary AddressInit {
            DOMString country = "";
            sequence<DOMString> addressLine = [];
            DOMString region = "";
            DOMString city = "";
            DOMString dependentLocality = "";
            DOMString postalCode = "";
            DOMString sortingCode = "";
            DOMString organization = "";
            DOMString recipient = "";
            DOMString phone = "";
          };
        

An {{AddressInit}} is passed when [=PaymentAddress.PaymentAddress()|constructing=] a {{PaymentAddress}}. Its members are as follows.

country member
An country, represented as a [[ISO3166-1]] country code.
addressLine member
An address line, represented as a sequence.
region member
A region.
city member
A city.
dependentLocality member
A dependent locality.
postalCode member
A postal code.
sortingCode member
A sorting code.
organization member
An organization.
recipient member
A recipient. Under certain circumstances, this member may contain multiline information. For example, it might contain "care of" information.
phone member
A phone number, optionally structured to adhere to [[E.164]].

AddressErrors dictionary

          dictionary AddressErrors {
            DOMString addressLine;
            DOMString city;
            DOMString country;
            DOMString dependentLocality;
            DOMString organization;
            DOMString phone;
            DOMString postalCode;
            DOMString recipient;
            DOMString region;
            DOMString sortingCode;
          };
        

The members of the {{AddressErrors}} dictionary represent validation errors with specific parts of a physical address. Each dictionary member has a dual function: firstly, its presence denotes that a particular part of an address is suffering from a validation error. Secondly, the string value allows the developer to describe the validation error (and possibly how the end user can fix the error).

Developers need to be aware that users might not have the ability to fix certain parts of an address. As such, they need to be mindful not to ask the user to fix things they might not have control over.

addressLine member
Denotes that the address line has a validation error. In the user agent's UI, this member corresponds to the input field that provided the {{PaymentAddress}}'s {{PaymentAddress/addressLine}} attribute's value.
city member
Denotes that the city has a validation error. In the user agent's UI, this member corresponds to the input field that provided the {{PaymentAddress}}'s {{PaymentAddress/city}} attribute's value.
country member
Denotes that the country has a validation error. In the user agent's UI, this member corresponds to the input field that provided the {{PaymentAddress}}'s {{PaymentAddress/country}} attribute's value.
dependentLocality member
Denotes that the dependent locality has a validation error. In the user agent's UI, this member corresponds to the input field that provided the {{PaymentAddress}}'s {{PaymentAddress/dependentLocality}} attribute's value.
organization member
Denotes that the organization has a validation error. In the user agent's UI, this member corresponds to the input field that provided the {{PaymentAddress}}'s {{PaymentAddress/organization}} attribute's value.
phone member
Denotes that the phone number has a validation error. In the user agent's UI, this member corresponds to the input field that provided the {{PaymentAddress}}'s {{PaymentAddress/phone}} attribute's value.
postalCode member
Denotes that the postal code has a validation error. In the user agent's UI, this member corresponds to the input field that provided the {{PaymentAddress}}'s {{PaymentAddress/postalCode}} attribute's value.
recipient member
Denotes that the recipient has a validation error. In the user agent's UI, this member corresponds to the input field that provided the {{PaymentAddress}}'s {{PaymentAddress/addressLine}} attribute's value.
region member
Denotes that the region has a validation error. In the user agent's UI, this member corresponds to the input field that provided the {{PaymentAddress}}'s {{PaymentAddress/region}} attribute's value.
sortingCode member
The sorting code has a validation error. In the user agent's UI, this member corresponds to the input field that provided the {{PaymentAddress}}'s {{PaymentAddress/sortingCode}} attribute's value.

Creating a `PaymentAddress` from user-provided input

The steps to create a `PaymentAddress` from user-provided input are given by the following algorithm. The algorithm takes a list |redactList|.

The |redactList| optionally gives user agents the possibility to limit the amount of personal information about the recipient that the API shares with the merchant.

For merchants, the resulting {{PaymentAddress}} object provides enough information to, for example, calculate shipping costs, but, in most cases, not enough information to physically locate and uniquely identify the recipient.

Unfortunately, even with the |redactList|, recipient anonymity cannot be assured. This is because in some countries postal codes are so fine-grained that they can uniquely identify a recipient.

  1. Let |details:AddressInit| be a newly created {{AddressInit}} dictionary.
  2. If "addressLine" is not in |redactList|, set |details|.{{AddressInit/addressLine}} to the result of splitting the user-provided address line into a list.
  3. If "country" is not in |redactList|, set |details|.{{AddressInit/country}} to the user-provided country as an upper case [[ISO3166-1]] alpha-2 code.
  4. If "phone" is not in |redactList|, set |details|.{{AddressInit/phone}} to the user-provided phone number.
  5. If "city" is not in |redactList|, set |details|.{{AddressInit/city}} to the user-provided city, or to the empty string if none was provided.
  6. If "dependentLocality" is not in |redactList|, set | details|.{{AddressInit/dependentLocality}} to the user-provided dependent locality.
  7. If "organization" is not in |redactList|, set |details|.{{AddressInit/organization}} to the user-provided recipient organization.
  8. If "postalCode" is not in |redactList|, set |details|.{{AddressInit/postalCode}} to the user-provided postal code. Optionally, redact part of |details|.{{AddressInit/postalCode}}.

    Postal codes in certain countries can be so specific as to uniquely identify an individual. This being a privacy concern, some user agents only return the part of a postal code that they deem sufficient for a merchant to calculate shipping costs. This varies across countries and regions, and so the choice to redact part, or all, of the postal code is left to the discretion of implementers in the interest of protecting users' privacy.

  9. If "recipient" is not in |redactList|, set |details|.{{AddressInit/recipient}} to the user-provided recipient of the transaction.
  10. If "region" is not in |redactList|:

    In some countries (e.g., Belgium) it is uncommon for users to include a region as part of a physical address (even if all the regions of a country are part of [[ISO3166-2]]). As such, when the user agent knows that the user is inputting the address for a particular country, it might not provide a field for the user to input a region. In such cases, the user agent returns an empty string for both {{PaymentAddress}}'s {{PaymentAddress/region}} attribute - but the address can still serve its intended purpose (e.g., be valid for shipping or billing purposes).

    1. Set |details|.{{AddressInit/region}} to the user-provided region.
  11. If "sortingCode" is not in |redactList|, set |details|.{{AddressInit/sortingCode}} to the user-provided sorting code.
  12. [=PaymentAddress.PaymentAddress()|Internally construct a new `PaymentAddress`=] with |details| and return the result.

PaymentShippingOption dictionary

        dictionary PaymentShippingOption {
          required DOMString id;
          required DOMString label;
          required PaymentCurrencyAmount amount;
          boolean selected = false;
        };
      

The {{PaymentShippingOption}} dictionary has members describing a shipping option. Developers can provide the user with one or more shipping options by calling the {{PaymentRequestUpdateEvent/updateWith()}} method in response to a change event.

id member
A string identifier used to reference this {{PaymentShippingOption}}. It MUST be unique for a given {{PaymentRequest}}.
label member
A human-readable string description of the item. The user agent SHOULD use this string to display the shipping option to the user.
amount member
A {{PaymentCurrencyAmount}} containing the monetary amount for the item.
selected member
A boolean. When true, it indicates that this is the default selected {{PaymentShippingOption}} in a sequence. User agents SHOULD display this option by default in the user interface.

PaymentComplete enum

        enum PaymentComplete {
          "fail",
          "success",
          "unknown"
        };
      
"fail"
Indicates that processing of the payment failed. The user agent MAY display UI indicating failure.
"success"
Indicates the payment was successfully processed. The user agent MAY display UI indicating success.
"unknown"
The developer did not indicate success or failure and the user agent SHOULD NOT display UI indicating success or failure.

PaymentResponse interface

        [SecureContext, Exposed=Window]
        interface PaymentResponse : EventTarget  {
          [Default] object toJSON();

          readonly attribute DOMString requestId;
          readonly attribute DOMString methodName;
          readonly attribute object details;
          readonly attribute PaymentAddress? shippingAddress;
          readonly attribute DOMString? shippingOption;
          readonly attribute DOMString? payerName;
          readonly attribute DOMString? payerEmail;
          readonly attribute DOMString? payerPhone;

          [NewObject]
          Promise<undefined> complete(optional PaymentComplete result = "unknown");
          [NewObject]
          Promise<undefined> retry(optional PaymentValidationErrors errorFields = {});

          attribute EventHandler onpayerdetailchange;
        };
      

A {{PaymentResponse}} is returned when a user has selected a payment method and approved a payment request.

retry() method

The retry(|errorFields|) method MUST act as follows:

  1. Let |response:PaymentResponse| be [=this=].
  2. Let |request:PaymentRequest| be |response|.{{PaymentResponse/[[request]]}}.
  3. Let |document:Document| be |request|'s relevant global object's associated Document.
  4. If |document| is not [=Document/fully active=], then return a promise rejected with an {{"AbortError"}} {{DOMException}}.
  5. If |response|.{{PaymentResponse/[[complete]]}} is true, return a promise rejected with an {{"InvalidStateError"}} {{DOMException}}.
  6. If |response|.{{PaymentResponse/[[retryPromise]]}} is not null, return a promise rejected with an {{"InvalidStateError"}} {{DOMException}}.
  7. Set |request|.{{PaymentRequest/[[state]]}} to "[=state/interactive=]".
  8. Let |retryPromise:Promise| be a new promise.
  9. Set |response|.{{PaymentResponse/[[retryPromise]]}} to |retryPromise|.
  10. If |errorFields:PaymentValidationErrors| was passed:
    1. Optionally, show a warning in the developer console if any of the following are true:
      1. |request|.{{PaymentRequest/[[options]]}}.{{PaymentOptions/requestPayerName}} is false, and |errorFields|.{{PaymentValidationErrors/payer}}.{{PayerErrors/name}} is present.
      2. |request|.{{PaymentRequest/[[options]]}}.{{PaymentOptions/requestPayerEmail}} is false, and |errorFields|.{{PaymentValidationErrors/payer}}.{{PayerErrors/email}} is present.
      3. |request|.{{PaymentRequest/[[options]]}}.{{PaymentOptions/requestPayerPhone}} is false, and |errorFields|.{{PaymentValidationErrors/payer}}.{{PayerErrors/phone}} is present.
      4. |request|.{{PaymentRequest/[[options]]}}.{{PaymentOptions/requestShipping}} is false, and |errorFields|.{{PaymentValidationErrors/shippingAddress}} is present.
    2. If |errorFields:PaymentValidationErrors|.{{PaymentValidationErrors/paymentMethod}} member was passed, and if required by the specification that defines |response|.{{PaymentResponse/payment}}/a>, then [=converted to an IDL value|convert=] |errorFields|'s {{PaymentValidationErrors/paymentMethod}} member to an IDL value of the type specified there. Otherwise, [=converted to an IDL value|convert=] to {{object}}.
    3. Set |request|'s payment-relevant browsing context's payment request is showing boolean to false.
    4. If conversion results in a exception |error|:
      1. Reject |retryPromise| with |error|.
      2. Set user agent's payment request is showing boolean to false.
      3. Return.
    5. By matching the members of |errorFields| to input fields in the user agent's UI, indicate to the end user that something is wrong with the data of the payment response. For example, a user agent might draw the user's attention to the erroneous |errorFields| in the browser's UI and display the value of each field in a manner that helps the user fix each error. Similarly, if the {{PaymentValidationErrors/error}} member is passed, present the error in the user agent's UI. In the case where the value of a member is the empty string, the user agent MAY substitute a value with a suitable error message.
  11. Otherwise, if |errorFields| was not passed, signal to the end user to attempt to retry the payment. Re-enable any UI element that affords the end user the ability to retry accepting the payment request.
  12. If |document| stops being [=Document/fully active=] while the user interface is being shown, or no longer is by the time this step is reached, then:
    1. Close down the user interface.
    2. Set |request|'s payment-relevant browsing context's payment request is showing boolean to false.
  13. Finally, when |retryPromise| settles, set |response|.{{PaymentResponse/[[retryPromise]]}} to null.
  14. Return |retryPromise|.

    The |retryPromise| will later be resolved by the user accepts the payment request algorithm, or rejected by either the user aborts the payment request algorithm or abort the update.

PaymentValidationErrors dictionary

            dictionary PaymentValidationErrors {
              PayerErrors payer;
              AddressErrors shippingAddress;
              DOMString error;
              object paymentMethod;
            };
          
payer member
Validation errors related to the payer details.
shippingAddress member
Represents validation errors with the {{PaymentResponse}}'s {{PaymentResponse/shippingAddress}}.
error member
A general description of an error with the payment from which the user can attempt to recover. For example, the user may recover by retrying the payment. A developer can optionally pass the {{PaymentValidationErrors/error}} member on its own to give a general overview of validation issues, or it can be passed in combination with other members of the {{PaymentValidationErrors}} dictionary.
paymentMethod member
A payment method specific errors. See, for example, [[[?payment-method-basic-card]]]'s {{BasicCardErrors}}.

PayerErrors dictionary

          dictionary PayerErrors {
            DOMString email;
            DOMString name;
            DOMString phone;
          };
          

The {{PayerErrors}} is used to represent validation errors with one or more payer details.

Payer details are any of the payer's name, payer's phone number, and payer's email.

email member
Denotes that the payer's email suffers from a validation error. In the user agent's UI, this member corresponds to the input field that provided the {{PaymentResponse}}'s {{PaymentResponse/payerEmail}} attribute's value.
name member
Denotes that the payer's name suffers from a validation error. In the user agent's UI, this member corresponds to the input field that provided the {{PaymentResponse}}'s {{PaymentResponse/payerName}} attribute's value.
phone member
Denotes that the payer's phone number suffers from a validation error. In the user agent's UI, this member corresponds to the input field that provided the {{PaymentResponse}}'s {{PaymentResponse/payerPhone}} attribute's value.
            const payer = {
              email: "The domain is invalid.",
              phone: "Unknown country code.",
              name: "Not in database.",
            };
            await response.retry({ payer });
          

methodName attribute

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

details attribute

An {{object}} or dictionary generated by a payment method that a merchant can use to process or validate a transaction (depending on the payment method).

shippingAddress attribute

If the {{PaymentOptions/requestShipping}} member was set to true in the {{PaymentOptions}} passed to the {{PaymentRequest}} constructor, then {{PaymentRequest/shippingAddress}} will be the full and final shipping address chosen by the user.

shippingOption attribute

If the {{PaymentOptions/requestShipping}} member was set to true in the {{PaymentOptions}} passed to the {{PaymentRequest}} constructor, then {{PaymentRequest/shippingOption}} will be the {{PaymentShippingOption/id}} attribute of the selected shipping option.

payerName attribute

If the {{PaymentOptions/requestPayerName}} member was set to true in the {{PaymentOptions}} passed to the {{PaymentRequest}} constructor, then {{PaymentResponse/payerName}} will be the name provided by the user.

payerEmail attribute

If the {{PaymentOptions/requestPayerEmail}} member was set to true in the {{PaymentOptions}} passed to the {{PaymentRequest}} constructor, then {{PaymentResponse/payerEmail}} will be the email address chosen by the user.

payerPhone attribute

If the {{PaymentOptions/requestPayerPhone}} member was set to true in the {{PaymentOptions}} passed to the {{PaymentRequest}} constructor, then {{PaymentResponse/payerPhone}} will be the phone number chosen by the user.

requestId attribute

The corresponding payment request {{PaymentRequest/id}} that spawned this payment response.

complete() method

The {{PaymentResponse/complete()}} method is called after the user has accepted the payment request and the {{PaymentResponse/[[acceptPromise]]}} has been resolved. Calling the {{PaymentResponse/complete()}} method tells the user agent that the payment interaction is over (and SHOULD cause any remaining user interface to be closed).

After the payment request has been accepted and the {{PaymentResponse}} returned to the caller, but before the caller calls {{PaymentResponse/complete()}}, the payment request user interface remains in a pending state. At this point the user interface SHOULD NOT offer a cancel command because acceptance of the payment request has been returned. However, if something goes wrong and the developer never calls {{PaymentResponse/complete()}} then the user interface is blocked.

For this reason, implementations MAY impose a timeout for developers to call {{PaymentResponse/complete()}}. If the timeout expires then the implementation will behave as if {{PaymentResponse/complete()}} was called with no arguments.

The {{PaymentResponse/complete()}} method MUST act as follows:

  1. Let |response:PaymentResponse| be [=this=].
  2. If |response|.{{PaymentResponse/[[complete]]}} is true, return a promise rejected with an {{"InvalidStateError"}} {{DOMException}}.
  3. If |response|.{{PaymentResponse/[[retryPromise]]}} is not null, return a promise rejected with an {{"InvalidStateError"}} {{DOMException}}.
  4. Let |promise:Promise| be a new promise.
  5. Set |response|.{{PaymentResponse/[[complete]]}} to true.
  6. Return |promise| and perform the remaining steps in parallel.
  7. If |document| stops being [=Document/fully active=] while the user interface is being shown, or no longer is by the time this step is reached, then:
    1. Close down the user interface.
    2. Set |request|'s payment-relevant browsing context's payment request is showing boolean to false.
  8. Otherwise:
    1. Close down any remaining user interface. The user agent MAY use the value |result| to influence the user experience.
    2. Set |request|'s payment-relevant browsing context's payment request is showing boolean to false.
    3. Resolve |promise| with undefined.

onpayerdetailchange attribute

Allows a developer to handle "payerdetailchange" events.

Internal Slots

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

Internal Slot Description (non-normative)
[[\complete]] Is true if the request for payment has completed (i.e., {{PaymentResponse/complete()}} was called, or there was a fatal error that made the response not longer usable), or false otherwise.
[[\request]] The {{PaymentRequest}} instance that instantiated this {{PaymentResponse}}.
[[\retryPromise]] Null, or a {{Promise}} that resolves when a user accepts the payment request or rejects if the user aborts the payment request.

Permissions Policy integration

This specification defines a policy-controlled permission identified by the string "payment". Its default allowlist is 'self'.

Events

Summary

Event name Interface Dispatched when… Target
shippingaddresschange {{PaymentRequestUpdateEvent}} The user provides a new shipping address. {{PaymentRequest}}
shippingoptionchange {{PaymentRequestUpdateEvent}} The user chooses a new shipping option. {{PaymentRequest}}
payerdetailchange {{PaymentRequestUpdateEvent}} The user changes the payer name, the payer email, or the payer phone (see payer detail changed algorithm). {{PaymentResponse}}
paymentmethodchange {{PaymentMethodChangeEvent}} The user chooses a different payment method within a payment handler. {{PaymentRequest}}

PaymentMethodChangeEvent interface

          [SecureContext, Exposed=Window]
          interface PaymentMethodChangeEvent : PaymentRequestUpdateEvent {
            constructor(DOMString type, optional PaymentMethodChangeEventInit eventInitDict = {});
            readonly attribute DOMString methodName;
            readonly attribute object? methodDetails;
          };
        

methodDetails attribute

When getting, returns the value it was initialized with. See {{PaymentMethodChangeEventInit/methodDetails}} member of {{PaymentMethodChangeEventInit}} for more information.

methodName attribute

When getting, returns the value it was initialized with. See {{PaymentMethodChangeEventInit/methodName}} member of {{PaymentMethodChangeEventInit}} for more information.

PaymentMethodChangeEventInit dictionary

            dictionary PaymentMethodChangeEventInit : PaymentRequestUpdateEventInit {
              DOMString methodName = "";
              object? methodDetails = null;
            };
          
methodName member
A string representing the payment method identifier.
methodDetails member
An object representing some data from the payment method, or null.

PaymentRequestUpdateEvent interface

          [SecureContext, Exposed=Window]
          interface PaymentRequestUpdateEvent : Event {
            constructor(DOMString type, optional PaymentRequestUpdateEventInit eventInitDict = {});
            undefined updateWith(Promise<PaymentDetailsUpdate> detailsPromise);
          };
        

The {{PaymentRequestUpdateEvent}} enables developers to update the details of the payment request in response to a user interaction.

Constructor

The {{PaymentRequestUpdateEvent}}'s {{PaymentRequestUpdateEvent/constructor(type, eventInitDict)}} MUST act as follows:

  1. Let |event:PaymentRequestUpdateEvent| be the result of calling the [=Event/constructor=] of {{PaymentRequestUpdateEvent}} with |type| and |eventInitDict|.
  2. Set |event|.{{PaymentRequestUpdateEvent/[[waitForUpdate]]}} to false.
  3. Return |event|.

updateWith() method

The {{PaymentRequestUpdateEvent/updateWith()}} with |detailsPromise:Promise| method MUST act as follows:

  1. Let |event:PaymentRequestUpdateEvent| be [=this=].
  2. If |event|'s {{Event/isTrusted}} attribute is false, then [=exception/throw=] an {{"InvalidStateError"}} {{DOMException}}.
  3. If |event|.{{PaymentRequestUpdateEvent/[[waitForUpdate]]}} is true, then [=exception/throw=] an {{"InvalidStateError"}} {{DOMException}}.
  4. If |event|'s [=Event/target=] is an instance of {{PaymentResponse}}, let |request:PaymentRequest| be |event|'s [=Event/target=]'s {{PaymentResponse/[[request]]}}.
  5. Otherwise, let |request:PaymentRequest| be the value of |event|'s [=Event/target=].
  6. Assert: |request| is an instance of {{PaymentRequest}}.
  7. If |request|.{{PaymentRequest/[[state]]}} is not "[=state/interactive=]", then [=exception/throw=] an {{"InvalidStateError"}} {{DOMException}}.
  8. If |request|.{{PaymentRequest/[[updating]]}} is true, then [=exception/throw=] an {{"InvalidStateError"}} {{DOMException}}.
  9. Set |event|'s [=Event/stop propagation flag=] and [=Event/stop immediate propagation flag=].
  10. Set |event|.{{PaymentRequestUpdateEvent/[[waitForUpdate]]}} to true.
  11. Let |pmi:URL or null| be null.
  12. If |event| has a {{PaymentMethodChangeEvent/methodName}} attribute, set |pmi| to the {{PaymentMethodChangeEvent/methodName}} attribute's value.
  13. Run the update a PaymentRequest's details algorithm with |detailsPromise|, |request|, and |pmi|.

Internal Slots

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

Internal Slot Description (non-normative)
[[\waitForUpdate]] A boolean indicating whether an {{PaymentRequestUpdateEvent/updateWith()}}-initiated update is currently in progress.

PaymentRequestUpdateEventInit dictionary

            dictionary PaymentRequestUpdateEventInit : EventInit {};
          

Algorithms

When the internal slot [[\state]] of a {{PaymentRequest}} object is set to "[=state/interactive=]", the user agent will trigger the following algorithms based on user interaction.

Can make payment algorithm

The can make payment algorithm checks if the user agent supports making payment with the payment methods with which the {{PaymentRequest}} was constructed.

  1. Let |request:PaymentRequest| be the {{PaymentRequest}} object on which the method was called.
  2. If |request|.{{PaymentRequest/[[state]]}} is not "[=state/created=]", then return a promise rejected with an {{"InvalidStateError"}} {{DOMException}}.
  3. Optionally, at the top-level browsing context's discretion, return a promise rejected with a {{"NotAllowedError"}} {{DOMException}}.

    This allows user agents to apply heuristics to detect and prevent abuse of the calling method for fingerprinting purposes, such as creating {{PaymentRequest}} objects with a variety of supported payment methods and triggering the can make payment algorithm on them one after the other. For example, a user agent may restrict the number of successful calls that can be made based on the top-level browsing context or the time period in which those calls were made.

  4. Let |hasHandlerPromise:Promise| be a new promise.
  5. Return |hasHandlerPromise|, and perform the remaining steps in parallel.
  6. For each |paymentMethod| tuple in |request|. {{PaymentRequest/[[serializedMethodData]]}}:
    1. Let |identifier| be the first element in the |paymentMethod| tuple.
    2. If the user agent has a payment handler that supports handling payment requests for |identifier|, resolve |hasHandlerPromise| with true and terminate this algorithm.
  7. Resolve |hasHandlerPromise| with false.

Shipping address changed algorithm

The shipping address changed algorithm runs when the user provides a new shipping address. It MUST run the following steps:

  1. Let |request:PaymentRequest| be the {{PaymentRequest}} object that the user is interacting with.
  2. Queue a task on the user interaction task source to run the following steps:
    1. The |redactList| limits the amount of personal information about the recipient that the API shares with the merchant.

      For merchants, the resulting {{PaymentAddress}} object provides enough information to, for example, calculate shipping costs, but, in most cases, not enough information to physically locate and uniquely identify the recipient.

      Unfortunately, even with the |redactList|, recipient anonymity cannot be assured. This is because in some countries postal codes are so fine-grained that they can uniquely identify a recipient.

    2. Let |redactList:list| be the empty list. Set |redactList| to « "organization", "phone", "recipient", "addressLine" ».
    3. Let |address:PaymentAddress| be the result of running the steps to create a `PaymentAddress` from user-provided input with |redactList|.
    4. Set |request|.{{PaymentRequest/shippingAddress}} to |address|.
    5. Run the PaymentRequest updated algorithm with |request| and "shippingaddresschange".

Shipping option changed algorithm

The shipping option changed algorithm runs when the user chooses a new shipping option. It MUST run the following steps:

  1. Let |request:PaymentRequest| be the {{PaymentRequest}} object that the user is interacting with.
  2. Queue a task on the user interaction task source to run the following steps:
    1. Set the {{PaymentRequest/shippingOption}} attribute on |request| to the id string of the {{PaymentShippingOption}} provided by the user.
    2. Run the PaymentRequest updated algorithm with |request| and "shippingoptionchange".

Payment method changed algorithm

A payment handler MAY run the payment method changed algorithm when the user changes payment method with |methodDetails|, which is a dictionary or an {{object}} or null, and a |methodName|, which is a DOMString that represents the payment method identifier of the payment handler the user is interacting with.

When the user selects or changes a payment method (e.g., a credit card), the {{PaymentMethodChangeEvent}} includes redacted billing address information for the purpose of performing tax calculations. Redacted attributes include, but are not limited to, address line, dependent locality, organization, phone number, and recipient.

  1. Let |request:PaymentRequest| be the {{PaymentRequest}} object that the user is interacting with.
  2. Queue a task on the user interaction task source to run the following steps:
    1. Assert: |request|.{{PaymentRequest/[[updating]]}} is false. Only one update can take place at a time.
    2. Assert: |request|.{{PaymentRequest/[[state]]}} is "[=state/interactive=]".
    3. Fire an event named "paymentmethodchange" at |request| using {{PaymentMethodChangeEvent}}, with its {{PaymentMethodChangeEvent/methodName}} attribute initialized to |methodName|, and its {{PaymentMethodChangeEvent/methodDetails}} attribute initialized to |methodDetails|.

PaymentRequest updated algorithm

The PaymentRequest updated algorithm is run by other algorithms above to fire an event to indicate that a user has made a change to a {{PaymentRequest}} called |request| with an event name of |name|:

  1. Assert: |request|.{{PaymentRequest/[[updating]]}} is false. Only one update can take place at a time.
  2. Assert: |request|.{{PaymentRequest/[[state]]}} is "[=state/interactive=]".
  3. Let |event:PaymentRequestUpdateEvent| be the result of creating an event using the {{PaymentRequestUpdateEvent}} interface.
  4. Initialize |event|'s {{Event/type}} attribute to |name|.
  5. Dispatch |event| at |request|.
  6. If |event|.{{PaymentRequestUpdateEvent/[[waitForUpdate]]}} is true, disable any part of the user interface that could cause another update event to be fired.
  7. Otherwise, set |event|.{{PaymentRequestUpdateEvent/[[waitForUpdate]]}} to true.

Payer detail changed algorithm

The user agent MUST run the payer detail changed algorithm when the user changes the |payer name|, or the |payer email|, or the |payer phone| in the user interface:

  1. Let |request:PaymentRequest| be the {{PaymentRequest}} object that the user is interacting with.
  2. If |request|.{{PaymentRequest/[[response]]}} is null, return.
  3. Let |response:PaymentResponse| be |request|.{{PaymentRequest/[[response]]}}.
  4. Queue a task on the user interaction task source to run the following steps:
    1. Assert: |request|.{{PaymentRequest/[[updating]]}} is false.
    2. Assert: |request|.{{PaymentRequest/[[state]]}} is "[=state/interactive=]".
    3. Let |options:PaymentOptions| be |request|.{{PaymentRequest/[[options]]}}.
    4. If |payer name| changed and |options|.{{PaymentOptions/requestPayerName}} is true:
      1. Set |response|.{{PaymentResponse/payerName}} attribute to |payer name|.
    5. If |payer email| changed and |options|.{{PaymentOptions/requestPayerEmail}} is true:
      1. Set |response|.{{PaymentResponse/payerEmail}} to |payer email|.
    6. If |payer phone| changed and |options|.{{PaymentOptions/requestPayerPhone}} is true:
      1. Set |response|.{{PaymentResponse/payerPhone}} to |payer phone|.
    7. Let |event:PaymentRequestUpdateEvent| be the result of creating an event using {{PaymentRequestUpdateEvent}}.
    8. Initialize |event|'s {{Event/type}} attribute to "payerdetailchange".
    9. Dispatch |event| at |response|.
    10. If |event|.{{PaymentRequestUpdateEvent/[[waitForUpdate]]}} is true, disable any part of the user interface that could cause another change to the payer details to be fired.
    11. Otherwise, set |event|.{{PaymentRequestUpdateEvent/[[waitForUpdate]]}} to true.

User accepts the payment request algorithm

The user accepts the payment request algorithm runs when the user accepts the payment request and confirms that they want to pay. It MUST queue a task on the user interaction task source to perform the following steps:

  1. Let |request:PaymentRequest| be the {{PaymentRequest}} object that the user is interacting with.
  2. If the |request|.{{PaymentRequest/[[updating]]}} is true, then terminate this algorithm and take no further action. The user agent user interface SHOULD ensure that this never occurs.
  3. If |request|.{{PaymentRequest/[[state]]}} is not "[=state/interactive=]", then terminate this algorithm and take no further action. The user agent user interface SHOULD ensure that this never occurs.
  4. If the {{PaymentOptions/requestShipping}} value of |request|.{{PaymentRequest/[[options]]}} is true, then if the {{PaymentRequest/shippingAddress}} attribute of |request| is null or if the {{PaymentRequest/shippingOption}} attribute of |request| is null, then terminate this algorithm and take no further action. The user agent SHOULD ensure that this never occurs.
  5. Let |isRetry:boolean| be true if |request|.{{PaymentRequest/[[response]]}} is not null, false otherwise.
  6. Let |response:PaymentResponse| be |request|.{{PaymentRequest/[[response]]}} if |isRetry| is true, or a new {{PaymentResponse}} otherwise.
  7. If |isRetry| if false, initialize the newly created |response|:
    1. Set |response|.{{PaymentResponse/[[request]]}} to |request|.
    2. Set |response|.{{PaymentResponse/[[retryPromise]]}} to null.
    3. Set |response|.{{PaymentResponse/[[complete]]}} to false.
    4. Set the {{PaymentResponse/requestId}} attribute value of |response| to the value of |request|.{{PaymentRequest/[[details]]}}.{{PaymentDetailsInit/id}}.
    5. Set |request|.{{PaymentRequest/[[response]]}} to |response|.
  8. Let |handler:payment handler| be |request|.{{PaymentRequest/[[handler]]}}.
  9. Set the {{PaymentResponse/methodName}} attribute value of |response| to the payment method identifier of |handler|.
  10. Set the {{PaymentResponse/details}} attribute value of |response| to an object resulting from running the |handler|'s steps to respond to a payment request.
  11. If the {{PaymentOptions/requestShipping}} value of |request|.{{PaymentRequest/[[options]]}} is false, then set the {{PaymentResponse/shippingAddress}} attribute value of |response| to null. Otherwise:
    1. Let |redactList:list| be the empty list.
    2. Let |shippingAddress:PaymentAddress| be the result of create a `PaymentAddress` from user-provided input with |redactList|.
    3. Set the {{PaymentResponse/shippingAddress}} attribute value of |response| to |shippingAddress|.
    4. Set the {{PaymentResponse/shippingAddress}} attribute value of |request| to |shippingAddress|.
  12. If the {{PaymentOptions/requestShipping}} value of |request|.{{PaymentRequest/[[options]]}} is true, then set the {{PaymentResponse/shippingOption}} attribute of |response| to the value of the {{PaymentResponse/shippingOption}} attribute of |request|. Otherwise, set it to null.
  13. If the {{PaymentOptions/requestPayerName}} value of |request|.{{PaymentRequest/[[options]]}} is true, then set the {{PaymentResponse/payerName}} attribute of |response| to the payer's name provided by the user, or to null if none was provided. Otherwise, set it to null.
  14. If the {{PaymentOptions/requestPayerEmail}} value of |request|.{{PaymentRequest/[[options]]}} is true, then set the {{PaymentResponse/payerEmail}} attribute of |response| to the payer's email address provided by the user, or to null if none was provided. Otherwise, set it to null.
  15. If the {{PaymentOptions/requestPayerPhone}} value of |request|.{{PaymentRequest/[[options]]}} is true, then set the {{PaymentResponse/payerPhone}} attribute of |response| to the payer's phone number provided by the user, or to null if none was provided. When setting the {{PaymentResponse/payerPhone}} value, the user agent SHOULD format the phone number to adhere to [[E.164]].
  16. Set |request|.{{PaymentRequest/[[state]]}} to "[=state/closed=]".
  17. If |isRetry| is true, resolve |response|.{{PaymentResponse/[[retryPromise]]}} with undefined. Otherwise, resolve |request|.{{PaymentRequest/[[acceptPromise]]}} with |response|.

User aborts the payment request algorithm

The user aborts the payment request algorithm runs when the user aborts the payment request through the currently interactive user interface. It MUST queue a task on the user interaction task source to perform the following steps:

  1. Let |request:PaymentRequest| be the {{PaymentRequest}} object that the user is interacting with.
  2. If |request|.{{PaymentRequest/[[state]]}} is not "[=state/interactive=]", then terminate this algorithm and take no further action. The user agent user interface SHOULD ensure that this never occurs.
  3. Set |request|.{{PaymentRequest/[[state]]}} to "[=state/closed=]".
  4. Set |request|'s payment-relevant browsing context's payment request is showing boolean to false.
  5. Let |error| be an {{"AbortError"}} {{DOMException}}.
  6. Let |response:PaymentResponse| be |request|.{{PaymentRequest/[[response]]}}.
  7. If |response| is not null:
    1. Set |response|.{{PaymentResponse/[[complete]]}} to true.
    2. Assert: |response|.{{PaymentResponse/[[retryPromise]]}} is not null.
    3. Reject |response|.{{PaymentResponse/[[retryPromise]]}} with |error|.
  8. Otherwise, reject |request|.{{PaymentRequest/[[acceptPromise]]}} with |error|.
  9. Abort the current user interaction and close down any remaining user interface.

Update a PaymentRequest's details algorithm

The update a PaymentRequest's details algorithm takes a {{PaymentDetailsUpdate}} |detailsPromise|, a {{PaymentRequest}} |request|, and |pmi| that is either a DOMString or null (a payment method identifier). The steps are conditional on the |detailsPromise| settling. If |detailsPromise| never settles then the payment request is blocked. The user agent SHOULD provide the user with a means to abort a payment request. Implementations MAY choose to implement a timeout for pending updates if |detailsPromise| doesn't settle in a reasonable amount of time.

In the case where a timeout occurs, or the user manually aborts, or the payment handler decides to abort this particular payment, the user agent MUST run the user aborts the payment request algorithm.

  1. Set |request|.{{PaymentRequest/[[updating]]}} to true.
  2. In parallel, disable the user interface that allows the user to accept the payment request. This is to ensure that the payment is not accepted until the user interface is updated with any new details.
  3. Upon rejection of |detailsPromise|:
    1. Abort the update with |request| and an {{"AbortError"}} {{DOMException}}.
  4. Upon fulfillment of |detailsPromise| with value |value|:
    1. Let |details:PaymentDetailsUpdate| be the result of [=converted to an IDL value|converting=] |value| to a {{PaymentDetailsUpdate}} dictionary. If this [=exception/throw=] an exception, abort the update with |request| and with the thrown exception.
    2. Let |serializedModifierData| be an empty list.
    3. Let |selectedShippingOption| be null.
    4. Let |shippingOptions| be an empty sequence<{{PaymentShippingOption}}>.
    5. Validate and canonicalize the details:
      1. If the {{PaymentDetailsUpdate/total}} member of |details| is present, then:
        1. Check and canonicalize total amount |details|.{{PaymentDetailsUpdate/total}}.{{PaymentItem/amount}}. If an exception is thrown, then abort the update with |request| and that exception.
      2. If the {{PaymentDetailsBase/displayItems}} member of |details| is present, then for each |item| in |details|.{{PaymentDetailsBase/displayItems}}:
        1. Check and canonicalize amount |item|.{{PaymentItem/amount}}. If an exception is thrown, then abort the update with |request| and that exception.
      3. If the {{PaymentDetailsBase/shippingOptions}} member of |details| is present, and |request|.{{PaymentRequest/[[options]]}}.{{PaymentOptions/requestShipping}} is true, then:
        1. Let |seenIDs| be an empty set.
        2. For each |option| in |details|.{{PaymentDetailsBase/shippingOptions}}:
          1. Check and canonicalize amount |option|.{{PaymentShippingOption/amount}}. If an exception is thrown, then abort the update with |request| and that exception.
          2. If |seenIDs|[|option|.{{PaymentShippingOption/id}] exists, then abort the update with |request| and a {{TypeError}}.
          3. Append |option|.{{PaymentShippingOption/id}} to |seenIDs|.
          4. Append |option| to |shippingOptions|.
          5. If |option|.{{PaymentShippingOption/selected}} is true, then set |selectedShippingOption| to |option|.{{PaymentShippingOption/id}}.
      4. If the {{PaymentDetailsBase/modifiers}} member of |details| is present, then:
        1. Let |modifiers| be the sequence |details|.{{PaymentDetailsBase/modifiers}}.
        2. Let |serializedModifierData| be an empty list.
        3. For each {{PaymentDetailsModifier}} |modifier| in |modifiers|:
          1. Run the steps to validate a payment method identifier with |modifier|.{{PaymentDetailsModifier/supportedMethods}}. If it returns false, then abort the update with |request| and a {{RangeError}} exception. Optionally, inform the developer that the payment method identifier is invalid.
          2. If the {{PaymentDetailsModifier/total}} member of |modifier| is present, then:
            1. Check and canonicalize total amount |modifier|.{{PaymentDetailsModifier/total}}.{{PaymentItem/amount}}. If an exception is thrown, then abort the update with |request| and that exception.
          3. If the {{PaymentDetailsModifier/additionalDisplayItems}} member of |modifier| is present, then for each {{PaymentItem}} |item| in |modifier|.{{PaymentDetailsModifier/additionalDisplayItems}}:
            1. Check and canonicalize amount |item|.{{PaymentItem/amount}}. If an exception is thrown, then abort the update with |request| and that exception.
          4. If the {{PaymentDetailsModifier/data}} member of |modifier| is missing, let |serializedData| be null. Otherwise, let |serializedData| be the result of JSON-serializing |modifier|.{{PaymentDetailsModifier/data}} into a string. If JSON-serializing throws an exception, then abort the update with |request| and that exception.
          5. Add |serializedData| to |serializedModifierData|.
          6. Remove the {{PaymentDetailsModifier/data}} member of |modifier|, if it is present.
    6. If the {{PaymentDetailsUpdate/paymentMethodErrors}} member is present and |identifier| is not null:
      1. If required by the specification that defines the |pmi|, then [=converted to an IDL value|convert=] {{PaymentDetailsUpdate/paymentMethodErrors}} to an IDL value.
      2. If conversion results in a exception |error|, abort the update with |error|.
      3. The payment handler SHOULD display an error for each relevant erroneous field of {{PaymentDetailsUpdate/paymentMethodErrors}}.
    7. Update the {{PaymentRequest}} using the new details:
      1. If the {{PaymentDetailsUpdate/total}} member of |details| is present, then:
        1. Set |request|.{{PaymentRequest/[[details]]}}.{{PaymentDetailsInit/total}} to |details|.{{PaymentDetailsUpdate/total}}.
      2. If the {{PaymentDetailsBase/displayItems}} member of |details| is present, then:
        1. Set |request|.{{PaymentRequest/[[details]]}}.{{PaymentDetailsBase/displayItems}} to |details|.{{PaymentDetailsBase/displayItems}}.
      3. If the {{PaymentDetailsBase/shippingOptions}} member of |details| is present, and |request|.{{PaymentRequest/[[options]]}}.{{PaymentOptions/requestShipping}} is true, then:
        1. Set |request|.{{PaymentRequest/[[details]]}}.{{PaymentDetailsBase/shippingOptions}} to |shippingOptions|.
        2. Set the value of |request|'s {{PaymentRequest/shippingOption}} attribute to |selectedShippingOption|.
      4. If the {{PaymentDetailsBase/modifiers}} member of |details| is present, then:
        1. Set |request|.{{PaymentRequest/[[details]]}}.{{PaymentDetailsBase/modifiers}} to |details|.{{PaymentDetailsBase/modifiers}}.
        2. Set |request|.{{PaymentRequest/[[serializedModifierData]]}} to |serializedModifierData|.
      5. If |request|.{{PaymentRequest/[[options]]}}.{{PaymentOptions/requestShipping}} is true, and |request|.{{PaymentRequest/[[details]]}}.{{PaymentDetailsBase/shippingOptions}} is empty, then the developer has signified that there are no valid shipping options for the currently-chosen shipping address (given by |request|'s {{PaymentRequest/shippingAddress}}).

        In this case, the user agent SHOULD display an error indicating this, and MAY indicate that the currently-chosen shipping address is invalid in some way. The user agent SHOULD use the {{PaymentDetailsUpdate/error}} member of |details|, if it is present, to give more information about why there are no valid shipping options for that address.

        Further, if |details|["{{PaymentDetailsUpdate/shippingAddressErrors}}"] member is present, the user agent SHOULD display an error specifically for each erroneous field of the shipping address. This is done by matching each present member of the {{AddressErrors}} to a corresponding input field in the shown user interface.

        Similarly, if |details|["{{payerErrors}}"] member is present and |request|.{{PaymentRequest/[[options]]}}'s {{PaymentOptions/requestPayerName}}, {{PaymentOptions/requestPayerEmail}}, or {{PaymentOptions/requestPayerPhone}} is true, then display an error specifically for each erroneous field.

        Likewise, if |details|.{{PaymentDetailsUpdate/paymentMethodErrors}} is present, then display errors specifically for each erroneous input field for the particular payment method.

  5. Set |request|.{{PaymentRequest/[[updating]]}} to false.
  6. Update the user interface based on any changed values in |request|. Re-enable user interface elements disabled prior to running this algorithm.

Abort the update

To abort the update with a {{PaymentRequest}} |request| and exception |exception|:

  1. Optionally, show an error message to the user when letting them know an error has occurred.
  2. Abort the current user interaction and close down any remaining user interface.
  3. Queue a task on the user interaction task source to perform the following steps:
    1. Set |request|'s payment-relevant browsing context's payment request is showing boolean to false.
    2. Set |request|.{{PaymentRequest/[[state]]}} to "[=state/closed=]".
    3. Let |response:PaymentResponse| be |request|.{{PaymentRequest/[[response]]}}.
    4. If |response| is not null, then:
      1. Set |response|.{{PaymentResponse/[[complete]]}} to true.
      2. Assert: |response|.{{PaymentResponse/[[retryPromise]]}} is not null.
      3. Reject |response|.{{PaymentResponse/[[retryPromise]]}} with |exception|.
    5. Otherwise, reject |request|.{{PaymentRequest/[[acceptPromise]]}} with |exception|.
    6. Set |request|.{{PaymentRequest/[[updating]]}} to false.
  4. Abort the algorithm.

Abort the update runs when there is a fatal error updating the payment request, such as the supplied |detailsPromise| rejecting, or its fulfillment value containing invalid data. This would potentially leave the payment request in an inconsistent state since the developer hasn't successfully handled the change event.

Consequently, the {{PaymentRequest}} moves to a "[=state/closed=]" state. The error is signaled to the developer through the rejection of the [[\acceptPromise]], i.e., the promise returned by {{PaymentRequest/show()}}.

Similarly, abort the update occurring during {{PaymentResponse/retry()}} causes the {{PaymentResponse/[[retryPromise]]}} to reject, and the corresponding {{PaymentRequest}}'s {{PaymentRequest/[[complete]]}} internal slot will be set to true (i.e., it can no longer be used).

Privacy and Security Considerations

User protections with show() method

To help ensure that users do not inadvertently share sensitive credentials with an origin, this API requires that PaymentRequest's {{PaymentRequest/show()}} method be invoked while the relevant {{Window}} has [=transient activation=] (e.g., via a click or press).

To avoid a confusing user experience, this specification limits the user agent to displaying one at a time via the {{PaymentRequest/show()}} method. In addition, the user agent can limit the rate at which a page can call {{PaymentRequest/show()}}.

Secure contexts

The API defined in this specification is only exposed in secure contexts. In practice, this means that this API is only available over HTTPS. This is to limit the possibility of payment method data (e.g., credit card numbers) being sent in the clear.

Cross-origin payment requests

It is common for merchants and other payees to delegate checkout and other e-commerce activities to payment service providers through an iframe. This API supports payee-authorized cross-origin iframes through [[HTML]]'s [^iframe/allow^] attribute.

Payment handlers have access to both the origin that hosts the iframe and the origin of the iframe content (where the {{PaymentRequest}} initiates).

Encryption of data fields

The {{PaymentRequest}} API does not directly support encryption of data fields. Individual payment methods may choose to include support for encrypted data but it is not mandatory that all payment methods support this.

How user agents match payment handlers

As part of {{PaymentRequest/show()}}, the user agent typically displays a list of matching payment handlers that satisfy the payment methods accepted by the merchant and other conditions. Matching can take into account payment method information provided as input to the API, information provided by the payment method owner, the payment handlers registered by the user, user preferences, and other considerations.

For security reasons, a user agent can limit matching (in {{PaymentRequest/show()}} and {{PaymentRequest/canMakePayment()}}) to payment handlers from the same origin as a URL payment method identifier.

Data usage

Payment method owners establish the privacy policies for how user data collected for the payment method may be used. Payment Request API sets a clear expectation that data will be used for the purposes of completing a transaction, and user experiences associated with this API convey that intention. It is the responsibility of the payee to ensure that any data usage conforms to payment method policies. For any permitted usage beyond completion of the transaction, the payee should clearly communicate that usage to the user.

Exposing user information

The user agent MUST NOT share information about the user with a developer (e.g., the shipping address) without user consent.

The user agent MUST NOT share the values of the {{PaymentDetailsBase/displayItems}} member or {{PaymentDetailsModifier/additionalDisplayItems}} member with a third-party payment handler without user consent.

The {{PaymentMethodChangeEvent}} enables the payee to update the displayed total based on information specific to a selected payment method. For example, the billing address associated with a selected payment method might affect the tax computation (e.g., VAT), and it is desirable that the user interface accurately display the total before the payer completes the transaction. At the same time, it is desirable to share as little information as possible prior to completion of the payment. Therefore, when a payment method defines the steps for when a user changes payment method, it is important to minimize the data shared via the {{PaymentMethodChangeEvent}}'s {{PaymentMethodChangeEvent/methodDetails}} attribute. Requirements and approaches for minimizing shared data are likely to vary by payment method and might include:

Where sharing of privacy-sensitive information might not be obvious to users (e.g., when [=payment handler/payment method changed algorithm | changing payment methods =]), it is RECOMMENDED that user agents inform the user of exactly what information is being shared with a merchant.

canMakePayment() protections

The {{PaymentRequest/canMakePayment()}} method provides feature detection for different payment methods. It may become a fingerprinting vector if in the future, a large number of payment methods are available. purposes. User agents are expected to protect the user from abuse of the method. For example, user agents can reduce user fingerprinting by:

For rate-limiting the user agent might look at repeated calls from:

These rate-limiting techniques intend to increase the cost associated with repeated calls, whether it is the cost of managing multiple eTLDs or the user experience friction of opening multiple windows (tabs or pop-ups).

Accessibility Considerations

For the user-facing aspects of Payment Request API, implementations integrate with platform accessibility APIs via form controls and other input modalities. Furthermore, to increase the intelligibility of total, shipping addresses, and contact information, implementations format data according to system conventions.

Dependencies

This specification relies on several other underlying specifications.

ISO 3366-2
Country subdivision name and country subdivision code element are defined in [[ISO3166-2]].
ECMAScript
The terms internal slot, and JSON.stringify are defined by [[ECMASCRIPT]].

The term JSON-serialize applied to a given object means to run the algorithm specified by the original value of the JSON.stringify function on the supplied object, passing the supplied object as the sole argument, and return the resulting string. This can throw an exception.

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

Although this specification is primarily targeted at web browsers, it is feasible that other software could also implement this specification in a conforming manner.

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.

Acknowledgements

This specification was derived from a report published previously by the Web Platform Incubator Community Group.

Changelog

Changes from between CR2 until now:

Changes from between CR1 and CR2: