w3c / payment-request

Payment Request API
https://www.w3.org/TR/payment-request/
Other
489 stars 135 forks source link

Make "total" and "details" optional #912

Open maxlgu opened 4 years ago

maxlgu commented 4 years ago

TL;DR

Given that when Digital Goods API is used with PaymentRequest API, the total amount is unnecessary, we propose to make the “total” field optional in PaymentRequest API spec, along with a few consequent changes.

Make “total” optional

The current PaymentRequest API requires the “total” field (of PaymentDetailsInit) to be mandatory, because any payment must come with a total amount. The mandatory “total” field was reasonable when we assume that developers could specify the “total” only with this field. But this assumption is invalid when the PaymentRequest API is used in conjunction with the proposed Digital Goods API, which connects with the digital store backend where the goods (with product ID) and their prices are specified. With the DPM API, merchants could simply provide the product ID in the PaymentRequest without knowing the total amount. Therefore, we propose to make the “total” field optional in the PaymentRequest API.

Note that we are not proposing making "total" generally optional (for all payment methods). Rather, it would become at the discretion of the user agent or payment handler when it is required (i.e., it would no longer be "required" in the WebIDL type, but the handler is allowed to throw an exception if it's not given).

For traditional payment methods, this would still be required (otherwise there is no way to communicate to the payment provider how much money is actually being spent). But for some new payment modes such as the proposed Digital Goods API, the server is the source of truth on what the price of the item is, and requiring the payment amount to be given by the client is redundant.

Make “details” optional

After “total” becomes optional, required by the WebIdl spec, the “details” field would have to become optional with a default value:

"If the type of an argument is a dictionary type or a union type that has a dictionary type as one of its flattened member types, and that dictionary type and its ancestors have no required members, and the argument is either the final argument or is followed only by optional arguments, then the argument must be specified as optional and have a default value provided."

So we propose “details” should become optional and default to “{}”.

Improved developer ergonomics

These changes would improve the developer ergonomics, as developers would be able to write: new PaymentRequest([{supportedMethods: methodName, data: {productId: ‘abc’}}]); instead of: new PaymentRequest([{supportedMethods: methodName, data: {productId: ‘abc’}], {total: {...}});

Asynchronously fail a request missing required “total”

In cases where “total” is required but missing, we propose that canMakePayment(), hasEnrolledInstrument() and show() should return a promise rejected with a "NotAllowedError" DOMException. Before the proposal, missing “total” would cause a TypeError “Missing required member(s): total” in PaymentRequest’s constructor synchronously. Note that after the proposal the failure would become asynchronous.

Why not fail a missing “total” in the constructor any more? Although it’s good for backwards compatibility, we move away from constructor because:

This change of failure may break the usages of PaymentRequest API that handle TypeError for a missing total. But we deem this breakage as trivial because it’s an error-handling for a programming error - the merchants should have always provided “total” instead of relying on the TypeError.

Summary

In summary, we propose these changes to the PaymentRequest API spec:

ChangeIDL
Remove “required” from the “total” field

dictionary PaymentDetailsInit : PaymentDetailsBase {
  DOMString id;
  required PaymentItem total;
};
Add “optional” to “details”, and give it a default value {}

[SecureContext, Exposed=Window]
interface PaymentRequest : EventTarget {
  constructor(
    sequence methodData,
    optional PaymentDetailsInit details = {},
    optional PaymentOptions options = {}
  );
  ...
}; 
ianbjacobs commented 4 years ago

cc @aestes, @marcoscaceres, @romandev

marcoscaceres commented 4 years ago

Why not move productId into PaymentDetails instead? Then we can mandate that if total is missing, then productId must be there (as productId serves as substitute for total)?

marcoscaceres commented 4 years ago

Underlying question here: would any other payment handler apart from Google Pay benefit from productId?

mgiuca commented 4 years ago

Note: This isn't just for Google Pay, this is to support any provider using our (as yet woefully under-)proposed Digital Product Management API, which could be any digital purchasing backend. I'm planning to post more details of that in the next few days.

Given that, it may be a bit premature to change the Payment Request spec for a use case that doesn't yet have a firm design. @maxlgu would you be happy to sit on this until that solidifies?

ianbjacobs commented 4 years ago

Noting support from Visa for this proposal via the WG's mailing list: https://lists.w3.org/Archives/Public/public-payments-wg/2020May/0004.html

maxlgu commented 4 years ago

@mgiuca , I would sit on this until DPM API solidifies.

mgiuca commented 4 years ago

There's some confusion on a few threads about this that we would be relaxing the restriction across the board. @maxlgu I think you should update the proposal to be clearer that the individual payment processors can still mandate the total, but that this allows new payment processors to not require a total.

Here's the text of my reply on the above thread:

We are not proposing making "total" generally optional (for all payment methods). Rather, it would become at the discretion of the user agent or payment handler when it is required (i.e., it would no longer be "required" in the WebIDL type, but the handler is allowed to throw an exception if it's not given).

For traditional payment methods, this would still be required (otherwise there is no way to communicate to the payment provider how much money is actually being spent). But for some new payment modes such as the proposed Digital Product Management API, the server is the source of truth on what the price of the item is, and requiring the payment amount to be given by the client is redundant.

maxlgu commented 4 years ago

@mgiuca , I've incorporated your words into the proposal. Thanks!

adrianhopebailie commented 4 years ago

It seems unnecessary to use the PR API for Digital Goods Services in this way.

This change would mean the Payment Request API is no longer for payments but rather for purchases.

As I suggested in https://github.com/WICG/digital-goods/issues/5, I recommend adding a purchase method to the new API instead.

If it's important to invoke the payment flow of the digital goods service via PR API then why not simply pass in the amount that was returned from itemService.getDetails?

rsolomakhin commented 4 years ago

Could you please clarify your understanding of differences between payments and purchases? I think I've been working on the wrong API this whole time :-D

rsolomakhin commented 4 years ago

If it's important to invoke the payment flow of the digital goods service via PR API then why not simply pass in the amount that was returned from itemService.getDetails?

That would give an impression to the web developer that the amount can be modified and is being passed to the payment handler. That is not the case, however, as these types of payment handlers use the product ID to lookup the price in their source of truth database.