From cf6220348ad03abe5f27a388f37cc718197ac509 Mon Sep 17 00:00:00 2001 From: Sahel Sharify Date: Mon, 21 Oct 2019 14:01:52 -0400 Subject: [PATCH] Shipping Address and Payer's Contact info Delegation (#349) closes https://github.com/w3c/payment-handler/issues/337 The following tasks have been completed: [x] web platform tests ( enable-shipping-contact-delegation, change-shipping-option, change-shipping-address) [ ] MDN Docs added. Implementation commitment: [ ] Safari [x] Chrome (https://crbug.com/984694) [ ] Firefox [ ] Edge --- index.html | 467 ++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 406 insertions(+), 61 deletions(-) diff --git a/index.html b/index.html index a6a9138..27fda77 100644 --- a/index.html +++ b/index.html @@ -330,11 +330,13 @@

interface PaymentManager { [SameObject] readonly attribute PaymentInstruments instruments; attribute DOMString userHint; + Promise<void> enableDelegations(FrozenArray<PaymentDelegation> delegations); };

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

@@ -362,6 +364,55 @@

cause confusion to display the additional hint.

+
+

+ enableDelegations() method +

+

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

+
+ +
+

+ PaymentDelegation enum +

+
+        enum PaymentDelegation {
+          "shippingAddress",
+          "payerName",
+          "payerPhone",
+          "payerEmail"
+        };
+        
+
+
+ "shippingAddress" +
+
+ The payment handler will provide shipping address whenever needed. +
+
+ "payerName" +
+
+ The payment handler will provide payer's name whenever needed. +
+
+ "payerPhone" +
+
+ The payment handler will provide payer's phone whenever needed. +
+
+ "payerEmail" +
+
+ The payment handler will provide payer's email whenever needed. +
+
@@ -786,6 +837,8 @@

navigator.serviceWorker.register("/sw.js"); const registration = await navigator.serviceWorker.ready; + await registration.paymentManager.enableDelegations( + ['shippingAddress', 'payerName']); // Excellent, we got it! Let's now set up the user's cards. await addInstruments(registration); @@ -1138,22 +1191,25 @@

-
+

- The PaymentMethodChangeResponse + The PaymentRequestDetailsUpdate

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

-        dictionary PaymentMethodChangeResponse {
+        dictionary PaymentRequestDetailsUpdate {
           DOMString error;
           PaymentCurrencyAmount total;
           FrozenArray<PaymentDetailsModifier> modifiers;
+          FrozenArray<PaymentShippingOption> shippingOptions;
           object paymentMethodErrors;
+          AddressErrors shippingAddressErrors;
         };
         
@@ -1161,8 +1217,8 @@

error member

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

@@ -1170,9 +1226,12 @@

total member

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

@@ -1180,10 +1239,21 @@

modifiers member

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

+
+
+

+ shippingOptions member +

+

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

@@ -1194,6 +1264,14 @@

Validation errors for the payment method, if any.

+
+

+ shippingAddressErrors member +

+

+ Validation errors for the shipping address, if any. +

+
@@ -1218,8 +1296,12 @@

readonly attribute FrozenArray<PaymentDetailsModifier> modifiers; readonly attribute DOMString instrumentKey; readonly attribute boolean requestBillingAddress; + readonly attribute object? paymentOptions; + readonly attribute FrozenArray<PaymentShippingOption>? shippingOptions; Promise<WindowClient?> openWindow(USVString url); - Promise<PaymentMethodChangeResponse?> changePaymentMethod(DOMString methodName, optional object? methodDetails = null); + Promise<PaymentRequestDetailsUpdate?> changePaymentMethod(DOMString methodName, optional object? methodDetails = null); + Promise<PaymentRequestDetailsUpdate?> changeShippingAddress(AddressInit shippingAddress); + Promise<PaymentRequestDetailsUpdate?> changeShippingOption(DOMString shippingOption); void respondWith(Promise<PaymentHandlerResponse> handlerResponsePromise); }; @@ -1319,6 +1401,27 @@

in the PaymentRequest.

+
+

+ paymentOptions attribute +

+

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

+
+
+

+ shippingOptions attribute +

+

+ The value of ShippingOptions on the + PaymentDetailsInit from the corresponding + PaymentRequest. Available only when shipping address is + requested. +

+

openWindow() method @@ -1340,6 +1443,30 @@

