diff --git a/index.html b/index.html index 7cd5b9b..73ef54b 100644 --- a/index.html +++ b/index.html @@ -173,12 +173,13 @@

Dependencies

is defined by the Payment Request Architecture document [[PAYMENT-ARCH]]. --> -
HTML5
The terms global object,origin, queue a task, - browsing context, and top-level browsing - context are defined by [[!HTML5]].
- + is defined by [[!ECMA-262-2015]]. +
DOM4
-
The Event type and the terms fire +
+

DOMException and the following DOMException types from [[!DOM4]] are used:

@@ -222,30 +236,55 @@

Dependencies

+ + + + + + + + +
Type Message (optional)
AbortErrorThe operation was aborted
InvalidStateError The object is in an invalid state
SecurityError The operation is only supported in a secure context
OperationErrorThe operation failed for an operation-specific reason.
WebIDL
-
When this specification says to throw an error, the +
+ + exceptions. + --> +

The following DOMException types from [[!WEBIDL]] are used:

+ + + + + + + + + +
TypeMessage (optional)
NotAllowedErrorThe request is not allowed by the user agent or the + platform in the current context.
+
Secure Contexts
-
The term secure context is defined by the Secure +
The terms secure context is defined by the Secure Contexts specification [[!POWERFUL-FEATURES]].
URL
The URL concept and URL parser are @@ -256,8 +295,17 @@

Dependencies

Context and Network Error are defined in [[!FETCH]].
Service Workers
-
The term service worker is defined in - [[SERVICE-WORKERS]].
+
The terms + service worker + service worker registration, + active worker, + installing worker, + waiting worker, + handle functional event, + extend lifetime promises, + and + scope URL + are defined in [[SERVICE-WORKERS]].
@@ -307,7 +355,7 @@

Registration and Unregistration

