Skip to content

Commit f8f56a4

Browse files
maxlguCommit Bot
authored and
Commit Bot
committed
[PlayBilling] Total optional on only app-store billing requested
Context: Total is a field in the "new PaymentRequest()" API. It specifies the amount and currency of the payment request. However, when the merchant requests for the app-store billing (e.g., Google Play Store billing - by specifying "https://play.google.com/billing" as the method), the total field becomes unnecessary. This is because app-stores takes the total from elsewhere. Before: The total field is mandatory for PaymentRequest. After: The total field is optional if only app-store methods are requested. When total field is optional and left out, Chrome would add a total of amount 0, currency "ZZZ" and label "AppStoreBillingTotalPlaceHolder". Change: * Added a RuntimeEnabledFeature: PaymentRequestTotalOptional * Added an about flag: payment-request-optional-total * change the optionality of the total field and details field of PaymentRequest API. Related Doc: * Chrome Status: https://www.chromestatus.com/feature/5226111782879232 * Intent to Prototype: https://groups.google.com/a/chromium.org/forum/#!msg/blink-dev/TJVn0Ps9ugA/3unr2Vo8AgAJ * W3C explainer: w3c/payment-request#912 Bug: 1066531 Change-Id: Id5ad87b9fc452fd41a1ebef066d981737545a235 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2150974 Reviewed-by: Rouslan Solomakhin <[email protected]> Reviewed-by: Yaron Friedman <[email protected]> Reviewed-by: David Bokan <[email protected]> Commit-Queue: Liquan (Max) Gu <[email protected]> Cr-Commit-Position: refs/heads/master@{#769914}
1 parent 596bbbf commit f8f56a4

22 files changed

+523
-25
lines changed

components/payments/core/features.cc