called, it runs the change payment method algorithm.

+
+

+ changeShippingAddress() + method +

+

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

+
+
+

+ changeShippingOption() + method +

+

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

+

PaymentCurrencyAmount total; sequence<PaymentDetailsModifier> modifiers; DOMString instrumentKey; + PaymentOptions paymentOptions; + sequence<PaymentShippingOption> shippingOptions; };

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

@@ -1635,6 +1765,24 @@

PaymentInstrument, or the empty string if none was selected. +
+ paymentOptions +
+
+ A structured clone of the paymentOptions dictionary + passed to the constructor of the corresponding + PaymentRequest. +
+
+ shippingOptions +
+
+ A structured clone of the shippingOptions field on the + PaymentDetailsInit from the corresponding + PaymentRequest. +

Then run the following steps in parallel, with @@ -1869,6 +2017,11 @@

dictionary PaymentHandlerResponse { DOMString methodName; object details; + DOMString? payerName; + DOMString? payerEmail; + DOMString? payerPhone; + AddressInit shippingAddress; + DOMString? shippingOption; };
@@ -1914,6 +2067,46 @@

+
+

+ payerName attribute +

+

+ The user provided payer's name. +

+
+
+

+ payerEmail attribute +

+

+ The user provided payer's email. +

+
+
+

+ payerPhone attribute +

+

+ The user provided payer's phone number. +

+
+
+

+ shippingAddress attribute +

+

+ The user provided shipping address. +

+
+
+

+ shippingOption attribute +

+

+ The identifier of the user selected shipping option. +

+

