diff --git a/index.html b/index.html index 37189695..f03d5cc4 100644 --- a/index.html +++ b/index.html @@ -292,10 +292,15 @@

  • The |details|: The details of the transaction, as a PaymentDetailsInit dictionary. This includes total cost, and - optionally a list of goods or services being purchased. Additionally, - it can optionally include "modifiers" to how payments are made. For - example, "if you pay with a card belonging to network X, it incurs a - US$3.00 processing fee". + optionally a list of goods or services being purchased, for physical + goods, and shipping options. Additionally, it can optionally include + "modifiers" to how payments are made. For example, "if you pay with a + card belonging to network X, it incurs a US$3.00 processing fee". +
  • +
  • The |options|: Optionally, a list of things as {{PaymentOptions}} + that the site needs to deliver the good or service (e.g., for physical + goods, the merchant will typically need a physical address to ship to. + For digital goods, an email will usually suffice).
  • @@ -359,20 +364,44 @@

    label: "Value-Added Tax (VAT)", amount: { currency: "GBP", value: "5.00" }, }, - { - label: "Standard shipping", - amount: { currency: "GBP", value: "5.00" }, - }, ], total: { label: "Total due", // The total is GBP£65.00 here because we need to - // add tax and shipping. + // add shipping (below). The selected shipping + // costs GBP£5.00. amount: { currency: "GBP", 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",
    +              // Shipping by truck, 2 days
    +              label: "🚛  Envío por camión (2 dias)",
    +              amount: { currency: "EUR", value: "5.00" },
    +              selected: true,
    +            },
    +            {
    +              id: "drone",
    +              // Drone shipping, 2 hours
    +              label: "🚀 Drone Express (2 horas)",
    +              amount: { currency: "EUR", value: "25.00" }
    +            },
    +          ];
    +          Object.assign(details, { shippingOptions });
    +        
    +

    Conditional modifications to payment request @@ -407,6 +436,30 @@

    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 @@ -420,6 +473,9 @@

    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) { @@ -445,6 +501,79 @@

    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 @@ -511,7 +640,8 @@

    interface PaymentRequest : EventTarget { constructor( sequence<PaymentMethodData> methodData, - PaymentDetailsInit details + PaymentDetailsInit details, + optional PaymentOptions options = {} ); [NewObject] Promise<PaymentResponse> show(optional Promise<PaymentDetailsUpdate> detailsPromise); @@ -521,7 +651,12 @@

    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; }; @@ -536,6 +671,12 @@

    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 @@ -559,14 +700,15 @@

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

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

    1. If [=this=]'s [=relevant global object=]'s [=associated @@ -597,8 +739,8 @@

    2. For each |paymentMethod| of |methodData|:
      1. Run the - steps to validate a payment method identifier with + "payment-request-ctor-pmi-handling.https.html">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 @@ -687,6 +829,48 @@

    3. +
    4. Let |selectedShippingOption| be null. +
    5. +
    6. 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. +
      3. If the {{PaymentDetailsBase/shippingOptions}} member of + |details| is present, then: +
          +
        1. Let |seenIDs| be an empty set. +
        2. +
        3. For each |option:PaymentShippingOption| in + |details|.{{PaymentDetailsBase/shippingOptions}}: +
            +
          1. + Check and canonicalize amount + |item|.{{PaymentItem/amount}}. Rethrow any exceptions. +
          2. +
          3. If |seenIDs| contains + |option|.{{PaymentShippingOption/id}}, then throw a + {{TypeError}}. Optionally, inform the developer that + shipping option IDs must be unique. +
          4. +
          5. Otherwise, append + |option|.{{PaymentShippingOption/id}} to |seenIDs|. +
          6. +
          7. If |option|.{{PaymentShippingOption/selected}} is + true, then set |selectedShippingOption| to + |option|.{{PaymentShippingOption/id}}. +
          8. +
          +
        4. +
        +
      4. +
      5. Set |details|.{{PaymentDetailsBase/shippingOptions}} to + |options|. +
      6. +
      +
    7. Let |serializedModifierData| be an empty list.
    8. Process payment details modifiers: @@ -754,6 +938,8 @@

    9. Set |request|.{{PaymentRequest/[[handler]]}} to `null`.
    10. +
    11. Set |request|.{{PaymentRequest/[[options]]}} to |options|. +
    12. Set |request|.{{PaymentRequest/[[state]]}} to "[=PaymentRequest/created=]".
    13. @@ -769,6 +955,17 @@

    14. Set |request|.{{PaymentRequest/[[response]]}} to null.
    15. +
    16. Set the value of |request|'s {{PaymentRequest/shippingOption}} + attribute to |selectedShippingOption|. +
    17. +
    18. Set the value of the {{PaymentRequest/shippingAddress}} attribute + on |request| to null. +
    19. +
    20. 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. +
    21. Return |request|.
    @@ -1233,11 +1430,69 @@

    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". @@ -1301,7 +1556,16 @@

    - [[\state]] + [[\options]] + + + The {{PaymentOptions}} supplied to the constructor. + + + + + [[\state]]

    @@ -1596,6 +1860,7 @@

             dictionary PaymentDetailsBase {
               sequence<PaymentItem> displayItems;
    +          sequence<PaymentShippingOption> shippingOptions;
               sequence<PaymentDetailsModifier> modifiers;
             };
             
    @@ -1612,6 +1877,41 @@

    {{PaymentDetailsInit/total}} amount is the sum of these items. +
    + 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
    @@ -1675,7 +1975,10 @@

               dictionary PaymentDetailsUpdate : PaymentDetailsBase {
    +            DOMString error;
                 PaymentItem total;
    +            AddressErrors shippingAddressErrors;
    +            PayerErrors payerErrors;
                 object paymentMethodErrors;
               };
             
    @@ -1689,6 +1992,21 @@

    {{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
    @@ -1701,6 +2019,19 @@

    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
    @@ -1777,6 +2108,131 @@

    +
    +

    + 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 @@ -1894,6 +2350,58 @@

    +
    +

    + 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. +
    +
    +

    PaymentResponse interface @@ -1906,6 +2414,11 @@

    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( @@ -1914,6 +2427,8 @@

    ); [NewObject] Promise<undefined> retry(optional PaymentValidationErrors errorFields = {}); + + attribute EventHandler onpayerdetailchange; };

    @@ -1953,7 +2468,7 @@

    during {{PaymentResponse/retry()}}.

    -

    +

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

    @@ -1989,6 +2504,35 @@

  • 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. +
      3. + |request|.{{PaymentRequest/[[options]]}}.{{PaymentOptions/requestPayerEmail}} + is false, and + |errorFields|.{{PaymentValidationErrors/payer}}.{{PayerErrors/email}} + is present. +
      4. +
      5. + |request|.{{PaymentRequest/[[options]]}}.{{PaymentOptions/requestPayerPhone}} + is false, and + |errorFields|.{{PaymentValidationErrors/payer}}.{{PayerErrors/phone}} + is present. +
      6. +
      7. + |request|.{{PaymentRequest/[[options]]}}.{{PaymentOptions/requestShipping}} + is false, and + |errorFields|.{{PaymentValidationErrors/shippingAddress}} is + present. +
      8. +
      +
    2. If @@ -2064,11 +2608,26 @@

                   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
      @@ -2099,22 +2658,79 @@

  • - -
    -

    - methodName attribute -

    -

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

    -
    -
    -

    - details attribute -

    -

    +

    +

    + 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). @@ -2136,6 +2752,66 @@

    +
    +

    + 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 @@ -2226,86 +2902,835 @@

    -
  • Set |response|.{{PaymentResponse/[[complete]]}} to true. +
  • Set |response|.{{PaymentResponse/[[complete]]}} to true. +
  • +
  • Return |promise| and perform the remaining steps in + parallel. +
  • +
  • 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. +
    3. Set |request|'s payment-relevant browsing context's + payment request is showing boolean to false. +
    4. +
    +
  • +
  • Otherwise: +
      +
    1. Close down any remaining user interface. The user + agent MAY use the value |result| and |serializedData| to + influence the user experience. +
    2. +
    3. Set |request|'s payment-relevant browsing context's + payment request is showing boolean to false. +
    4. +
    5. Resolve |promise| with undefined. +
    6. +
    +
  • + +

    +
    +

    + 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. +
    +
    + +
    +

    + 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. +
    3. Set |address|.{{PaymentAddress/[[addressLine]]}} to the empty + frozen array, and all other [=PaymentAddress slots|internal slots=] + to the empty string. +
    4. +
    5. If |details| was not passed, return |address|. +
    6. +
    7. 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. +
      3. If |country| is not a valid [[ISO3166-1]] alpha-2 code, + throw a {{RangeError}} exception. +
      4. +
      5. Set |address|.{{PaymentAddress/[[country]]}} to |country|. +
      6. +
      +
    8. +
    9. Let |cleanAddressLines:list| be an empty list. +
    10. +
    11. 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|. +
      2. +
      +
    12. +
    13. Set |address|.{{PaymentAddress/[[addressLine]]}} to a new + frozen array created from |cleanAddressLines|. +
    14. +
    15. If |details|.{{AddressInit/region}} is present, strip + leading and trailing ASCII whitespace from + |details|.{{AddressInit/region}} and set + |address|.{{PaymentAddress/[[region]]}} to the result. +
    16. +
    17. If |details|.{{AddressInit/city}} is present, strip leading + and trailing ASCII whitespace from + |details|.{{AddressInit/city}} and set + |address|.{{PaymentAddress/[[city]]}} to the result. +
    18. +
    19. If |details|.{{AddressInit/dependentLocality}} is present, + strip leading and trailing ASCII whitespace from + |details|.{{AddressInit/dependentLocality}} and set + |address|.{{PaymentAddress/[[dependentLocality]]}} to the result. +
    20. +
    21. If |details|.{{AddressInit/postalCode}} is present, strip + leading and trailing ASCII whitespace from + |details|.{{AddressInit/postalCode}} and set + |address|.{{PaymentAddress/[[postalCode]]}} to the result. +
    22. +
    23. If |details|.{{AddressInit/sortingCode}} is present, strip + leading and trailing ASCII whitespace from + |details|.{{AddressInit/sortingCode}} and set + |address|.{{PaymentAddress/[[sortingCode]]}} to the result. +
    24. +
    25. If |details|.{{AddressInit/organization}} is present, strip + leading and trailing ASCII whitespace from + |details|.{{AddressInit/organization}} and set + |address|.{{PaymentAddress/[[organization]]}} to the result. +
    26. +
    27. If |details|.{{AddressInit/recipient}} is present, strip + leading and trailing ASCII whitespace from + |details|.{{AddressInit/recipient}} and set + |address|.{{PaymentAddress/[[recipient]]}} to the result. +
    28. +
    29. If |details|.{{AddressInit/phone}} is present, strip leading + and trailing ASCII whitespace from + |details|.{{AddressInit/phone}} and set + |address|.{{PaymentAddress/[[phone]]}} to the result. +
    30. +
    31. Return |address|. +
    32. +
    +
    +
    +

    + 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. +
    3. If "addressLine" is not in |redactList|, set + |details|.{{AddressInit/addressLine}} to the result of splitting the + user-provided address line into a list. + +
    4. +
    5. If "country" is not in |redactList|, set + |details|.{{AddressInit/country}} to the user-provided country as an + upper case [[ISO3166-1]] alpha-2 code.
    6. -
    7. Return |promise| and perform the remaining steps in - parallel. +
    8. If "phone" is not in |redactList|, set + |details|.{{AddressInit/phone}} to the user-provided phone number. +
    9. -
    10. 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. -
      3. Set |request|'s payment-relevant browsing context's - payment request is showing boolean to false. -
      4. -
      +
    11. If "city" is not in |redactList|, set + |details|.{{AddressInit/city}} to the user-provided city, or to the + empty string if none was provided.
    12. -
    13. Otherwise: +
    14. If "dependentLocality" is not in |redactList|, set | + details|.{{AddressInit/dependentLocality}} to the user-provided + dependent locality. +
    15. +
    16. If "organization" is not in |redactList|, set + |details|.{{AddressInit/organization}} to the user-provided recipient + organization. +
    17. +
    18. 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. +

      +
      +
    19. +
    20. If "recipient" is not in |redactList|, set + |details|.{{AddressInit/recipient}} to the user-provided recipient of + the transaction. +
    21. +
    22. +

      + 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. Close down any remaining user interface. The user - agent MAY use the value |result| and |serializedData| to - influence the user experience. -
      2. -
      3. Set |request|'s payment-relevant browsing context's - payment request is showing boolean to false. -
      4. -
      5. Resolve |promise| with undefined. +
      6. Set |details|.{{AddressInit/region}} to the user-provided + region.
    23. +
    24. If "sortingCode" is not in |redactList|, set + |details|.{{AddressInit/sortingCode}} to the user-provided sorting + code. +
    25. +
    26. [=PaymentAddress.PaymentAddress()|Internally construct a new + `PaymentAddress`=] with |details| and return the result. +
    -
    -

    - 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. -
    -

    @@ -2350,6 +3775,49 @@

    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 @@ -2481,13 +3949,50 @@

    changes made by the end user through the UI), developers need to immediately call {{PaymentRequestUpdateEvent/updateWith()}}.

    +
    +              // ❌ Bad - this won't work!
    +              request.onshippingaddresschange = async ev => {
    +                // await goes to next tick, and updateWith()
    +                // was not called.
    +                const details = await getNewDetails(oldDetails);
    +                // 💥 So it's now too late! updateWith()
    +                // throws "InvalidStateError".
    +                ev.updateWith(details);
    +              };
    +
    +              // ✅ Good - UI will wait.
    +              request.onshippingaddresschange = ev => {
    +                // Calling updateWith() with a promise is ok 👍
    +                const promiseForNewDetails = getNewDetails(oldDetails);
    +                ev.updateWith(promiseForNewDetails);
    +              };
    +            

    Additionally, {{PaymentRequestUpdateEvent/[[waitForUpdate]]}} prevents reuse of {{PaymentRequestUpdateEvent}}.

    +
    +              // ❌ Bad - calling updateWith() twice doesn't work!
    +              request.addEventListener("shippingaddresschange", ev => {
    +                ev.updateWith(details); // this is ok.
    +                // 💥 [[waitForUpdate]] is true, throws "InvalidStateError".
    +                ev.updateWith(otherDetails);
    +              });
    +
    +              // ❌ Bad - this won't work either!
    +              request.addEventListener("shippingaddresschange", async ev => {
    +                const p = Promise.resolve({ ...details });
    +                ev.updateWith(p);
    +                await p;
    +                // 💥 Only one call to updateWith() is allowed,
    +                // so the following throws "InvalidStateError"
    +                ev.updateWith({ ...newDetails });
    +              });
    +            

    + "PaymentRequestUpdateEvent/updatewith-method.https.html, PaymentRequestUpdateEvent/updateWith-incremental-update-manual.https.html"> The {{PaymentRequestUpdateEvent/updateWith()}} with |detailsPromise:Promise| method MUST act as follows:

    @@ -2636,6 +4141,88 @@

    +
    +

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

    + 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. +
    3. + 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. +
      3. Run the PaymentRequest updated algorithm with + |request| and "shippingoptionchange". +
      4. +
      +
    4. +
    +

    Payment method changed algorithm @@ -2649,6 +4236,15 @@

    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. @@ -2679,7 +4275,7 @@

      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 @@ -2712,9 +4308,83 @@

    - User accepts the payment request algorithm + 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. +
    3. If |request|.{{PaymentRequest/[[response]]}} is null, return. +
    4. +
    5. Let |response:PaymentResponse| be + |request|.{{PaymentRequest/[[response]]}}. +
    6. +
    7. + Queue a task on the user interaction task source to + run the following steps: +
        +
      1. Assert: |request|.{{PaymentRequest/[[updating]]}} is false. +
      2. +
      3. Assert: |request|.{{PaymentRequest/[[state]]}} is + "[=PaymentRequest/interactive=]". +
      4. +
      5. Let |options:PaymentOptions| be + |request|.{{PaymentRequest/[[options]]}}. +
      6. +
      7. If |payer name| changed and + |options|.{{PaymentOptions/requestPayerName}} is true: +
          +
        1. Set |response|.{{PaymentResponse/payerName}} attribute to + |payer name|. +
        2. +
        +
      8. +
      9. If |payer email| changed and + |options|.{{PaymentOptions/requestPayerEmail}} is true: +
          +
        1. Set |response|.{{PaymentResponse/payerEmail}} to |payer + email|. +
        2. +
        +
      10. +
      11. If |payer phone| changed and + |options|.{{PaymentOptions/requestPayerPhone}} is true: +
          +
        1. Set |response|.{{PaymentResponse/payerPhone}} to |payer + phone|. +
        2. +
        +
      12. +
      13. Let |event:PaymentRequestUpdateEvent| be the result of + creating an event using {{PaymentRequestUpdateEvent}}. +
      14. +
      15. Initialize |event|'s {{Event/type}} attribute to + "payerdetailchange". +
      16. +
      17. + Dispatch |event| at |response|. +
      18. +
      19. 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. +
      20. +
      21. Otherwise, set + |event|.{{PaymentRequestUpdateEvent/[[waitForUpdate]]}} to true. +
      22. +
      +
    8. +
    +
    +
    +

    + 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 @@ -2734,6 +4404,13 @@

    take no further action. The user agent user interface SHOULD ensure that this never occurs. +
  • 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. +
  • Let |isRetry:boolean| be true if |request|.{{PaymentRequest/[[response]]}} is not null, false otherwise. @@ -2768,6 +4445,50 @@

    to an object resulting from running the |handler|'s steps to respond to a payment request.

  • +
  • 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. +
    3. Let |shippingAddress:PaymentAddress| be the result of + create a `PaymentAddress` from user-provided input with + |redactList|. +
    4. +
    5. Set the {{PaymentResponse/shippingAddress}} attribute value + of |response| to |shippingAddress|. +
    6. +
    7. Set the {{PaymentResponse/shippingAddress}} attribute value + of |request| to |shippingAddress|. +
    8. +
    +
  • +
  • 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. +
  • +
  • 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. +
  • +
  • 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. +
  • +
  • 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]]. +
  • Set |request|.{{PaymentRequest/[[state]]}} to "[=PaymentRequest/closed=]".
  • @@ -2879,6 +4600,11 @@

  • Let |serializedModifierData| be an empty list.
  • +
  • Let |selectedShippingOption| be null. +
  • +
  • Let |shippingOptions| be an empty + sequence<{{PaymentShippingOption}}>. +
  • Validate and canonicalize the details:
    1. If the {{PaymentDetailsUpdate/total}} member of |details| @@ -2904,6 +4630,41 @@

  • +
  • 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. +
    3. 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. +
      3. + If |seenIDs|[|option|.{{PaymentShippingOption/id}] + exists, then abort the update with |request| + and a {{TypeError}}. +
      4. +
      5. Append |option|.{{PaymentShippingOption/id}} to + |seenIDs|. +
      6. +
      7. Append |option| to |shippingOptions|. +
      8. +
      9. If |option|.{{PaymentShippingOption/selected}} is + true, then set |selectedShippingOption| to + |option|.{{PaymentShippingOption/id}}. +
      10. +
      +
    4. +
    +
  • If the {{PaymentDetailsBase/modifiers}} member of |details| is present, then:
      @@ -2915,7 +4676,9 @@

    1. For each {{PaymentDetailsModifier}} |modifier| in |modifiers|:
        -
      1. Run the steps to validate a payment method +
      2. + Run the steps to validate a payment method identifier with |modifier|.{{PaymentDetailsModifier/supportedMethods}}. If it returns false, then abort the update @@ -3004,6 +4767,21 @@

    2. +
    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. +
      3. Set the value of |request|'s + {{PaymentRequest/shippingOption}} attribute to + |selectedShippingOption|. +
      4. +
      +
    4. If the {{PaymentDetailsBase/modifiers}} member of |details| is present, then:
        @@ -3017,6 +4795,50 @@

    5. +
    6. +

      + 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. +

      +
  • @@ -3202,7 +5024,7 @@

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

    In particular, the {{PaymentMethodData}}'s {{PaymentMethodData/data}} @@ -3237,8 +5059,22 @@

    data shared via the {{PaymentMethodChangeEvent}}'s {{PaymentMethodChangeEvent/methodDetails}} attribute. Requirements and approaches for minimizing shared data are likely to vary by - payment method. + payment method and might include:

    +
      +
    • Use of a "|redactList|" for physical addresses. The + current specification makes use of a "|redactList|" to redact the + address line, organization, phone number, and + recipient from a {{PaymentRequest/shippingAddress}}. +
    • +
    • Support for instructions from the payee identifying specific + elements to exclude or include from the payment method + response data (returned through {{PaymentResponse}}.|details|). The + payee might provide these instructions via + PaymentMethodData.|data|, enabling a payment method + definition to evolve without requiring changes to the current API. +
    • +

    Where sharing of privacy-sensitive information might not be obvious to users (e.g., when [=payment handler/payment method changed @@ -3317,7 +5153,9 @@

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

    @@ -3353,7 +5191,7 @@

    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