diff --git a/index.html b/index.html index 55c95b4..9a28d61 100644 --- a/index.html +++ b/index.html @@ -125,7 +125,7 @@

Introduction

  • How origins register their support for different payment methods with the user agent.
  • How origins provide information to faciliate the display - of payment options for selection by the user.
  • + of payment instruments for selection by the user.
  • How user agents invoke and exchange data with payment handlers from an origin.
  • @@ -197,6 +197,10 @@

    Dependencies

    InvalidStateError The object is in an invalid state + + NotFoundError + The object can not be found here. + SecurityError The operation is only supported in a secure @@ -270,7 +274,7 @@

    Overview of Handling Payment Requests

    supports a given payment method; these "capabilities" play a role in matching computations.
  • Information for organizing the display and grouping - of Options supported by the payment handler.
  • + of Instruments supported by the payment handler.
  • When the merchant calls Payment Request API (e.g., when @@ -282,16 +286,16 @@

    Overview of Handling Payment Requests

    capabilities are compared as part of determining whether there is a match.
  • The user agent displays matching payment handlers, - displaying and grouping Options according to information + displaying and grouping Instruments according to information (labels and icons) provided at registration or otherwise available from the Web app.
  • -
  • When the user selects an Option, the user agent fires the +
  • When the user selects an Instrument, the user agent fires the paymentrequest event in the service worker whose registration - included the payment handler that provided that Option. The + included the payment handler that provided that Instrument. The paymentrequest event includes some information from the PaymentRequest (defined in [[!PAYMENT-REQUEST-API]]) as well as additional information (e.g., origin and selected - Option).
  • + Instrument).
  • Once activated, the payment handler performs whatever steps are necessary to authenticate the user, process the payment, and return payment information to the payee. @@ -326,68 +330,158 @@

    Extensions to the ServiceWorkerRegistration [[!SERVICE-WORKERS]], which this specification extends.

             partial interface ServiceWorkerRegistration {
    -          readonly attribute PaymentAppManager paymentAppManager;
    +          readonly attribute PaymentManager paymentManager;
             };
           
    -
    -

    PaymentAppManager interface

    -
    -      interface PaymentAppManager {
    -        attribute PaymentAppOptions options;
    +    
    +

    PaymentManager interface

    +
    +      interface PaymentManager {
    +        attribute PaymentInstruments instruments;
             attribute PaymentWallets wallets;
           };
           
    -
    options attribute
    +
    instruments attribute
    - This attribute allows manipulation of payment options + This attribute allows manipulation of payment instruments associated with a payment handler. To be presented to the user as part of the payment request flow, payment handlers must have at least one registered payment - option, and that option needs to match the payment + Instrument, and that Instrument needs to match the payment methods and required capabilities specified by the payment request. -

    Is "options" the right term here? We - don't want this to get conflated with "PaymentOptions" in - the payment request specification. Other alternatives - might be "instruments" or "accounts."

    wallets attribute
    This attribute allows payment handlers to group payment - options together and provide a name and icon for such a + instruments together and provide a name and icon for such a group (e.g., to group together "business account" payment instruments separately from "personal account" payment instruments). The use of this grouping mechanism by payment handlers is completely optional. If payment handlers use - this grouping mechanism, then matching payment options that + this grouping mechanism, then matching payment instruments that do not appear in any groups should still be presented to users by the browser for selection.
    -
    -

    PaymentAppOptions interface

    -
    -      interface PaymentAppOptions {
    -          Promise<boolean>              delete(DOMString optionKey);
    -          Promise<PaymentAppOption> get(DOMString optionKey);
    +    
    +

    PaymentInstruments interface

    +
    +      interface PaymentInstruments {
    +          Promise<boolean>              delete(DOMString instrumentKey);
    +          Promise<PaymentInstrument> get(DOMString instrumentKey);
               Promise<sequence<DOMString>>  keys();
    -          Promise<boolean>              has(DOMString optionKey);
    -          Promise<void>                 set(DOMString optionKey,
    -                                              PaymentAppOption details);
    +          Promise<boolean>              has(DOMString instrumentKey);
    +          Promise<void>                 set(DOMString instrumentKey,
    +                                              PaymentInstrument details);
           };
           
    -

    Where it appears, The optionKey parameter is - a unique identifier for the option; it will be passed to the - payment handler to indicate the PaymentAppOption +

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

    -

    TODO: flesh out details after the WG reaches - general consensus on the shape of this API.

    +
    + +
    delete method
    +
    + When called, this method executes the following steps: +
      +
    1. Let p be a new promise.
    2. +
    3. Run the following steps in parallel:
    4. +
        +
      1. If the collection contains a PaymentInstrument + with a matching instrumentKey, remove it from + the collection and resolve p with true.
      2. +
      3. Otherwise, resolve p with false.
      4. +
      +
    5. Return p
    6. +
    +
    + +
    get method
    +
    + When called, this method executes the following steps: +
      +
    1. Let p be a new promise.
    2. +
    3. Run the following steps in parallel:
    4. +
        +
      1. If the collection contains a PaymentInstrument + with a matching instrumentKey, resolve p + with that PaymentInstrument. +
      2. Otherwise, reject p with a + DOMException whose value is + "NotFoundError". +
      +
    5. Return p
    6. +
    +
    + +
    keys method
    +
    + When called, this method executes the following steps: +
      +
    1. Let p be a new promise.
    2. +
    3. Run the following steps in parallel:
    4. +
        +
      1. Resolve p with a sequence that contains all + the instrumentKeys for the PaymentInstruments + contained in the collection. +
      +
    5. Return p
    6. +
    +
    + +
    has method
    +
    + When called, this method executes the following steps: +
      +
    1. Let p be a new promise.
    2. +
    3. Run the following steps in parallel:
    4. +
        +
      1. If the collection contains a PaymentInstrument + with a matching instrumentKey, resolve p + with true.
      2. +
      3. Otherwise, resolve p with false.
      4. +
      +
    5. Return p
    6. +
    +
    + +
    set method
    +
    + When called, this method executes the following steps: +
      +
    1. Let p be a new promise.
    2. +
    3. Run the following steps in parallel:
    4. +
        +
      1. If the collection contains a PaymentInstrument + with a matching instrumentKey, replace it with + the PaymentInstrument in details. +
      2. Otherwise, PaymentInstrument + insert the PaymentInstrument in details + as a new member of the collection and associate it with the + key instrumentKey. +
      3. Resolve p.
      4. +
      +
    5. Return p
    6. +
    +
    +
    + +
    +        return paymentManager.instruments.keys().then((keys) => {
    +          Promise.all(keys.map(paymentManager.instruments.delete(key)))
    +        });
    +      
    + +
    -
    -

    PaymentAppOption dictionary

    -
    -      dictionary PaymentAppOption {
    +    
    +

    PaymentInstrument dictionary

    +
    +      dictionary PaymentInstrument {
             required DOMString name;
             sequence<ImageObjects> icons;
             sequence<DOMString> enabledMethods;
    @@ -397,26 +491,26 @@ 

    PaymentAppOption dictionary

    name member
    The name member is a string that - represents the label for this payment option as it is + represents the label for this payment Instrument as it is usually displayed to the user.
    icons member
    The icons member is an array of image objects that can serve as iconic representations of the - payment option when presented to the user for + payment Instrument when presented to the user for selection.
    enabledMethods member
    The enabledMethods + "payment-instrument-enabled-methods">enabledMethods member lists the payment method identifiers of the - payment methods enabled by this option. + payment methods enabled by this Instrument.
    capabilities member
    The capabilities + "payment-instrument-capabilities">capabilities member contains a list of payment-method-specific capabilities that this payment handler is capable of - supporting for this option. For example, for the + supporting for this Instrument. For example, for the basic-card payment method, this object will consist of an object with two fields: one for supportedNetworks, and another for @@ -425,7 +519,7 @@

    PaymentAppOption dictionary

    PaymentWallets interface

    -
    +      
           interface PaymentWallets {
               Promise<boolean>              delete(DOMString walletKey);
               Promise<WalletDetails> get(DOMString walletKey);
    @@ -437,16 +531,103 @@ 

    PaymentWallets interface

    Where it appears, The walletKey parameter is a unique identifier for the wallet.

    -

    TODO: flesh out details after the WG reaches - general consensus on the shape of this API.

    +
    + +
    delete method
    +
    + When called, this method executes the following steps: +
      +
    1. Let p be a new promise.
    2. +
    3. Run the following steps in parallel:
    4. +
        +
      1. If the collection contains a WalletDetails + with a matching walletKey, remove it from + the collection and resolve p with true.
      2. +
      3. Otherwise, resolve p with false.
      4. +
      +
    5. Return p
    6. +
    +
    + +
    get method
    +
    + When called, this method executes the following steps: +
      +
    1. Let p be a new promise.
    2. +
    3. Run the following steps in parallel:
    4. +
        +
      1. If the collection contains a WalletDetails + with a matching walletKey, resolve p + with that WalletDetails. +
      2. Otherwise, reject p with a + DOMException whose value is + "NotFoundError". +
      +
    5. Return p
    6. +
    +
    + +
    keys method
    +
    + When called, this method executes the following steps: +
      +
    1. Let p be a new promise.
    2. +
    3. Run the following steps in parallel:
    4. +
        +
      1. Resolve p with a sequence that contains all + the walletKeys for the WalletDetailss + contained in the collection. +
      +
    5. Return p
    6. +
    +
    + +
    has method
    +
    + When called, this method executes the following steps: +
      +
    1. Let p be a new promise.
    2. +
    3. Run the following steps in parallel:
    4. +
        +
      1. If the collection contains a WalletDetails + with a matching walletKey, resolve p + with true.
      2. +
      3. Otherwise, resolve p with false.
      4. +
      +
    5. Return p
    6. +
    +
    + +
    set method
    +
    + When called, this method executes the following steps: +
      +
    1. Let p be a new promise.
    2. +
    3. Run the following steps in parallel:
    4. +
        +
      1. If the collection contains a WalletDetails + with a matching walletKey, replace it with + the WalletDetails in details. +
      2. Otherwise, WalletDetails + insert the WalletDetails in details + as a new member of the collection and associate it with the + key walletKey. +
      3. Resolve p.
      4. +
      +
    5. Return p
    6. +
    +
    + +
    +
    -

    WalletDetails interface

    +

    WalletDetails dictionary

           dictionary WalletDetails {
               required DOMString name;
               sequence<ImageObject> icons;
    -          required sequence<DOMString> optionKeys;
    +          required sequence<DOMString> instrumentKeys;
           };
           
    @@ -458,68 +639,111 @@

    WalletDetails interface

    The icons member is an array of image objects that can serve as iconic representations of the wallet when presented to the user for selection.
    -
    optionKeys member
    -
    TODO
    +
    instrumentKeys member
    +
    The instrumentKeys member is a + list of instrumentKeys from + PaymentManager.instruments, + indicating which PaymentInstrument objects are associated + with this Wallet, and should be displayed as being "contained in" + the wallet. While it is not generally good practice, there is no + restriction that prevents a PaymentInstrument from appearing in + more than one Wallet. +

    Registration Example

    The following example shows how to register a payment handler:

    +

    + Issue 94. The means for code requesting permission to handle + payments is not yet defined. The code below is based on one + potential model (a requestPermission() method on the + PaymentManager), + but this is likely to change. +

    -         navigator.serviceWorker.register('/exampleapp.js')
    -         .then(function(registration) {
    -           registration.paymentAppManager.options.set(
    -             "dc2de27a-ca5e-4fbd-883e-b6ded6c69d4f",
    -             {
    -               name: "Visa ending ****4756",
    -               enabledMethods: ["basic-card"],
    -               capabilities: {
    -                 supportedNetworks: ['visa'],
    -                 supportedTypes: ['credit']
    -               }
    -             });
    +        window.addEventListerner("DOMContentLoaded", async() => {
    +          const { registration } =
    +            await navigator.serviceWorker.register('/sw.js');
    +          if (!paymentManager) {
    +            return; // not supported, so bail out.
    +          }
    +          const state =
    +            await navigator.permissions.query({ name: "paymenthandler" });
     
    -           registration.paymentAppManager.options.set(
    -             "c8126178-3bba-4d09-8f00-0771bcfd3b11",
    -             {
    -               name: "My Bob Pay Account: john@example.com",
    -               enabledMethods: ["https://bobpay.com/"]
    -             });
    +          switch (state) {
    +            case "denied":
    +              return;
    +            case "prompt":
    +              // Note -- it's not clear how this should work yet; see Issue 94.
    +              const result = await registration.paymentManager.requestPermission();
    +              if (result === "denied") {
    +                return;
    +              }
    +              break;
    +          }
    +          // Excellent, we got it! Let's now set up the user's cards.
    +          await addInstruments(registration);
    +        }, { once: true });
     
    -           registration.paymentAppManager.options.set(
    -             "new-card",
    -             {
    -               name: "Add new credit/debit card to ExampleApp",
    -               enabledMethods: ["basic-card"],
    -               capabilities: {
    -                 supportedNetworks:
    -                   ['visa','mastercard','amex','discover'],
    -                 supportedTypes: ['credit','debit','prepaid']
    -               }
    -             });
    +        function addInstruments(registration) {
    +          const instrumentPromises = [
    +            registration.paymentManager.instruments.set(
    +              "dc2de27a-ca5e-4fbd-883e-b6ded6c69d4f",
    +              {
    +                name: "Visa ending ****4756",
    +                enabledMethods: ["basic-card"],
    +                capabilities: {
    +                  supportedNetworks: ['visa'],
    +                  supportedTypes: ['credit']
    +                }
    +              }),
     
    -           registration.paymentAppManager.wallets.set(
    -             "12a1b7e5-16c0-4c09-a312-9b191d08517b",
    -             {
    -               name: "Acme Bank Personal Accounts",
    -               icons: [
    -                        { src: "icon/lowres.webp",
    -                          sizes: "48x48",
    -                          type: "image/webp"
    -                        },
    -                        { src: "icon/lowres",
    -                          sizes: "48x48"
    -                        }
    -                      ],
    -               optionKeys: [
    -                   "dc2de27a-ca5e-4fbd-883e-b6ded6c69d4f",
    -                   "c8126178-3bba-4d09-8f00-0771bcfd3b11",
    -                   "new-card"
    -                 ]
    +            registration.paymentManager.instruments.set(
    +              "c8126178-3bba-4d09-8f00-0771bcfd3b11",
    +              {
    +                name: "My Bob Pay Account: john@example.com",
    +                enabledMethods: ["https://bobpay.com/"]
    +              }),
    +
    +            registration.paymentManager.instruments.set(
    +              "new-card",
    +              {
    +                name: "Add new credit/debit card to ExampleApp",
    +                enabledMethods: ["basic-card"],
    +                capabilities: {
    +                  supportedNetworks:
    +                    ['visa','mastercard','amex','discover'],
    +                  supportedTypes: ['credit','debit','prepaid']
    +                }
    +              }),
    +            ];
    +
    +            return Promise.all(instrumentPromises).then(() => {
    +              registration.paymentManager.wallets.set(
    +                "12a1b7e5-16c0-4c09-a312-9b191d08517b",
    +                {
    +                  name: "Acme Bank Personal Accounts",
    +                  icons: [
    +                           { src: "icon/lowres.webp",
    +                             sizes: "48x48",
    +                             type: "image/webp"
    +                           },
    +                           { src: "icon/lowres",
    +                             sizes: "48x48"
    +                           }
    +                         ],
    +                  instrumentKeys: [
    +                      "dc2de27a-ca5e-4fbd-883e-b6ded6c69d4f",
    +                      "c8126178-3bba-4d09-8f00-0771bcfd3b11",
    +                      "new-card"
    +                    ]
    +                });
                  });
    -         });
    +          };
          

    The Editors will update the payment method identifier syntax in @@ -528,8 +752,8 @@

    Registration Example

    upon.

    -
    -

    Origin and Option Display for Selection

    +
    +

    Origin and Instrument Display for Selection

    After applying the matching algorithm defined in Payment Request API, the user agent displays a list of matching origins for the user to make a selection. This specification includes a @@ -566,9 +790,9 @@

    Ordering of Payment Handlers

    -

    Display of Options

    +

    Display of Instruments

    The user agent MUST enable - the user to select any displayed Option.

    + the user to select any displayed Instrument.

    • At a minimum, we expect user agents to display an icon and label for each matching origin to help the user make a @@ -582,17 +806,17 @@

      Display of Options

      card's brand and the last four digits of the card to remind the user which cards the origin knows about.
    -

    Issue 98. There has been pushback to always requiring - display of options (e.g., on a mobile devices). User agents - can incrementally show options. Or user agents can return an - empty option ID and it becomes the payment app's - responsibility to display options to the user.

    + display of instruments (e.g., on a mobile devices). User agents + can incrementally show instruments. Or user agents can return an + empty Instrument ID and it becomes the payment app's + responsibility to display instruments to the user.

    -

    Grouping of Options

    -

    At times, the same origin may wish to group options with +

    Grouping of Instruments

    +

    At times, the same origin may wish to group instruments with greater flexibility and granularity than merely "by origin." These use cases include:

      @@ -602,48 +826,50 @@

      Grouping of Options

      business wallet vs personal wallet)
    • Multiple instruments held with a single provider
    -

    A Wallet is a grouping of Options +

    A Wallet is a grouping of Instruments for display purposes.

    To enable developers to build payment apps in a variety of ways, we decouple the registration (and subsequent display) - of Options from how payment handlers respond to + of Instruments from how payment handlers respond to paymentrequest events. However, the user agent is responsible for communicating the user's selection in the event.

    -

    Selection of Options

    +

    Selection of Instruments

    Users agents may wish to enable the user to select - individual displayed Options. The payment handler would - receive information about the selected option and could take + individual displayed Instruments. The payment handler would + receive information about the selected Instrument and could take action, potentially eliminating an extra click (first open - the payment app then select the option).

    -

    +

    Issue 98. Should we require that, if displayed, - individual Options must be selectable? Or should we allow - flexibility that Options may be displayed, but selecting any + individual Instruments must be selectable? Or should we allow + flexibility that Instruments may be displayed, but selecting any one invokes all registered payment handlers? One idea that has been suggested: the user agent (e.g., on a mobile device) could first display the app-level icon/logo. Upon selection, - the user agent could display the Options in a submenu.

    + the user agent could display the Instruments in a submenu.

    Payment Handler Invocation, Display and Response

    -

    Once the user has selected an Option, the user agent fires a +

    Once the user has selected an Instrument, the user agent fires a paymentrequest event with a Payment App Request, and uses the subsequent Payment App Response to create a PaymentReponse for [[!PAYMENT-REQUEST-API]].

    -

    Payment App Request

    The payment app request is - conveyed using the following dictionary: +

    PaymentAppRequest interface

    + The payment app request is + conveyed using the following interface:
    -      dictionary PaymentAppRequest {
    -        DOMString origin;
    -        sequence<PaymentMethodData> methodData;
    -        PaymentItem total;
    -        sequence<PaymentDetailsModifier> modifiers;
    -        DOMString optionId;
    +      interface PaymentAppRequest {
    +        readonly attribute DOMString origin;
    +        readonly attribute DOMString id;
    +        readonly attribute FrozenArray<PaymentMethodData> methodData;
    +        readonly attribute PaymentItem total;
    +        readonly attribute FrozenArray<PaymentDetailsModifier> modifiers;
    +        readonly attribute DOMString instrumentId;
           };
         
    @@ -656,6 +882,12 @@

    Payment App Request

    The payment app request is Serialization of an Origin" algorithm defined in section 6.1 of [[!RFC6454]]. +
    id attribute
    +
    + When getting, the id attribute returns this the + [[\details]].id from the PaymentRequest that + corresponds to this PaymentAppRequest. +
    methodData attribute
    This attribute contains PaymentMethodData @@ -694,17 +926,13 @@

    Payment App Request

    The payment app request is using the Modifiers Population Algorithm defined below.
    -
    optionId attribute
    +
    instrumentId attribute
    - This attribute indicates the PaymentAppOption + This attribute indicates the PaymentInstrument selected by the user. It corresponds to the id field provided during registration.
    -

    See - issue 91 for discussion about the inclusion of line items - in the Payment App Request.

    Method Data Population Algorithm

    To initialize the value of the methodData, @@ -713,10 +941,10 @@

    Method Data Population Algorithm

    1. Set registeredMethods to an empty set.
    2. -
    3. For each PaymentAppOption option in +
    4. For each PaymentInstrument instrument in the payment handler's - PaymentAppManager.options, add all entries - in option.enabledMethods to + PaymentManager.instruments, add all entries + in instrument.enabledMethods to registeredMethods.
    5. Create a new empty Sequence.
    6. @@ -763,10 +991,10 @@

      Modifiers Population Algorithm

      1. Set registeredMethods to an empty set.
      2. -
      3. For each PaymentAppOption option in +
      4. For each PaymentInstrument instrument in the payment handler's - PaymentAppManager.options, add all entries - in option.enabledMethods to + PaymentManager.instruments, add all entries + in instrument.enabledMethods to registeredMethods.
      5. Create a new empty Sequence.
      6. @@ -939,7 +1167,8 @@

        Payment Handler Windows

        handler to the user.

    -

    Payment App Response

    The payment app response +

    PaymentAppResponse dictionary

    + The payment app response is conveyed using the following dictionary:
           dictionary PaymentAppResponse {
    @@ -1021,12 +1250,13 @@ 

    Payment App Response

    The payment app response paymentRequestEvent.respondWith(new Promise(function(accept,reject) { /* ... processing may occur here ... */ accept({ - methodName: "basic-card#visa", + methodName: "basic-card", details: { - card_number : "1232343451234", - expiry_month : "12", - expiry_year : "2020", - cvv : "123" + cardHolderName: "John Smith", + cardNumber: "1232343451234", + expiryMonth: "12", + expiryYear : "2020", + cardSecurityCode: "123" } }); }); @@ -1049,9 +1279,9 @@

    Example of handling the paymentrequest

    Per issue 97, we expect not to use - clients.OpenWindow and instead await a proposal for a new - openClientWindow method. We will need to update these - examples.

    + clients.OpenWindow() and instead await a proposal for a new + openClientWindow method on the PaymentRequestEvent. + We will need to update these examples.

    @@ -1066,6 +1296,7 @@ 

    Example of handling the paymentrequest } }); + // Note -- this will probably chage to e.openClientWindow(...) clients.openWindow("https://www.example.com/bobpay/pay") .then(function(windowClient) { windowClient.postMessage(e.data); @@ -1182,5 +1413,6 @@

    Private Browsing Mode

    +