that the user has not yet registered.
  • When registration is desired it might happen in a variety of ways:
  • @@ -336,7 +384,7 @@

    Registration and Unregistration

    Payment App Identification

    @@ -410,20 +457,43 @@

    Definitions

    data-oninclude="restrictReferences">

    Updates needed for payment method option definition.

    - +

    Payment App Implementation Technology

    -
    browser-based payment app
    -
    a payment app that runs in a browser.
    +
    + browser-based payment app
    +
    a payment app that runs in a browser. Browser-based + payment apps may elect to display a user interface, or to operate without + any user interaction. This decision is made at runtime, and may vary based + on criteria of the app's choosing (such as how long it has been since the + user last authenticated themselves).
    -
    headless Web payment app
    -
    a payment app that has no user interface.
    - -
    native payment app
    +
    native payment app
    a payment app built with the operating system default technology stack that uses non-Web technologies.
    +
    ignored payment app
    +
    Need a definition for ignored payment app
    + +
    + payment app identifier
    +
    + A unique identifier for a payment app (e.g., from a payment + method manifest file). This specification defines thses identifiers + as to be service worker scope URLs. As such, they are not + expected to be dereferenced. +
    +
    payment app window
    +
    + A service worker WindowClient used by browser-based + payment apps to interact with the user when doing so is necessary to + complete the payment transaction. +

    The Web Payments Working Group intends for this specification to apply to any payment app that the may be invoked by the user agent, whatever technologies have been used to implement the payment app.

    +
    +

    Payment App Registration States

    registered payment app
    @@ -440,6 +510,8 @@

    Payment App Registration States

    A registered payment app with at least one enabled payment method.
    +
    +

    Payment App Selection States

    matching payment app
    @@ -463,11 +535,13 @@

    Payment App Selection States

    selected payment app
    the payment app whose option has been selected by the user to make a payment, but not yet invoked.
    -
    invoked payment app
    -
    the selected payment app that the user agent has invoked (executed) and passed payment app input data.
    +
    invoked payment app
    +
    the selected payment app that the user agent has invoked (executed) and passed payment app input data.
    -

    Payment Options

    +
    +
    +

    Payment Options

    We need a clearer introduction to the concept of a payment option, and how it relates to payment apps.

    @@ -487,6 +561,8 @@

    Payment Options

    the payment option selected by the user to handle the payment request.
    +
    +

    Payment Method Support

    supported payment method
    @@ -509,6 +585,8 @@

    Payment Method Support

    +
    +

    Payment App Invocation and Response Data

    payment app request data
    @@ -524,165 +602,366 @@

    Payment App Invocation and Response Data

    the Payment Request API as the payment response.
    -
    -

    Payment App Registration, Updates and Unregistration

    +
    +
    +

    Overview of Service-Worker-Based Approach

    -

    To support registration and unregistration, the user agent provides primitives that may be called from within a Web - page.

    +

    In this specification we use service workers + to connect browsers with browser-based payment apps. We do so for + several reasons:

    -

    Payment apps are registered with the user agent through a call to the register() method of the API. - The input to the registration process consists of:

    -
      -
    1. a payment_app_id, a URL that identifies the app.
    2. -
    3. a request_url which identifies the - endpoint where the user agent submits payment requests.
    4. -
    5. a sequence of PaymentOption - dictionaries to provide information displayed to the user to facilitate payment app selection.
    6. -
    +
      +
    • + Service workers provide a cleanly isolated execution context that allows + the payment app to run with clean separation from the + payee website. +
    • +
    • We can leverage the ways in which service workers already + handle lifecycle management (registration, update, and removal of + payment apps), enable offline use, etc.
    • +
    • We can leverage service worker origins for security, especially + when used in conjunction with origins associated with payment + method identifiers.
    • +
    -

    - See issue 20 about whether we should have two identifiers (or one) and expectations for dereferencing. -

    +

    + The use of service workers restricts browser-based payment apps so + that they must run only in secure contexts. The introduction of this + restriction is deliberate, due to the sensitivity of the role that payment + apps play. +

    -

    - See issue 33 and discussion about how to organize registration data. Can or should it be part of a "payment method" manifest? What is relationship to appmanifest for specifying icons, etc.? -

    - +

    Here is the flow envisioned by this document:

    +
      +
    1. Through registration, a service worker is created and + associated with payment methods + (and associated metadata).
    2. +
    3. When the payment request API is called, the browser + displays a list of registered service workers associated + with matching payment methods (along with any other payment apps + that may be available to the browser).
    4. +
    5. When the user selects a browser-based payment + app, the corresponding service worker is activated, and + it receives a PaymentRequestEvent.
    6. +
    7. Once active, the payment app performs whatever steps are + necessary to authenticate the user, process the payment, and return + payment information to the payee. If interaction with the user + is necessary, the payment app can open a payment app + window for that purpose.
    8. +
    9. Finally, once the payment app is finished with its processing, it + resolves a promise passed to it in the event. This causes the + Promise<PaymentResponse> returned from + PaymentRequest.show() to resolve.
    10. +
    +
    +
    +

    + Payment App Registration +

    -

    The PaymentOption dictionary

    -
    -dictionary PaymentOption {
    -    DOMString label;
    -    DOMString icon;
    -    sequence<DOMString> enabled_methods;
    -};
    -
    +

    + Extensions to the ServiceWorkerRegistration interface +

    +

    + The Service Worker specification defines a ServiceWorkerRegistration interface + [[!SERVICE-WORKERS]], which this specification extends. +

    +
    +        partial interface ServiceWorkerRegistration {
    +          readonly attribute PaymentAppManager paymentAppManager;
    +        };
    +      
    +
    +
    +

    + PaymentAppManager interface +

    +
    +      interface PaymentAppManager {
    +        Promise<void> setManifest(PaymentAppManifest manifest);
    +        Promise<PaymentAppManifest> getManifest ();
    +      };
    +      
    +
    +

    + PaymentAppManager.setManifest() +

    +

    + The setManifest method is used to enable a + service worker to process payment requests, and to set the + properties associated with the payment app. +

    +

    The setManifest method, when invoked, MUST run the + following steps or their equivalent: +

      +
    1. + Let promise be a new Promise. +
    2. +
    3. + Return promise and asynchronously perform the remaining + steps. +
    4. +
    5. + If the current settings object is not a secure + context, reject promise with a + DOMException whose name is + "SecurityError" and terminate these steps. +
    6. +
    7. + Let manifest be the value of the manifest + argument. +
    8. +
    9. + Let registration be the + PaymentAppManager's associated service + worker registration. +
    10. +
    11. If registration has no active worker, run the following substeps: +
        +
      1. If registration has no installing worker and no waiting + worker, reject promise with a DOMException whose + name is "InvalidStateError" and terminate these steps. +
      2. +
      3. Wait for the installing worker or waiting worker of + registration to become its active worker. +
      4. +
      5. If registration fails to activate either worker, reject + promise with a DOMException whose name is + "InvalidStateError" and terminate these steps. +
      6. +
      7. Once registration has an active worker, proceed with the steps + below. +
      8. +
      +
    12. +
    13. + Ask the user whether they allow the payment app to be + registered to handle the indicated payment methods + unless a prearranged trust relationship applies or the user has + already granted or denied permission explicitly for this payment + app. +
    14. +
    15. + If permission is not granted, reject promise with a + DOMException whose name is + "NotAllowedError" and terminate these steps. +
    16. +
    17. + Register the payment app with the browser for future use, + associating manifest's label and + icon with the payment app for user reference. +
    18. +
    19. + For each PaymentAppOption present in the + options field of the manifest: +
        +
      1. + Add a new payment option to the payment app's + registration, associating it with the PaymentAppOption + label and icon fields. +
      2. +
      3. + For each payment method indicated in the + PaymentAppOption's enabledMethods + field, associate the payment option and the payment app + with the payment method for future use. +
      4. +
      +
    20. +
    21. + Resolve promise with undefined. +
    22. +
    +
    +
    +

    + PaymentAppManager.getManifest() +

    +

    + The getManifest method is used to + retreive the properties associated with a registered + payment app. +

    +

    The getManifest method, when invoked, MUST run the + following steps or their equivalent: +

      +
    1. + Let promise be a new Promise. +
    2. +
    3. + Return promise and asynchronously perform the remaining + steps. +
    4. +
    5. + If the current settings object is not a secure + context, reject promise with a + DOMException whose name is + "SecurityError" and terminate these steps. +
    6. +
    7. + If there is no PaymentAppManifest associated with the + Service Worker, reject promise with a + DOMException whose name is "AbortError" + and teminate these steps. +
    8. +
    9. + Retrieve the PaymentAppManifest associated with the + Service Worker. +
    10. +
    11. + Let manifest be the retreved PaymentAppManifest. +
    12. +
    13. + Resolve promise with manifest. +
    14. +
    +
    +
    +
    +

    + PaymentAppManifest interface +

    +
    +      dictionary PaymentAppManifest {
    +        DOMString label;
    +        DOMString? icon;
    +        sequence<PaymentAppOption> options;
    +      };
    +      
    label member
    - The label member is a - string that represents the lable for this option as it is usually displayed to the user when - selecting a payment app. + The label member is a string that represents the + label for this payment app as it is usually displayed + to the user.
    icon member
    +
    + The iconmember defines an icon for this + payment app as it is usually displayed to the user. +
    - Need to define how an icon would be represented in this data. Url? Data URL? Size options? + Need to define how an icon would be represented in this data. + Url? Data URL? Size options?
    -
    enabled_methods member
    +
    options member
    - The enabled_methods member - lists the payment method identifiers of the payment methods enabled by this option. + The options + member lists the payment method identifiers of the + payment methods enabled by this option.
    -
    -
    -

    PaymentApp.register()

    - -

    This section will need to be updated in light of issue 33 and the draft proposal from Adam Roach.

    - -

    - The register method is used to register, update, or unregister a payment app with a user agent. -

    -

    - Payment apps are uniquely identifed by their payment_app_id. To update the payment options - offered by a registered payment app, a website calls register() using the same - payment_app_id as was used when registering the app and pass in the new sequence of options. To - unregister a payment app the website calls register() with an empty sequence of options. -

    -
    -partial interface PaymentApp {
    -    static Promise<void> register (URLString payment_app_id, URLString request_url, sequence<PaymentOption> payment_options);
    -};
    -
    +
    +
    +

    + PaymentAppOption dictionary +

    +
    +      dictionary PaymentAppOption {
    +        DOMString label;
    +        DOMString? icon;
    +        DOMString id;
    +        sequence<DOMString> enabledMethods;
    +      };
    +      
    -
    payment_app_id parameter
    -
    An identifier for the payment app. If a payment app is already installed with the same request_url - the registered payment options will be replaced with the new sequence of options provided.
    -
    request_url parameter
    +
    label member
    -

    - The request_url identifies a service that accepts - payment request messages via HTTP POST. -

    -

    - The request_url is the API entry point for the payment - app and MUST have the same origin as the web application that attempts - to register the payment app. -

    -

    Native apps could be registered from Web pages, thus allowing the association of an origin to a native app.

    + The label member is a string that represents the + label for this option as it is usually displayed to the user + when selecting a payment app.
    -
    payment_options parameter
    +
    icon member
    +
    + Need to define how an icon would be represented in this data. + Url? Data URL? Size options? +
    +
    id member
    -

    - The payment_options is a sequence of one or more - options that can be presented to the user for selection when the [[!PAYMENT-REQUEST-API]] is invoked. -

    + The id member is an identifier, unique + within the PaymentAppManifest, that will be passed to the + payment app to indicate which PaymentAppOption the user + selected. +
    +
    enabledMethods member
    +
    + The enabledMethods + member lists the payment method identifiers of the + payment methods enabled by this option.
    - -

    The register method MUST act as follows:

    -
      -
    1. Let promise be a new Promise.
    2. -
    3. Return promise and asynchronously perform the remaining steps.
    4. -
    5. If the origin of request_url does not match the origin of the current document - reject promise with a SecurityError.
    6. -
    7. Let registered_app be the registered payment app where - registered_app@[[\request_url]] is equal to request_url.
    8. -
    9. If registered_app is null, let new_app be a new registered payment app and: -
        -
      1. Set new_app@[[\\request_url]] to request_url.
      2. -
      3. Set new_app@[[\\payment_options]] to payment_options.
      4. -
      5. Store new_app as a registered payment app.
      6. -
      -
    10. -
    11. Else -
        -
      1. If payment_options is an empty sequences, unregister registered_app.
      2. -
      3. Else, set registered_app@[[\\payment_options]] to payment_options.
      4. -
      -
    12. -
    13. Resolve promise with undefined.
    14. -
    -

    The following example shows how to register a payment app:

    -
    -             PaymentApp.register(
    -               "https://example.com/paymentapp/v1",
    -               [
    -                {
    -                    label: "Visa ending ****4756",
    -                    icon: "...",
    -                    enabled_methods: ["https://webpayments.org/payment-methods/card/visa"]
    -                },
    -                {
    -                    label: "My Bob Pay Account",
    -                    icon: "...",
    -                    enabled_methods: ["https://bobpay.com/"]
    -                },
    -                {
    -                    label: "Add new credit/debit card to ExampleApp",
    -                    icon: "...",
    -                    enabled_methods: [
    -                        "https://webpayments.org/payment-methods/card/visa",
    -                        "https://webpayments.org/payment-methods/card/mastercard",
    -                        "https://webpayments.org/payment-methods/card/amex" ]
    -                }
    -               ]
    -               ).then(function(value) {
    -                  console.log("Installed https://example.com/paymentapp/v1 payment app"); // Success!
    -                }, function(reason) {
    -                  console.log(reason); // Error!
    -               });
    -         
    +
    +

    + Registration Example +

    +

    The following example shows how to register a browser-based payment + app:

    +
    +         navigator.serviceWorker.register('/exampleapp.js')
    +         .then(function(registration) {
    +           return registration.paymentAppManager.setManifest({
    +             label: "ExampleApp",
    +             icon: "...",
    +             options: [
    +               {
    +                 label: "Visa ending ****4756",
    +                 icon: "...",
    +                 id: "dc2de27a-ca5e-4fbd-883e-b6ded6c69d4f",
    +                 enabledMethods: ["basic-card#visa"]
    +               },
    +               {
    +                 label: "My Bob Pay Account: john@example.com",
    +                 icon: "...",
    +                 id: "c8126178-3bba-4d09-8f00-0771bcfd3b11",
    +                 enabledMethods: ["https://bobpay.com/"]
    +               },
    +               {
    +                 label: "Add new credit/debit card to ExampleApp",
    +                 icon: "...",
    +                 id: "new-card",
    +                 enabledMethods: [
    +                   "basic-card#visa",
    +                   "basic-card#mastercard",
    +                   "basic-card#amex"
    +               }
    +             ]
    +           });
    +         }).then(function() {
    +           console.log("Installed payment app from /paymentapp.js"); // Success!
    +         }).catch(function(error) {
    +           console.log(error);
    +         });
    +     
    +

    + The Editors will update the payment method identifier syntax in this + and other examples to align with [[!METHOD-IDENTIFIERS]], once a final + format has been agreed upon. +

    +
    + +
    +

    + Native App Registration +

    - What, if anything, should we say about registering native payment apps? + What, if anything, should we say about registering native payment + apps? +

    +

    + Native payment apps could be registered from Web pages (is this + true?), thus allowing the association of an origin to a native + payment app.

    +

    Payment App Matching

    - We anticipate that the Payment Method Identifier specification will define the PMI matching algorithm. This + We anticipate that [[!METHOD-IDENTIFIERS]] will define the PMI matching algorithm. This specification will explain how to invoke that algorithm using data available from the Payment Request API input and payment method information aggregated from:

    @@ -701,7 +980,7 @@

    Selectable App Information Display

    payment request. How will these be ordered when they are displayed to the user, where do recommended apps fit in to the order and how do we treat apps that are both registered and recommended?

    -

    +

    What information is needed by the user agent to display selectable apps/options? This needs to be captured during registration.

    @@ -754,7 +1033,8 @@

    Examples of Ordering of Selectable Payment Apps

    Selection by the User

      -
    • The user agent MUST enable the user to select any displayed payment option.
    • +
    • The user agent MUST enable the user to + select any displayed payment app.
    • If the user selects an unregistered recommended payment app, the user agent SHOULD offer the user an opportunity to register it.
    @@ -763,189 +1043,532 @@

    Selection by the User

    Payment App Invocation, Display and Response

    - Once the user has selected a payment option, the user agent is responsible for preparing payment app request data, - invoking the payment app, providing the request data to the payment app, and returning the payment app response - through the Payment Request API. + Once the user has selected a payment option, the user agent is + responsible for preparing payment app request data, invoking the + payment app, providing the request data to the payment app, and + returning the payment app response through the Payment Request API.

    -

    This specification will describe a service worker approach to launching apps; see issue 33 and the draft proposal from Adam Roach. - -

    The Working Group should discuss how to capture good practice for payment app development, with examples that illustrate common payment flows, different platforms, etc.. A public resource (e.g., on github) could foster contributions of good practice information from the developer community.

    -
    +

    Payment App Request Data

    -

    - Let P be the intersection of payment methods supported by the payee and enabled by the selected payment option. - Send the data corresponding to P, as well as any global transaction data (total, etc.), and payee origin information (per issue 27) to the payment app. - The details depend on discussions about the shape of the Payment Request API. -

    -

    - Once we have finalized the shape of the data that is returned from the matching process we must define a - deterministic algorithm for serializing this as JSON for transmission over the wire. -

    -
    -
    -

    Payment App Invocation

    -

    - This section will need to be updated in light of issue 33 and the draft proposal from Adam Roach.

    - -

    The following algorithm is to be replace.

    - + The payment app request data is conveyed using the following + dictionary: +
    +      dictionary PaymentAppRequestData {
    +        DOMString origin;
    +        sequence<PaymentMethodData> methodData;
    +        PaymentItem total;
    +        sequence<PaymentDetailsModifier> modifiers;
    +      };
    +    
    +
    +
    origin attribute
    +
    + This attribute a string that indicates the origin of the + payee web page. It MUST be formatted according to the + "Unicode + Serialization of an Origin" algorithm defined in section 6.1 of + [[!RFC6454]]. +
    +
    methodData attribute
    +
    + This attribute contains PaymentMethodData + dictionaries containing the payment method identifiers for the + payment methods that the web site accepts and any associated + payment method specific data. + It is populated from the + PaymentRequest using the Method Data Population Algorithm + defined below. +
    +
    total attribute
    +
    + This attribute indicates the total amount being requested for payment. + It is initialized with a structured clone of the + total field of the PaymentDetails provided + when the corresponding PaymentRequest object was instantiated. +
    +
    modifiers attribute
    +
    + This sequence of PaymentDetailsModifier dictionaries + contains modifiers for particular payment method identifiers (e.g., + if the payment amount or currency type varies based on a + per-payment-method basis). It is populated from the + PaymentRequest using the Modifiers Population Algorithm + defined below. +
    +
    +
    +

    + Method Data Population Algorithm +

    +

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

      -
    1. Let payment request be the string that is the - outcome of the steps to prepare the payment app request data. +
    2. + Set registeredMethods to an empty set.
    3. -
    4. Let app_url be the request_url member of the selected payment app.
    5. -
    6. Let request be a new [[!FETCH]] Request, whose members are set to the following values: - - - - - - - - - - - - - - - - - - - - - -
      MemberValue
      URLThe value of app_url
      methodThe value "POST"
      header list - - - - - -
      Accepttext/html;application/json
      -
      bodyThe value of payment request
      +
    7. + For each PaymentAppOption option in the + payment app's PaymentAppManifest, add all entries in + option.enabledMethods to + registeredMethods. +
    8. +
    9. + Create a new empty Sequence.
    10. -
    11. Await the result of performing a fetch with - request, letting response be the result.
    12. -
    13. If response is a network error, terminate this algorithm.
    14. -
    15. Switch on response's MIME type: -
        -
      • if text/html go to the section below on Payment App Display
      • -
      • else if application/json then go to the section below on Payment App Response
      • -
      • otherwise reject the Promise created during PaymentRequest.show with an error.
      • -
      +
    16. + Set dataList to the newly created Sequence.
    17. +
    18. + For each item in + PaymentRequest@[[\methodData]] in the + corresponding payment request, perform the following steps: +
        +
      1. + Set inData to the item under consideration. +
      2. +
      3. + Set commonMethods to the set intersection of + inData.supportedMethods + and registeredMethods. +
      4. +
      5. + If commonMethods is empty, skip the remaining substeps + and move on to the next item (if any). +
      6. +
      7. + Create a new PaymentMethodData object. +
      8. +
      9. + Set outData to the newly created + PaymentMethodData. +
      10. +
      11. + Set outData.supportedMethods to + a list containing the members of commonMethods. +
      12. +
      13. + Set outData.data to a structured clone of + inData.data. +
      14. +
      15. + Append outData to dataList. +
      16. +
      +
    19. + Set methodData to dataList. +
    -

    - We should get input form Web Platform and WebAppSec on how to best construct this request. -

    -

    - A remote payment app may be getting payment authorization from the user via different channel (such as a - mobile app) so we need the app to be able to send keep-alive messages back to the browser while this is in - progress. -

    -

    - If the user cancels the handling of the payment request then the app should return a response - with an appropriate error/response code. Will this be done through the HTTP status code or defined as a value - that is set in the response. +

    +
    +

    + Modifiers Population Algorithm +

    +

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

    -
    -

    Communication mail fail at various points in the flow; see design considerations for some ideas for managing this.

    -
    +
      +
    1. + Set registeredMethods to an empty set. +
    2. +
    3. + For each PaymentAppOption option in the + payment app's PaymentAppManifest, add all entries in + option.enabledMethods to + registeredMethods. +
    4. +
    5. + Create a new empty Sequence. +
    6. +
    7. + Set modifierList to the newly created + Sequence. +
    8. +
    9. + For each item in + PaymentRequest@[[\paymentDetails]].modifiers + in the corresponding payment request, perform the following steps: +
        +
      1. + Set inModifier to the item under consideration. +
      2. +
      3. + Set commonMethods to the set intersection of + inModifier.supportedMethods + and registeredMethods. +
      4. +
      5. + If commonMethods is empty, skip the remaining substeps + and move on to the next item (if any). +
      6. +
      7. + Create a new PaymentDetailsModifier object. +
      8. +
      9. + Set outModifier to the newly created + PaymentDetailsModifier. +
      10. +
      11. + Set outModifier.supportedMethods to + a list containing the members of commonMethods. +
      12. +
      13. + Set outModifier.total to a structured + clone of inModifier.total. +
      14. +
      15. + Set outModifier.additionalDisplayItems + to a structured clone of + inModifier.additionalDisplayItems. +
      16. +
      17. + Append outModifier to modifierList. +
      18. +
      +
    10. + Set modifiers to modifierList. +
    11. +
    +
    -

    Payment App Display

    -

    - The Working Group is still discussing how payment apps are displayed for user interaction. -

    +

    Payment App Invocation

    - Payment apps are invoked via an HTTP request. If the resulting HTTP response body is text/html then the user agent - MUST render this content for the user. -

    -
      -
    1. The user agent MUST render the Response in a new secure context. See issue 24.
    2. -
    3. The user agent MUST make the PaymentApp.respond() method available to the page that is - rendered.
    4. -
    -

    - This specification does not otherwise prescribe how user agents render the payment app. This may be in a new tab/window, in - a special modal dialogue specifically for this purpose, in an iframe embedded in the web page of the payee - website or any other mechanism the user agent defines. + Payment apps are invoked when a payee requests a payment + by calling PaymentRequest.show() and the user selects a + payment app (or has one implicitly selected by previously established + user preferences). If the user selects a browser-based payment + app to service the request, the service worker corresponding + to that application receives an event with the + PaymentAppRequestData containing information about the payment + being requested. The event also contains a function that allows the + payment app to provide a payment response back to the + payee. Ths process is formally described in the following + sections.

    -

    The PaymentAppResponse dictionary

    -

    - A payment app that runs in the user agent responds to a payment request by returning a new - PaymentAppResponse via the PaymentApp.respond() method. -

    -
    -dictionary PaymentAppResponse {
    -    DOMString method;
    -    object details;
    -};
    -
    -
    -
    method member
    -
    - The method member is the payment method - identifer of the payment method used to handle the payment request. -
    -
    details member
    -
    - The details member contains the payment method - specific data that is expected in a payment response. -
    -
    +

    + Extension to ServiceWorkerGlobalScope +

    +

    + The Service Worker specification defines a + ServiceWorkerGlobalScope interface [[!SERVICE-WORKERS]], + which this specification extends. +

    +
    +        partial interface ServiceWorkerGlobalScope {
    +          attribute EventHandler onpaymentrequest;
    +        };
    +      
    +
    +
    onpaymentrequest attribute
    +
    + The onpaymentrequest attribute is an event handler + whose corresponding event handler event type is + paymentrequest. +
    +
    + +

    The PaymentRequestEvent interface represents a received + payment request.

    -

    PaymentApp.respond()

    +

    + The paymentrequest Event +

    - The payment app submits the PaymentAppResponse by calling the respond() method and - passing this in as a parameter. -

    -
    -partial interface PaymentApp {
    -    static void respond (PaymentAppResponse response);
    -};
    -
    -

    The respond method MUST act as follows:

    -

    - This should be a light algorithm that simply hands off to the steps defined in Payment Request + The PaymentRequestEvent represents a received payment + request.

    -

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

    -
    -             PaymentApp.respond(
    -               "https://webpayments.org/payment-methods/card/visa",
    -               {
    -                 card_number :  "1232343451234",
    -                 expiry_month : "12",
    -                 expiry_year :  "2020",
    -                 cvv :          "123"
    -               });
    +      
    +      [Exposed=ServiceWorker]
    +      interface PaymentRequestEvent : ExtendableEvent {
    +        readonly attribute PaymentAppRequestData data;
    +        void respondWith((Promise<PaymentResponse>
    +        or PaymentResponse) r);
    +      };
           
    +
    +
    data attribute
    +
    + This attribute contains the payment app request data associated + with this payment request. +
    +
    respondWith method
    +
    + This method is used by the payment app to provide a + PaymentResponse when the payment successfully completes. +
    +
    +

    + Upon receiving a payment request by way of + PaymentRequest.show() and subsequent user selection of a + browser-based payment app, the user agent MUST run + the following steps or their equivalent: +

    +
      +
    1. Let registration be the service worker + registration corresponding to the browser-based payment app + selected by the user. +
    2. +
    3. If registration is not found, reject the Promise that + was created by PaymentRequest.show() with a + DOMException whose value "InvalidStateError" and + terminate these steps. +
    4. +
    5. Invoke the Handle Functional Event algorithm with a + service worker registration of registration and + callbackSteps set to the following steps: +
        +
      1. Set global to the global object that was provided + as an argument.
      2. +
      3. Create a trusted event, + e, that uses the + PaymentRequestEvent interface, with the + event type paymentrequest, which does not bubble, + is not cancelable, and has no default action.
      4. +
      5. Set the data attribute of + e to a new PaymentAppRequestData instance, + populated as described in + . +
      6. +
      7. Dispatch e to global.
      8. +
      9. Wait for all of the promises in the extend lifetime + promises of e to resolve.
      10. +
      11. If the payment app has not provided a payment app + response as described in , reject the Promise that + was created by PaymentRequest.show() with a + DOMException whose value "OperationError". +
      +
    6. +
    +

    + Payment App Display +

    +

    + Payment Apps that require user input can open a payment window using the + clients.openWindow() method defined in [[!SERVICE-WORKERS]]. + Absent user perferences that override such behavior, user interaction is + required during payment requests, in the form of payment app selection. As + a consquence, the user agent MUST treat a paymentrequest event as + user interaction for the purposes of determining whether the service + worker is allowed to open a window. +

    +

    + The actual rendering of a payment app window is a browser + implementation detail. While opening an entirely new window is possible, + it is more likely that the contents will be rendered in a way that makes + it more obvious that the interactions pertain to the payment + transaction. This is an area for potential user agent experimentation + and differentiation. The opening of a payment app window versus + other types of windows can be distinguished based on the event type the + user agent is using to grant permission to open a window. +

    +

    The remainder of this section is a non-normative explanation of how + the service worker WindowClient class can be used to interact + with users.

    +

    + Upon calling clients.openWindow(), the payment app receives a + Promise which resolves to a WindowClient. For the + purposes of this discussion, we will refer to this + WindowClient as client. The payment app can use + the client.postMessage() method to send messages + to the payment app window. +

    +

    + When a payment app window receives the message event + from the payment app, this event will contain a source + attribute which indicates the payment app's service worker. The payment + app window can then call source.postMessage() to send a + response to the payment app. Once the payment app window has complete its + interaction with the user, it closes the window and uses this + postMessage() call to return information to the payment app. +

    +

    + In order for this approach to work, we have to treat a + paymentrequest as permission to open a popup, which is a formal + property relied up on by [[!SERVICE-WORKERS]]. We need to be careful + that this does not become an end-run around exiting pop-up protections. +

    +

    + Do we want to define a new FrameType for payment app + windows? This requires input from someone with detailed knowledge of + service worker design. +

    +
    +

    Payment App Response

    - The user agent receives a response from the payment app in one of two forms:

    + The user agent receives a successful response from the payment app + through resolution of the Promise provided to the respondWith + function of the corresponding PaymentRequestEvent dictionary. + The application is expected to resolve the Promise with a + PaymentResponse dictionary instance containing the payment + response information. +

    +

    + When this Promise is resolved, the user agent MUST run the user + accepts the payment request algorithm as defined in + [[!PAYMENT-REQUEST-API]], replacing steps 6 and 7 with these steps or + their equiivalent: +

      -
    1. a JSON serialized - PaymentAppResponse sent as a response to the HTTP request that invoked the app
    2. -
    3. a - PaymentAppResponse dictionary instance submitted via the PaymentApp.respond() method
    4. +
    5. + Set appResponse to the PaymentResponse used to + resolve the PaymentRequestEvent.respondWith Promise. +
    6. +
    7. + If appResponse.methodName is not present or + not set to one of the values from + PaymentRequestEvent.data, reject the Promise + created by PaymentRequest.show() with DOMException + whose value "InvalidStateError" and terminate these steps. +
    8. +
    9. + Create a structured clone of + appResponse.methodName + and assign it to + response.methodName. +
    10. +
    11. + If appResponse.details is not present, + reject the Promise created by + PaymentRequest.show() with a DOMException whose + value is "InvalidStateError" and terminate these steps. +
    12. +
    13. + Create a structured clone of + appResponse.details + and assign it to + response.details. +
    -

    - The user agent MUST use this PaymentAppResponse to resolve the promise that was created - by PaymentRequest.show when the user agent executed the user accepts the payment request algorithm - as defined in [[!PAYMENT-REQUEST-API]]. -

    -

    - Some payment methods might require a back channel to guarantee payment response delivery (especially push payment methods). - Should it be part of the generic portion of paymentRequest and paymentResponse? +

    + The user agent receives a failure response from the payment app through + rejection of the Promise. The user agent MUST use the rejection reason + to reject the Promise that was created by + PaymentRequest.show(). +

    +

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

    +
    +      paymentRequestEvent.respondWith(new Promise(function(accept,reject) {
    +        /* ... processing may occur here ... */
    +        accept({
    +          methodName: "basic-card#visa",
    +          details: {
    +            card_number :  "1232343451234",
    +            expiry_month : "12",
    +            expiry_year :  "2020",
    +            cvv :          "123"
    +           }
    +        });
    +      });
    +    
    +

    + Some payment methods might require a back channel to guarantee payment + response delivery (especially push payment methods). Should it be part + of the generic portion of paymentRequest and paymentResponse? [Ed Note: + the "complete()" attribute of the "PaymentResponse" interface would serve + this purpose quite cleanly.]

    +
    +

    Example using HTTP POST

    +

    + This example codes shows how to use this API using a scheme a scheme in + which a POST is sent to a URL with the payment request as a + body. The response is allowed to be either application/json + (which is inferred to contain a payment response), or + text/html (which contains content to be rendered to the + user). +

    +
    +      var contentType;
    +      var paymentPromise;
    +      /* Handle payment request from a payee */
    +      self.addEventListener('paymentrequest', function(e) {
    +        paymentPromise = new Promise(function(accept, reject) {
    +          fetch("https://www.example.com/bobpay/process",
    +            { method: "POST",  body: JSON.stringify(e.data) })
    +          .then(function(response) {
    +            contentType = response.headers.get("content-type");
    +            if (!contentType) {
    +              throw new Error("No content type header");
    +            }
    +            return response.text();
    +          }).then(function(body) {
    +            if(contentType.indexOf("application/json") !== -1) {
    +              /* Respond to the payment request with the received body */
    +              accept(JSON.parse(body));
    +            } else if (contentType.indexOf("text/html") !== -1) { {
    +              /* Open a new payment window and populate it with the
    +                 document returned from the response */
    +              var url = "data:text/html;base64," + btoa(body);
    +              clients.openWindow(url).then(function(windowClient) {
    +                windowClient.postMessage(e.data);
    +              });
    +            } else {
    +              throw new Error("Unexpected value in content type header");
    +            }
    +          }).catch(function(err) {
    +            reject(err);
    +          });
    +        e.respondWith(paymentPromise);
    +      });
    +
    +      /* Handle payment response from a payment app window */
    +      self.addEventListener('message', function(e) {
    +        if (e.data.hasOwnProperty('name')) {
    +          paymentPromise.reject(e.data);
    +        } else {
    +          paymentPromise.resolve(e.data);
    +        }
    +      });
    +    
    +

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

    +
    +<html> <body> <form id="form">
    +<table>
    +  <tr><th>Card Number:</th><td><input name="card_number"></td></tr>
    +  <tr><th>Expiration Month:</th><td><input name="expiry_month"></td></tr>
    +  <tr><th>Expiration Year:</th><td><input name="expiry_year"></td></tr>
    +  <tr><th>CVV:</th><td><input name="cvv"></td></tr>
    +  <tr><th></th><td><input type="submit" value="Pay"></td></tr>
    +</table>
    +</form>
    +
    +<script>
    +window.addEventListener("message", function(e) {
    +  var form = document.getElementById("form");
    +  /* Note: message sent from payment app is available in e.data */
    +  form.onsubmit = function() {
    +    var details = {};
    +    ["card_number","expiry_month","expiry_year","cvv"].forEach(function(field) {
    +      details[field] = form.elements[field].value;
    +    });
    +    e.source.postMessage({
    +      methodName: "basic-card#visa",
    +      details: details
    +    });
    +    window.close();
    +  }
    +});
    +</script> </body> </html>
    +    
    + +
    +

    Security and Privacy Considerations