Closed dcrousso closed 1 year ago
Hi @dcrousso,
As one of probably very few users of PaymentRequest (for Android only since iOS does not support custom payment applications), I came up with a similar need but different solution. The retry()
method may be useful but in my case there is a pretty sophisticated wallet which only uses the PaymentResponse
for navigation to a success page or back to a payment selection page (due to a failed or cancelled operation).
So this particular scheme rather needed a way to talk back to the merchant with the actual authorization while remaining in the wallet UI. The return from this call is either:
In the absence of a built-in talk-back mechanism, the wallet uses a somewhat awkward OOB channel requiring the Merchant to include cookies in the PaymentRequest call.
BTW, since PaymentRequest does not come with a companion API for payment instrument enrollment , an additional task was figuring out how to do that in a convenient and secure way. It turned out that (ab)using PaymentRequest was the best solution! https://cyberphone.github.io/doc/web/calling-apps-from-the-web.pdf
I prefer
partial interface PaymentResponse {
[NewObject] Promise<undefined> complete(optional PaymentComplete result = "unknown", optional PaymentMethodData paymentMethod);
}
…because we’d preserve backwards compat fairly easily.
@dcrousso, all, I'll pick this up the week of the 17th when I'm back from vacation.
Random observations:
PaymentMethodData
to the payment sheet via .complete()
is critical for this use case, then we should do the overload (i.e., no backwards compat, force a promise rejection). PaymentResponse
has an associated .methodName
attribute, which is the one the user chose to perform the payment (e.g., https://apple.com/pay). Thus, using a sequence<PaymentMethodData>
might be somewhat redundant, as the data being passed can only ever apply to whatever paymentResponse.methodName
is. So, with the above... I tend to land at @dcrousso second suggestion:
partial interface PaymentResponse {
[NewObject] Promise<undefined> complete(
optional PaymentComplete result = "unknown",
optional object paymentMethodData);
}
However, I'm worried about it being an unstructured object, so how about:
dictionary PaymentCompleteDetails {
object data;
}
partial interface PaymentResponse {
[NewObject] Promise<undefined> complete(
optional PaymentComplete result = "unknown",
optional PaymentCompleteDetails details = {});
}
Yeah 1 is not critical, so 2 makes the most sense. 3 is a good point. I think having dictionary PaymentCompleteDetails
is a good idea :)
Hi Guys, Being an application developer I don't think in IDL but in JavaScript. Anyway, would it be possible describing a possible use case a little bit more? I don't understand the flow.
Is the Merchant supposed to send down additional information to the payment handler? How would the Merchant know that such information is required without first have gotten some piece of information from the payment handler?
Pardon, if I got it all wrong.
Personally I would be extremely happy if there was a possibility adding a https://developer.mozilla.org/en-US/docs/Web/API/MessageChannel to PaymentRequest.
Being an application developer I don't think in IDL but in JavaScript. Anyway, would it be possible describing a possible use case a little bit more? I don't understand the flow.
Sure, let me try to explain.
Let's say http://marcospay.com lets a merchant complete a payment in the following way (this example is deliberately silly, but stay with me):
// marcospay supports playing the theme song to party town in the payment sheet, so cool! 🎉🥳
paymentResponse.complete("success", {data: {"theme": "party-town"}});
Is the Merchant supposed to send down additional information to the payment handler? How would the Merchant know that such information is required without first have gotten some piece of information from the payment handler?
By virtue that you've signed up with "marcospay". Marcospay's documentation tells you about passing "party-town" and what it does.
const data = {};
switch (paymentResponse.methodName) {
case "https://marcospay.com":
data.theme = "party-town";
break;
case "https://apple.com/pay":
data.abc123 = "something";
break;
/// and so on...
}
paymentResponse.complete("success", data);
Personally I would be extremely happy if there was a possibility adding a
MessageChannel
toPaymentRequest
.
That would fundamentally change the API and we would need a whole new design. The point here is just to pass a one off bit of data when the sheet is closing/payment is completing.
Thanx, now it became considerably clearer!
I must though admit that I don't fully understand the motivation since a normal payment application (in a Web context NB...), would rather return with a message from the Merchant. In addition, this kind of information can be provided in https://www.w3.org/TR/payment-request/#dom-paymentmethoddata
@dcrousso, I've put up a draft at https://github.com/w3c/payment-request/pull/982
Please see the issue around exceptions... would like to discuss what to do there with you over in the pull request.
@cyberphone wrote:
I must though admit that I don't fully understand the motivation since a normal payment application (in a Web context NB...), would rather return with a message from the Merchant. In addition, this kind of information can be provided in https://www.w3.org/TR/payment-request/#dom-paymentmethoddata
The addition .data
is dependent on how the payment is to .complete()
: "success", "failure", "unknown". So, it may not possible to know what the .data
might be before the merchant calls .complete()
... otherwise, the merchant would need to pass every possible .data
for every possible completion type.
That would be super ugly:
const methodData = [
{
supportedMethods: "https://example.com/payitforward",
data: {
payItForwardField: "ABC",
"success": {theme: "party-town"},
"failure": {theme: "sad-town"},
},
},
{
supportedMethods: "https://example.com/bobpay",
data: {
merchantIdentifier: "XXXX",
bobPaySpecificField: true,
"success": {bobThing: "fooo"},
"failure": {other: "bar"}
},
},
// and so on... this would get insanely large and redundant.
];
This looks pretty hypothetical but presumably Apple has a valid use case. I guess it is secret 🙃
The need for playing a merchant specified (but still predefined) melody or something like that for "failure" is not entirely apparent.
My payment application/handler requires a much more potent mechanism as outlined in: https://github.com/w3c/payment-request/issues/981#issuecomment-996500857 It is essentially a callback which is an established extension method featured in gazillions of proprietary JS-APIs.
Since the "sheet" never got any traction, adding data during initialization seems like a useful method with the API "as is".
However, don't let me discourage you from introducing this feature. I'm just a grumpy old platform nerd who don't change things unless it is proven to be necessary 🤓 But it is also possible that Apple had something quite different in mind with their proposal.
This is a more realistic scenario:
const methodData = [
{
supportedMethods: "https://apple.com/pay",
data: {
// Whatever extra data needed
"success": {theme: "party-town"},
"failure": {theme: "sad-town"},
}
}
];
Using a switch statement moves the data to another place but offers (AFAICT) no added functionality.
It'd be great for there to be a standardized way for developers/merchants to provide data back to the
PaymentRequest
after it's been accepted. This would allow additional information to be provided back to the underlying platform payment method handler/UI.I was thinking it'd be something like this:
or maybe even just
Alternatively, we maybe might want to wrap the
PaymentComplete
in a new IDLdictionary
(just likePaymentValidationErrors
), which would also allow for more extension in the future if desired.Though (in both cases) I'm not sure if we want just a
PaymentMethodData
or asequence<PaymentMethodData>
so that a developer/merchant can declaratively provide data back to all supported payment methods at once instead of having to adjust the data based on which payment method is currently being used.