@@ -1939,7 +2132,37 @@

  • If event.updateWith(detailsPromise) times out (optional), throw "InvalidStateError" DOMException.
  • -
  • Construct and return a PaymentMethodChangeResponse from +
  • Construct and return a PaymentRequestDetailsUpdate from + the detailsPromise in + event.updateWith(detailsPromise). +
  • + +

    +
    +

    + Change Payment Details Algorithm +

    +

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

    +
      +
    1. Run the PaymentRequest updated algorithm with + PaymentRequestUpdateEvent event constructed using + the updated details (shippingAddress or + shippingOption). +
    2. +
    3. If event.updateWith(detailsPromise) is not run, + return null. +
    4. +
    5. If event.updateWith(detailsPromise) throws, + rethrow the error. +
    6. +
    7. If event.updateWith(detailsPromise) times out + (optional), throw "InvalidStateError" DOMException. +
    8. +
    9. Construct and return a PaymentRequestDetailsUpdate from the detailsPromise in event.updateWith(detailsPromise).
    10. @@ -1992,46 +2215,100 @@

      dictionary. If this throws an exception, run the payment app failure algorithm and terminate these steps. -
    11. If handlerResponse.methodName is not present - or not set to one of the values from - event.methodData, - run the payment app failure algorithm and terminate - these steps. -
    12. -
    13. If handlerResponse.details is not present or - not JSON-serializable, run the payment app failure - algorithm and terminate these steps. -
    14. -
    15. Let serializeMethodName be the result of - StructuredSerialize - with handlerResponse.methodName. Rethrow any - exceptions. +
    16. Validate that all required members exist in + handlerResponse and are well formed. +
        +
      1. If handlerResponse.methodName is not + present or not set to one of the values from + event.methodData, run the + payment app failure algorithm and terminate these + steps. +
      2. +
      3. If handlerResponse.details is not present + or not JSON-serializable, run the payment app + failure algorithm and terminate these steps. +
      4. +
      5. Let shippingRequired be the requestShipping + value of the associated PaymentRequest's + paymentOptions. If shippingRequired and + handlerResponse.shippingAddress + is not present, run the payment app failure algorithm + and terminate these steps. +
      6. +
      7. If shippingRequired and + handlerResponse.shippingOption is + not present or not set to one of shipping options identifiers + from event.shippingOptions, + run the payment app failure algorithm and terminate + these steps. +
      8. +
      9. Let payerNameRequired be the requestPayerName + value of the associated PaymentRequest's + paymentOptions. If payerNameRequired and + handlerResponse.payerName is not + present, run the payment app failure algorithm and + terminate these steps. +
      10. +
      11. Let payerEmailRequired be the requestPayerEmail + value of the associated PaymentRequest's + paymentOptions. If payerEmailRequired and + handlerResponse.payerEmail is not + present, run the payment app failure algorithm and + terminate these steps. +
      12. +
      13. Let payerPhoneRequired be the requestPayerPhone + value of the associated PaymentRequest's + paymentOptions. If payerPhoneRequired and + handlerResponse.payerPhone is not + present, run the payment app failure algorithm and + terminate these steps. +
      14. +
    17. -
    18. Let serializeDetails be the result of - StructuredSerialize - with handlerResponse.details. Rethrow any - exceptions. +
    19. Serialize required members of handlerResponse ( + methodName and details are always required; + shippingAddress and shippingOption are + required when shippingRequired is true; + payerName, payerEmail, and + payerPhone are required when + payerNameRequired, payerEmailRequired, and + payerPhoneRequired are true, respectively.): +
        + For each memberin handlerResponseLet + serializeMemberbe the result of StructuredSerializewith + handlerResponse.member. Rethrow any + exceptions. +
    20. The user agent MUST run the user accepts the payment request algorithm as defined in [[!payment-request]], - replacing steps 7 and 8 with these steps or their equivalent. + replacing steps 9-15 with these steps or their equivalent.
        -
      1. Let methodName be the result of - StructuredDeserialize - with serializeMethodName. Rethrow any - exceptions. +
      2. Deserialize serialized members: +
          + For each serializeMemberlet + memberbe the result of StructuredDeserializewith + serializeMember. Rethrow any exceptions. +
      3. -
      4. Let details be the result of StructuredDeserialize with - serializeDetails. Rethrow any exceptions. -
      5. -
      6. If any exception occurs in any of the above steps, then - run the payment app failure algorithm and terminate - these steps. +
      7. If any exception occurs in the above step, then run the + payment app failure algorithm and terminate these + steps.
      8. Assign methodName to associated PaymentRequest's "PaymentResponse">response.details.
      9. +
      10. If shippingRequired, then set the + + shippingAddress attribute of associated + PaymentReqeust's response to + shippingAddress. Otherwise, set it to null. +
      11. +
      12. If shippingRequired, then set the + + shippingOption attribute of associated PaymentReqeust's + response to + shippingOption. Otherwise, set it to null. +
      13. +
      14. If payerNameRequired, then set the + payerName + attribute of associated PaymentReqeust's response to + payerName. Otherwise, set it to null. +
      15. +
      16. If payerEmailRequired, then set the + payerEmail + attribute of associated PaymentReqeust's response to + payerEmail. Otherwise, set it to null. +
      17. +
      18. If payerPhoneRequired, then set the + payerPhone + attribute of associated PaymentReqeust's response to + payerPhone. Otherwise, set it to null. +
    @@ -2085,7 +2396,23 @@

    expiryMonth: "12", expiryYear : "2020", cardSecurityCode: "123" - } + }, + shippingAddress: { + addressLine: [ + "1875 Explorer St #1000", + ], + city: "Reston", + country: "US", + dependentLocality: "", + organization: "", + phone: "+15555555555", + postalCode: "20190", + recipient: "John Smith", + region: "VA", + sortingCode: "" + }, + shippingOption: "express", + payerEmail: "john.smith@gmail.com", }); })); @@ -2128,6 +2455,11 @@

  • User agents should allow users to disable support for the CanMakePaymentEvent.
  • +
  • In a browser that supports Payment Handler API, + CanMakePaymentEvent will fire in registered payment handlers + that can provide all merchant requested information including + shipping address and payer's contact information whenever needed. +
  • @@ -2304,7 +2636,14 @@

    PaymentOptions, PaymentShippingOption, + AddressInit, + AddressErrors, + PaymentMethodChangeEvent, + PaymentRequestUpdateEvent, ID, canMakePayment(), @@ -2315,7 +2654,9 @@

    "!payment-request#user-accepts-the-payment-request-algorithm">user accepts the payment request algorithm, payment method - changed algorithm, and , PaymentRequest + updated algorithm, and JSON-serializable are defined by the Payment Request API specification [[!payment-request]]. @@ -2476,9 +2817,13 @@

    Try Clear Registration, Try Activate, and - scope URL are - defined in [[!SERVICE-WORKERS]]. + "!SERVICE-WORKERS#try-activate-algorithm">Try Activate, + ExtendableEvent, + ExtendableEventInit, + and scope URL + are defined in [[!SERVICE-WORKERS]].