+3
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ const base::Feature kStrictHasEnrolledAutofillInstrument{
5050
const base::Feature kPaymentRequestSkipToGPay{
5151
"PaymentRequestSkipToGPay", base::FEATURE_DISABLED_BY_DEFAULT};
5252

53+
const base::Feature kPaymentRequestOptionalTotal{
54+
"PaymentRequestOptionalTotal", base::FEATURE_DISABLED_BY_DEFAULT};
55+
5356
const base::Feature kPaymentRequestSkipToGPayIfNoCard{
5457
"PaymentRequestSkipToGPayIfNoCard", base::FEATURE_DISABLED_BY_DEFAULT};
5558

components/payments/core/features.h

+3
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ extern const base::Feature kPaymentRequestSkipToGPay;
6060
// eligible credit card.
6161
extern const base::Feature kPaymentRequestSkipToGPayIfNoCard;
6262

63+
// Enables the total field of PaymentRequest API to be optional.
64+
extern const base::Feature kPaymentRequestOptionalTotal;
65+
6366
// If enabled, just-in-time installable payment handlers are ranked lower than
6467
// complete autofill instruments in payment sheet's method selection section.
6568
extern const base::Feature kDownRankJustInTimePaymentApp;

content/child/runtime_features.cc

+2
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,8 @@ void SetRuntimeFeaturesFromChromiumFeatures() {
225225
{wf::EnablePaymentRequest, features::kWebPayments, kUseFeatureState},
226226
{wf::EnablePaymentHandlerMinimalUI, features::kWebPaymentsMinimalUI,
227227
kEnableOnly},
228+
{wf::EnablePaymentRequestOptionalTotal,
229+
features::kPaymentRequestOptionalTotal, kEnableOnly},
228230
{wf::EnablePaymentApp, features::kServiceWorkerPaymentApps, kEnableOnly},
229231
{wf::EnableGenericSensorExtraClasses, features::kGenericSensorExtraClasses,
230232
kEnableOnly},

content/public/common/content_features.cc

+4
Original file line numberDiff line numberDiff line change
@@ -809,6 +809,10 @@ const base::Feature kWebPayments{"WebPayments",
809809
const base::Feature kWebPaymentsMinimalUI{"WebPaymentsMinimalUI",
810810
base::FEATURE_DISABLED_BY_DEFAULT};
811811

812+
// Allows the total field of PaymentRequest API to be optional.
813+
const base::Feature kPaymentRequestOptionalTotal{
814+
"PaymentRequestOptionalTotal", base::FEATURE_DISABLED_BY_DEFAULT};
815+
812816
// Makes WebRTC use ECDSA certs by default (i.e., when no cert type was
813817
// specified in JS).
814818
const base::Feature kWebRtcEcdsaDefault{"WebRTC-EnableWebRtcEcdsa",

content/public/common/content_features.h

+1
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ CONTENT_EXPORT extern const base::Feature kOriginIsolationHeader;
9494
CONTENT_EXPORT extern const base::Feature kOriginPolicy;
9595
CONTENT_EXPORT extern const base::Feature kOverscrollHistoryNavigation;
9696
CONTENT_EXPORT extern const base::Feature kParkableStringsToDisk;
97+
CONTENT_EXPORT extern const base::Feature kPaymentRequestOptionalTotal;
9798
CONTENT_EXPORT extern const base::Feature kPeriodicBackgroundSync;
9899
CONTENT_EXPORT extern const base::Feature kPepper3DImageChromium;
99100
CONTENT_EXPORT extern const base::Feature kPepperCrossOriginRedirectRestriction;

third_party/blink/public/platform/web_runtime_features.h

+1
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ class WebRuntimeFeatures {
136136
BLINK_PLATFORM_EXPORT static void EnablePagePopup(bool);
137137
BLINK_PLATFORM_EXPORT static void EnablePaymentApp(bool);
138138
BLINK_PLATFORM_EXPORT static void EnablePaymentHandlerMinimalUI(bool);
139+
BLINK_PLATFORM_EXPORT static void EnablePaymentRequestOptionalTotal(bool);
139140
BLINK_PLATFORM_EXPORT static void EnablePaymentRequest(bool);
140141
BLINK_PLATFORM_EXPORT static void EnablePercentBasedScrolling(bool);
141142
BLINK_PLATFORM_EXPORT static void EnablePerformanceManagerInstrumentation(

third_party/blink/renderer/modules/BUILD.gn

+1
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,7 @@ jumbo_source_set("unit_tests") {
367367
"payments/payment_address_test.cc",
368368
"payments/payment_event_data_conversion_test.cc",
369369
"payments/payment_request_details_test.cc",
370+
"payments/payment_request_optional_total_test.cc",
370371
"payments/payment_request_test.cc",
371372
"payments/payment_request_update_event_test.cc",
372373
"payments/payment_response_test.cc",

third_party/blink/renderer/modules/payments/payment_details_init.idl

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
// https://w3c.github.io/browser-payment-api/#paymentdetailsinit-dictionary
5+
// https://w3c.github.io/payment-request/#paymentdetailsinit-dictionary
66

77
dictionary PaymentDetailsInit : PaymentDetailsBase {
88
DOMString id;
9-
required PaymentItem total;
9+
PaymentItem total;
1010
};

third_party/blink/renderer/modules/payments/payment_request.cc

+88-20
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include <utility>
1010

11+
#include "base/bind.h"
1112
#include "base/location.h"
1213
#include "base/stl_util.h"
1314
#include "build/build_config.h"
@@ -93,6 +94,9 @@ using ::payments::mojom::blink::PaymentValidationErrorsPtr;
9394
const char kHasEnrolledInstrumentDebugName[] = "hasEnrolledInstrument";
9495
const char kGooglePayMethod[] = "https://google.com/pay";
9596
const char kAndroidPayMethod[] = "https://android.com/pay";
97+
const char kGooglePlayBillingMethod[] = "https://play.google.com/billing";
98+
const char kUnknownCurrency[] = "ZZZ";
99+
const char kAppStoreBillingLabelPlaceHolder[] = "AppStoreBillingPlaceHolder";
96100

97101
} // namespace
98102

@@ -266,6 +270,18 @@ void ValidateShippingOptionOrPaymentItem(const T* item,
266270
}
267271
}
268272

273+
bool RequestingOnlyAppStoreBillingMethods(
274+
const Vector<payments::mojom::blink::PaymentMethodDataPtr>& method_data) {
275+
DCHECK(!method_data.IsEmpty());
276+
static const WTF::HashSet<String> app_store_billing_methods = {
277+
kGooglePlayBillingMethod};
278+
for (const auto& method : method_data) {
279+
if (!app_store_billing_methods.Contains(method->supported_method))
280+
return false;
281+
}
282+
return true;
283+
}
284+
269285
void ValidateAndConvertDisplayItems(
270286
const HeapVector<Member<PaymentItem>>& input,
271287
const String& item_names,
@@ -504,17 +520,50 @@ void ValidateAndConvertPaymentDetailsBase(const PaymentDetailsBase* input,
504520
}
505521
}
506522

523+
PaymentItemPtr CreateTotalPlaceHolderForAppStoreBilling(
524+
ExecutionContext& execution_context) {
525+
PaymentItemPtr total = payments::mojom::blink::PaymentItem::New();
526+
total->label = kAppStoreBillingLabelPlaceHolder;
527+
total->amount = payments::mojom::blink::PaymentCurrencyAmount::New();
528+
total->amount->currency = kUnknownCurrency;
529+
total->amount->value = "0";
530+
531+
return total;
532+
}
533+
507534
void ValidateAndConvertPaymentDetailsInit(const PaymentDetailsInit* input,
508535
const PaymentOptions* options,
509536
PaymentDetailsPtr& output,
510537
String& shipping_option_output,
538+
bool ignore_total,
511539
ExecutionContext& execution_context,
512540
ExceptionState& exception_state) {
513-
DCHECK(input->hasTotal());
514-
ValidateAndConvertTotal(input->total(), "total", output->total,
515-
execution_context, exception_state);
516-
if (exception_state.HadException())
517-
return;
541+
if (ignore_total) {
542+
output->total = CreateTotalPlaceHolderForAppStoreBilling(execution_context);
543+
if (input->hasTotal()) {
544+
execution_context.AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
545+
mojom::blink::ConsoleMessageSource::kJavaScript,
546+
mojom::blink::ConsoleMessageLevel::kWarning,
547+
"Specified total is ignored for in-app purchases with app stores. "
548+
"User will be shown the total derived from the product identifier."));
549+
}
550+
} else {
551+
// Whether details (i.e., input) being omitted, null, defined or {} is
552+
// indistinguishable, so we check all of its attributes to decide whether it
553+
// has been provided.
554+
if (!input->hasTotal() && !input->hasId()) {
555+
exception_state.ThrowTypeError("required member details is undefined.");
556+
return;
557+
}
558+
if (!input->hasTotal()) {
559+
exception_state.ThrowTypeError("required member total is undefined.");
560+
return;
561+
}
562+
ValidateAndConvertTotal(input->total(), "total", output->total,
563+
execution_context, exception_state);
564+
if (exception_state.HadException())
565+
return;
566+
}
518567

519568
ValidateAndConvertPaymentDetailsBase(input, options, output,
520569
shipping_option_output,
@@ -525,19 +574,26 @@ void ValidateAndConvertPaymentDetailsUpdate(const PaymentDetailsUpdate* input,
525574
const PaymentOptions* options,
526575
PaymentDetailsPtr& output,
527576
String& shipping_option_output,
577+
bool ignore_total,
528578
ExecutionContext& execution_context,
529579
ExceptionState& exception_state) {
530580
ValidateAndConvertPaymentDetailsBase(input, options, output,
531581
shipping_option_output,
532582
execution_context, exception_state);
533583
if (exception_state.HadException())
534584
return;
535-
536585
if (input->hasTotal()) {
537-
ValidateAndConvertTotal(input->total(), "total", output->total,
538-
execution_context, exception_state);
539-
if (exception_state.HadException())
540-
return;
586+
DCHECK(!RuntimeEnabledFeatures::PaymentRequestOptionalTotalEnabled() ||
587+
!ignore_total);
588+
if (ignore_total) {
589+
output->total =
590+
CreateTotalPlaceHolderForAppStoreBilling(execution_context);
591+
} else {
592+
ValidateAndConvertTotal(input->total(), "total", output->total,
593+
execution_context, exception_state);
594+
if (exception_state.HadException())
595+
return;
596+
}
541597
}
542598

543599
if (input->hasError()) {
@@ -665,9 +721,9 @@ PaymentRequest* PaymentRequest::Create(
665721
const HeapVector<Member<PaymentMethodData>>& method_data,
666722
const PaymentDetailsInit* details,
667723
ExceptionState& exception_state) {
668-
return MakeGarbageCollected<PaymentRequest>(execution_context, method_data,
669-
details, PaymentOptions::Create(),
670-
exception_state);
724+
return MakeGarbageCollected<PaymentRequest>(
725+
execution_context, method_data, details, PaymentOptions::Create(),
726+
mojo::NullRemote(), exception_state);
671727
}
672728

673729
PaymentRequest* PaymentRequest::Create(
@@ -677,7 +733,8 @@ PaymentRequest* PaymentRequest::Create(
677733
const PaymentOptions* options,
678734
ExceptionState& exception_state) {
679735
return MakeGarbageCollected<PaymentRequest>(
680-
execution_context, method_data, details, options, exception_state);
736+
execution_context, method_data, details, options, mojo::NullRemote(),
737+
exception_state);
681738
}
682739

683740
PaymentRequest::~PaymentRequest() = default;
@@ -982,7 +1039,7 @@ void PaymentRequest::OnUpdatePaymentDetails(
9821039
PaymentDetailsPtr validated_details =
9831040
payments::mojom::blink::PaymentDetails::New();
9841041
ValidateAndConvertPaymentDetailsUpdate(
985-
details, options_, validated_details, shipping_option_,
1042+
details, options_, validated_details, shipping_option_, ignore_total_,
9861043
*GetExecutionContext(), exception_state);
9871044
if (exception_state.HadException()) {
9881045
resolver->Reject(exception_state.GetException());
@@ -1067,6 +1124,8 @@ PaymentRequest::PaymentRequest(
10671124
const HeapVector<Member<PaymentMethodData>>& method_data,
10681125
const PaymentDetailsInit* details,
10691126
const PaymentOptions* options,
1127+
mojo::PendingRemote<payments::mojom::blink::PaymentRequest>
1128+
mock_payment_provider,
10701129
ExceptionState& exception_state)
10711130
: ExecutionContextLifecycleObserver(execution_context),
10721131
options_(options),
@@ -1081,8 +1140,8 @@ PaymentRequest::PaymentRequest(
10811140
this,
10821141
&PaymentRequest::OnUpdatePaymentDetailsTimeout),
10831142
is_waiting_for_show_promise_to_resolve_(false) {
1143+
DCHECK(details);
10841144
DCHECK(GetExecutionContext()->IsSecureContext());
1085-
10861145
if (!AllowedToUsePaymentRequest(execution_context)) {
10871146
exception_state.ThrowSecurityError(
10881147
"Must be in a top-level browsing context or an iframe needs to specify "
@@ -1114,9 +1173,12 @@ PaymentRequest::PaymentRequest(
11141173
if (exception_state.HadException())
11151174
return;
11161175

1176+
ignore_total_ =
1177+
RuntimeEnabledFeatures::PaymentRequestOptionalTotalEnabled() &&
1178+
RequestingOnlyAppStoreBillingMethods(validated_method_data);
11171179
ValidateAndConvertPaymentDetailsInit(details, options_, validated_details,
1118-
shipping_option_, *GetExecutionContext(),
1119-
exception_state);
1180+
shipping_option_, ignore_total_,
1181+
*GetExecutionContext(), exception_state);
11201182
if (exception_state.HadException())
11211183
return;
11221184

@@ -1143,8 +1205,14 @@ PaymentRequest::PaymentRequest(
11431205
scoped_refptr<base::SingleThreadTaskRunner> task_runner =
11441206
execution_context->GetTaskRunner(TaskType::kUserInteraction);
11451207

1146-
GetFrame()->GetBrowserInterfaceBroker().GetInterface(
1147-
payment_provider_.BindNewPipeAndPassReceiver(task_runner));
1208+
if (mock_payment_provider) {
1209+
payment_provider_.Bind(
1210+
std::move(mock_payment_provider),
1211+
execution_context->GetTaskRunner(TaskType::kMiscPlatformAPI));
1212+
} else {
1213+
GetFrame()->GetBrowserInterfaceBroker().GetInterface(
1214+
payment_provider_.BindNewPipeAndPassReceiver(task_runner));
1215+
}
11481216
payment_provider_.set_disconnect_handler(
11491217
WTF::Bind(&PaymentRequest::OnConnectionError, WrapWeakPersistent(this)));
11501218

third_party/blink/renderer/modules/payments/payment_request.h

+3
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ class MODULES_EXPORT PaymentRequest final
6565
const HeapVector<Member<PaymentMethodData>>&,
6666
const PaymentDetailsInit*,
6767
const PaymentOptions*,
68+
mojo::PendingRemote<payments::mojom::blink::PaymentRequest>
69+
mock_payment_provider,
6870
ExceptionState&);
6971
~PaymentRequest() override;
7072

@@ -179,6 +181,7 @@ class MODULES_EXPORT PaymentRequest final
179181
TaskRunnerTimer<PaymentRequest> complete_timer_;
180182
TaskRunnerTimer<PaymentRequest> update_payment_details_timer_;
181183
bool is_waiting_for_show_promise_to_resolve_;
184+
bool ignore_total_;
182185

183186
DISALLOW_COPY_AND_ASSIGN(PaymentRequest);
184187
};

third_party/blink/renderer/modules/payments/payment_request.idl

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
Exposed=Window,
1212
ActiveScriptWrappable
1313
] interface PaymentRequest : EventTarget {
14-
[CallWith=ExecutionContext, RaisesException] constructor(sequence<PaymentMethodData> methodData, PaymentDetailsInit details, optional PaymentOptions options = {});
14+
[CallWith=ExecutionContext, RaisesException] constructor(sequence<PaymentMethodData> methodData, optional PaymentDetailsInit details = {}, optional PaymentOptions options = {});
1515
[CallWith=ScriptState, RaisesException, NewObject] Promise<PaymentResponse> show(optional Promise<PaymentDetailsUpdate> detailsPromise);
1616
[CallWith=ScriptState, RaisesException, NewObject] Promise<void> abort();
1717
[CallWith=ScriptState, RaisesException, HighEntropy, Measure, NewObject] Promise<boolean> canMakePayment();

0 commit comments

Comments
 (0)