Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(148)

Side by Side Diff: third_party/WebKit/Source/modules/payments/PaymentRequest.cpp

Issue 2501593003: Implement the new basic card specification. (Closed)
Patch Set: Rebase Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "modules/payments/PaymentRequest.h" 5 #include "modules/payments/PaymentRequest.h"
6 6
7 #include "bindings/core/v8/ExceptionState.h" 7 #include "bindings/core/v8/ExceptionState.h"
8 #include "bindings/core/v8/ScriptPromiseResolver.h" 8 #include "bindings/core/v8/ScriptPromiseResolver.h"
9 #include "bindings/core/v8/ScriptState.h" 9 #include "bindings/core/v8/ScriptState.h"
10 #include "bindings/core/v8/V8StringResource.h" 10 #include "bindings/core/v8/V8StringResource.h"
11 #include "bindings/modules/v8/V8AndroidPayMethodData.h" 11 #include "bindings/modules/v8/V8AndroidPayMethodData.h"
12 #include "bindings/modules/v8/V8BasicCardRequest.h"
12 #include "bindings/modules/v8/V8PaymentDetails.h" 13 #include "bindings/modules/v8/V8PaymentDetails.h"
13 #include "core/EventTypeNames.h" 14 #include "core/EventTypeNames.h"
14 #include "core/dom/DOMException.h" 15 #include "core/dom/DOMException.h"
15 #include "core/dom/ExceptionCode.h" 16 #include "core/dom/ExceptionCode.h"
16 #include "core/events/Event.h" 17 #include "core/events/Event.h"
17 #include "core/events/EventQueue.h" 18 #include "core/events/EventQueue.h"
18 #include "core/frame/FrameOwner.h" 19 #include "core/frame/FrameOwner.h"
19 #include "core/html/HTMLIFrameElement.h" 20 #include "core/html/HTMLIFrameElement.h"
20 #include "modules/EventTargetModulesNames.h" 21 #include "modules/EventTargetModulesNames.h"
21 #include "modules/payments/AndroidPayMethodData.h" 22 #include "modules/payments/AndroidPayMethodData.h"
22 #include "modules/payments/AndroidPayTokenization.h" 23 #include "modules/payments/AndroidPayTokenization.h"
24 #include "modules/payments/BasicCardRequest.h"
23 #include "modules/payments/HTMLIFrameElementPayments.h" 25 #include "modules/payments/HTMLIFrameElementPayments.h"
24 #include "modules/payments/PaymentAddress.h" 26 #include "modules/payments/PaymentAddress.h"
25 #include "modules/payments/PaymentItem.h" 27 #include "modules/payments/PaymentItem.h"
26 #include "modules/payments/PaymentRequestUpdateEvent.h" 28 #include "modules/payments/PaymentRequestUpdateEvent.h"
27 #include "modules/payments/PaymentResponse.h" 29 #include "modules/payments/PaymentResponse.h"
28 #include "modules/payments/PaymentShippingOption.h" 30 #include "modules/payments/PaymentShippingOption.h"
29 #include "modules/payments/PaymentsValidators.h" 31 #include "modules/payments/PaymentsValidators.h"
30 #include "mojo/public/cpp/bindings/interface_request.h" 32 #include "mojo/public/cpp/bindings/interface_request.h"
31 #include "mojo/public/cpp/bindings/wtf_array.h" 33 #include "mojo/public/cpp/bindings/wtf_array.h"
32 #include "platform/RuntimeEnabledFeatures.h" 34 #include "platform/RuntimeEnabledFeatures.h"
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
110 112
111 return output; 113 return output;
112 } 114 }
113 }; 115 };
114 116
115 } // namespace mojo 117 } // namespace mojo
116 118
117 namespace blink { 119 namespace blink {
118 namespace { 120 namespace {
119 121
120 using payments::mojom::blink::AndroidPayCardNetwork;
121 using payments::mojom::blink::AndroidPayTokenization;
122
123 // If the website does not call complete() 60 seconds after show() has been 122 // If the website does not call complete() 60 seconds after show() has been
124 // resolved, then behave as if the website called complete("fail"). 123 // resolved, then behave as if the website called complete("fail").
125 static const int completeTimeoutSeconds = 60; 124 static const int completeTimeoutSeconds = 60;
126 125
127 const struct {
128 const AndroidPayCardNetwork code;
129 const char* name;
130 } kAndroidPayNetwork[] = {{AndroidPayCardNetwork::AMEX, "AMEX"},
131 {AndroidPayCardNetwork::DISCOVER, "DISCOVER"},
132 {AndroidPayCardNetwork::MASTERCARD, "MASTERCARD"},
133 {AndroidPayCardNetwork::VISA, "VISA"}};
134
135 const struct {
136 const AndroidPayTokenization code;
137 const char* name;
138 } kAndroidPayTokenization[] = {
139 {AndroidPayTokenization::GATEWAY_TOKEN, "GATEWAY_TOKEN"},
140 {AndroidPayTokenization::NETWORK_TOKEN, "NETWORK_TOKEN"}};
141
142 // Validates ShippingOption or PaymentItem, which happen to have identical 126 // Validates ShippingOption or PaymentItem, which happen to have identical
143 // fields, except for "id", which is present only in ShippingOption. 127 // fields, except for "id", which is present only in ShippingOption.
144 template <typename T> 128 template <typename T>
145 void validateShippingOptionOrPaymentItem(const T& item, 129 void validateShippingOptionOrPaymentItem(const T& item,
146 ExceptionState& exceptionState) { 130 ExceptionState& exceptionState) {
147 if (!item.hasLabel() || item.label().isEmpty()) { 131 if (!item.hasLabel() || item.label().isEmpty()) {
148 exceptionState.throwTypeError("Item label required"); 132 exceptionState.throwTypeError("Item label required");
149 return; 133 return;
150 } 134 }
151 135
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after
231 return; 215 return;
232 216
233 if (input.amount().value()[0] == '-') { 217 if (input.amount().value()[0] == '-') {
234 exceptionState.throwTypeError("Total amount value should be non-negative"); 218 exceptionState.throwTypeError("Total amount value should be non-negative");
235 return; 219 return;
236 } 220 }
237 221
238 output = payments::mojom::blink::PaymentItem::From(input); 222 output = payments::mojom::blink::PaymentItem::From(input);
239 } 223 }
240 224
241 void maybeSetAndroidPayMethodData(const ScriptValue& input, 225 // Parses Android Pay data to avoid parsing JSON in the browser.
242 PaymentMethodDataPtr& output, 226 void setAndroidPayMethodData(const ScriptValue& input,
243 ExceptionState& exceptionState) { 227 PaymentMethodDataPtr& output,
228 ExceptionState& exceptionState) {
244 AndroidPayMethodData androidPay; 229 AndroidPayMethodData androidPay;
245 DummyExceptionStateForTesting dummyExceptionState;
246 V8AndroidPayMethodData::toImpl(input.isolate(), input.v8Value(), androidPay, 230 V8AndroidPayMethodData::toImpl(input.isolate(), input.v8Value(), androidPay,
247 dummyExceptionState); 231 exceptionState);
248 if (dummyExceptionState.hadException()) 232 if (exceptionState.hadException())
249 return; 233 return;
250 234
251 if (androidPay.hasEnvironment() && androidPay.environment() == "TEST") 235 if (androidPay.hasEnvironment() && androidPay.environment() == "TEST")
252 output->environment = payments::mojom::blink::AndroidPayEnvironment::TEST; 236 output->environment = payments::mojom::blink::AndroidPayEnvironment::TEST;
253 237
254 output->merchant_name = androidPay.merchantName(); 238 output->merchant_name = androidPay.merchantName();
255 output->merchant_id = androidPay.merchantId(); 239 output->merchant_id = androidPay.merchantId();
256 240
257 // 0 means the merchant did not specify or it was an invalid value 241 // 0 means the merchant did not specify or it was an invalid value
258 output->min_google_play_services_version = 0; 242 output->min_google_play_services_version = 0;
259 if (androidPay.hasMinGooglePlayServicesVersion()) { 243 if (androidPay.hasMinGooglePlayServicesVersion()) {
260 bool ok = false; 244 bool ok = false;
261 int minGooglePlayServicesVersion = 245 int minGooglePlayServicesVersion =
262 androidPay.minGooglePlayServicesVersion().toIntStrict(&ok); 246 androidPay.minGooglePlayServicesVersion().toIntStrict(&ok);
263 if (ok) { 247 if (ok) {
264 output->min_google_play_services_version = minGooglePlayServicesVersion; 248 output->min_google_play_services_version = minGooglePlayServicesVersion;
265 } 249 }
266 } 250 }
267 251
268 if (androidPay.hasAllowedCardNetworks()) { 252 if (androidPay.hasAllowedCardNetworks()) {
253 using payments::mojom::blink::AndroidPayCardNetwork;
254
255 const struct {
256 const AndroidPayCardNetwork code;
257 const char* const name;
258 } androidPayNetwork[] = {{AndroidPayCardNetwork::AMEX, "AMEX"},
259 {AndroidPayCardNetwork::DISCOVER, "DISCOVER"},
260 {AndroidPayCardNetwork::MASTERCARD, "MASTERCARD"},
261 {AndroidPayCardNetwork::VISA, "VISA"}};
262
269 for (const String& allowedCardNetwork : androidPay.allowedCardNetworks()) { 263 for (const String& allowedCardNetwork : androidPay.allowedCardNetworks()) {
270 for (size_t i = 0; i < arraysize(kAndroidPayNetwork); ++i) { 264 for (size_t i = 0; i < arraysize(androidPayNetwork); ++i) {
271 if (allowedCardNetwork == kAndroidPayNetwork[i].name) { 265 if (allowedCardNetwork == androidPayNetwork[i].name) {
272 output->allowed_card_networks.push_back(kAndroidPayNetwork[i].code); 266 output->allowed_card_networks.push_back(androidPayNetwork[i].code);
273 break; 267 break;
274 } 268 }
275 } 269 }
276 } 270 }
277 } 271 }
278 272
279 if (androidPay.hasPaymentMethodTokenizationParameters()) { 273 if (androidPay.hasPaymentMethodTokenizationParameters()) {
280 const blink::AndroidPayTokenization& tokenization = 274 const blink::AndroidPayTokenization& tokenization =
281 androidPay.paymentMethodTokenizationParameters(); 275 androidPay.paymentMethodTokenizationParameters();
282 output->tokenization_type = 276 output->tokenization_type =
283 payments::mojom::blink::AndroidPayTokenization::UNSPECIFIED; 277 payments::mojom::blink::AndroidPayTokenization::UNSPECIFIED;
284 if (tokenization.hasTokenizationType()) { 278 if (tokenization.hasTokenizationType()) {
285 for (size_t i = 0; i < arraysize(kAndroidPayTokenization); ++i) { 279 using payments::mojom::blink::AndroidPayTokenization;
286 if (tokenization.tokenizationType() == 280
287 kAndroidPayTokenization[i].name) { 281 const struct {
288 output->tokenization_type = kAndroidPayTokenization[i].code; 282 const AndroidPayTokenization code;
283 const char* const name;
284 } androidPayTokenization[] = {
285 {AndroidPayTokenization::GATEWAY_TOKEN, "GATEWAY_TOKEN"},
286 {AndroidPayTokenization::NETWORK_TOKEN, "NETWORK_TOKEN"}};
287
288 for (size_t i = 0; i < arraysize(androidPayTokenization); ++i) {
289 if (tokenization.tokenizationType() == androidPayTokenization[i].name) {
290 output->tokenization_type = androidPayTokenization[i].code;
289 break; 291 break;
290 } 292 }
291 } 293 }
292 } 294 }
293 295
294 if (tokenization.hasParameters()) { 296 if (tokenization.hasParameters()) {
295 const Vector<String>& keys = 297 const Vector<String>& keys =
296 tokenization.parameters().getPropertyNames(exceptionState); 298 tokenization.parameters().getPropertyNames(exceptionState);
297 if (exceptionState.hadException()) 299 if (exceptionState.hadException())
298 return; 300 return;
299 String value; 301 String value;
300 for (const String& key : keys) { 302 for (const String& key : keys) {
301 if (!DictionaryHelper::get(tokenization.parameters(), key, value)) 303 if (!DictionaryHelper::get(tokenization.parameters(), key, value))
302 continue; 304 continue;
303 output->parameters.push_back( 305 output->parameters.push_back(
304 payments::mojom::blink::AndroidPayTokenizationParameter::New()); 306 payments::mojom::blink::AndroidPayTokenizationParameter::New());
305 output->parameters.back()->key = key; 307 output->parameters.back()->key = key;
306 output->parameters.back()->value = value; 308 output->parameters.back()->value = value;
307 } 309 }
308 } 310 }
309 } 311 }
310 } 312 }
311 313
312 void stringifyAndParseMethodSpecificData(const ScriptValue& input, 314 // Parses basic-card data to avoid parsing JSON in the browser.
315 void setBasicCardMethodData(const ScriptValue& input,
316 PaymentMethodDataPtr& output,
317 ExceptionState& exceptionState) {
318 BasicCardRequest basicCard;
319 V8BasicCardRequest::toImpl(input.isolate(), input.v8Value(), basicCard,
320 exceptionState);
321 if (exceptionState.hadException())
322 return;
323
324 if (basicCard.hasSupportedNetworks()) {
325 using payments::mojom::blink::BasicCardNetwork;
326
327 const struct {
328 const BasicCardNetwork code;
329 const char* const name;
330 } basicCardNetworks[] = {{BasicCardNetwork::AMEX, "amex"},
331 {BasicCardNetwork::DINERS, "diners"},
332 {BasicCardNetwork::DISCOVER, "discover"},
333 {BasicCardNetwork::JCB, "jcb"},
334 {BasicCardNetwork::MASTERCARD, "mastercard"},
335 {BasicCardNetwork::UNIONPAY, "unionpay"},
336 {BasicCardNetwork::VISA, "visa"}};
337
338 for (const String& network : basicCard.supportedNetworks()) {
339 for (size_t i = 0; i < arraysize(basicCardNetworks); ++i) {
340 if (network == basicCardNetworks[i].name) {
341 output->supported_networks.append(basicCardNetworks[i].code);
342 break;
343 }
344 }
345 }
346 }
347
348 if (basicCard.hasSupportedTypes()) {
349 using payments::mojom::blink::BasicCardType;
350
351 const struct {
352 const BasicCardType code;
353 const char* const name;
354 } basicCardTypes[] = {{BasicCardType::CREDIT, "credit"},
355 {BasicCardType::DEBIT, "debit"},
356 {BasicCardType::PREPAID, "prepaid"}};
357
358 for (const String& type : basicCard.supportedTypes()) {
359 for (size_t i = 0; i < arraysize(basicCardTypes); ++i) {
360 if (type == basicCardTypes[i].name) {
361 output->supported_types.append(basicCardTypes[i].code);
362 break;
363 }
364 }
365 }
366 }
367 }
368
369 void stringifyAndParseMethodSpecificData(const Vector<String>& supportedMethods,
370 const ScriptValue& input,
313 PaymentMethodDataPtr& output, 371 PaymentMethodDataPtr& output,
314 ExceptionState& exceptionState) { 372 ExceptionState& exceptionState) {
315 DCHECK(!input.isEmpty()); 373 DCHECK(!input.isEmpty());
316 if (!input.v8Value()->IsObject() || input.v8Value()->IsArray()) { 374 if (!input.v8Value()->IsObject() || input.v8Value()->IsArray()) {
317 exceptionState.throwTypeError("Data should be a JSON-serializable object"); 375 exceptionState.throwTypeError("Data should be a JSON-serializable object");
318 return; 376 return;
319 } 377 }
320 378
321 v8::Local<v8::String> value; 379 v8::Local<v8::String> value;
322 if (!v8::JSON::Stringify(input.context(), input.v8Value().As<v8::Object>()) 380 if (!v8::JSON::Stringify(input.context(), input.v8Value().As<v8::Object>())
323 .ToLocal(&value)) { 381 .ToLocal(&value)) {
324 exceptionState.throwTypeError( 382 exceptionState.throwTypeError(
325 "Unable to parse payment method specific data"); 383 "Unable to parse payment method specific data");
326 return; 384 return;
327 } 385 }
328 386
329 output->stringified_data = 387 output->stringified_data =
330 v8StringToWebCoreString<String>(value, DoNotExternalize); 388 v8StringToWebCoreString<String>(value, DoNotExternalize);
331 maybeSetAndroidPayMethodData(input, output, exceptionState); 389
390 // Serialize payment method specific data to be sent to the payment apps. The
391 // payment apps are responsible for validating and processing their method
392 // data asynchronously. Do not throw exceptions here.
393 if (supportedMethods.contains("https://android.com/pay")) {
394 setAndroidPayMethodData(input, output, exceptionState);
395 if (exceptionState.hadException())
396 exceptionState.clearException();
397 }
398 if (RuntimeEnabledFeatures::paymentRequestBasicCardEnabled() &&
399 supportedMethods.contains("basic-card")) {
400 setBasicCardMethodData(input, output, exceptionState);
401 if (exceptionState.hadException())
402 exceptionState.clearException();
403 }
332 } 404 }
333 405
334 void validateAndConvertPaymentDetailsModifiers( 406 void validateAndConvertPaymentDetailsModifiers(
335 const HeapVector<PaymentDetailsModifier>& input, 407 const HeapVector<PaymentDetailsModifier>& input,
336 Vector<PaymentDetailsModifierPtr>& output, 408 Vector<PaymentDetailsModifierPtr>& output,
337 ExceptionState& exceptionState) { 409 ExceptionState& exceptionState) {
338 if (input.isEmpty()) { 410 if (input.isEmpty()) {
339 exceptionState.throwTypeError( 411 exceptionState.throwTypeError(
340 "Must specify at least one payment details modifier"); 412 "Must specify at least one payment details modifier");
341 return; 413 return;
(...skipping 21 matching lines...) Expand all
363 "Must specify at least one payment method identifier"); 435 "Must specify at least one payment method identifier");
364 return; 436 return;
365 } 437 }
366 438
367 output.back()->method_data = 439 output.back()->method_data =
368 payments::mojom::blink::PaymentMethodData::New(); 440 payments::mojom::blink::PaymentMethodData::New();
369 output.back()->method_data->supported_methods = modifier.supportedMethods(); 441 output.back()->method_data->supported_methods = modifier.supportedMethods();
370 442
371 if (modifier.hasData() && !modifier.data().isEmpty()) { 443 if (modifier.hasData() && !modifier.data().isEmpty()) {
372 stringifyAndParseMethodSpecificData( 444 stringifyAndParseMethodSpecificData(
373 modifier.data(), output.back()->method_data, exceptionState); 445 modifier.supportedMethods(), modifier.data(),
446 output.back()->method_data, exceptionState);
374 } else { 447 } else {
375 output.back()->method_data->stringified_data = ""; 448 output.back()->method_data->stringified_data = "";
376 } 449 }
377 } 450 }
378 } 451 }
379 452
380 String getSelectedShippingOption( 453 String getSelectedShippingOption(
381 const Vector<PaymentShippingOptionPtr>& shippingOptions) { 454 const Vector<PaymentShippingOptionPtr>& shippingOptions) {
382 String result; 455 String result;
383 for (const PaymentShippingOptionPtr& shippingOption : shippingOptions) { 456 for (const PaymentShippingOptionPtr& shippingOption : shippingOptions) {
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
451 if (paymentMethodData.supportedMethods().isEmpty()) { 524 if (paymentMethodData.supportedMethods().isEmpty()) {
452 exceptionState.throwTypeError( 525 exceptionState.throwTypeError(
453 "Must specify at least one payment method identifier"); 526 "Must specify at least one payment method identifier");
454 return; 527 return;
455 } 528 }
456 529
457 output.push_back(payments::mojom::blink::PaymentMethodData::New()); 530 output.push_back(payments::mojom::blink::PaymentMethodData::New());
458 output.back()->supported_methods = paymentMethodData.supportedMethods(); 531 output.back()->supported_methods = paymentMethodData.supportedMethods();
459 532
460 if (paymentMethodData.hasData() && !paymentMethodData.data().isEmpty()) { 533 if (paymentMethodData.hasData() && !paymentMethodData.data().isEmpty()) {
461 stringifyAndParseMethodSpecificData(paymentMethodData.data(), 534 stringifyAndParseMethodSpecificData(paymentMethodData.supportedMethods(),
535 paymentMethodData.data(),
462 output.back(), exceptionState); 536 output.back(), exceptionState);
463 } else { 537 } else {
464 output.back()->stringified_data = ""; 538 output.back()->stringified_data = "";
465 } 539 }
466 } 540 }
467 } 541 }
468 542
469 String getValidShippingType(const String& shippingType) { 543 String getValidShippingType(const String& shippingType) {
470 static const char* const validValues[] = { 544 static const char* const validValues[] = {
471 "shipping", "delivery", "pickup", 545 "shipping", "delivery", "pickup",
(...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after
614 m_completeResolver = ScriptPromiseResolver::create(scriptState); 688 m_completeResolver = ScriptPromiseResolver::create(scriptState);
615 return m_completeResolver->promise(); 689 return m_completeResolver->promise();
616 } 690 }
617 691
618 void PaymentRequest::onUpdatePaymentDetails( 692 void PaymentRequest::onUpdatePaymentDetails(
619 const ScriptValue& detailsScriptValue) { 693 const ScriptValue& detailsScriptValue) {
620 if (!m_showResolver || !m_paymentProvider) 694 if (!m_showResolver || !m_paymentProvider)
621 return; 695 return;
622 696
623 PaymentDetails details; 697 PaymentDetails details;
624 DummyExceptionStateForTesting exceptionState; 698 ExceptionState exceptionState(v8::Isolate::GetCurrent(),
699 ExceptionState::ConstructionContext,
700 "PaymentDetails");
625 V8PaymentDetails::toImpl(detailsScriptValue.isolate(), 701 V8PaymentDetails::toImpl(detailsScriptValue.isolate(),
626 detailsScriptValue.v8Value(), details, 702 detailsScriptValue.v8Value(), details,
627 exceptionState); 703 exceptionState);
628 if (exceptionState.hadException()) { 704 if (exceptionState.hadException()) {
629 m_showResolver->reject( 705 m_showResolver->reject(
630 DOMException::create(SyntaxError, exceptionState.message())); 706 DOMException::create(SyntaxError, exceptionState.message()));
631 clearResolversAndCloseMojoConnection(); 707 clearResolversAndCloseMojoConnection();
632 return; 708 return;
633 } 709 }
634 710
(...skipping 286 matching lines...) Expand 10 before | Expand all | Expand 10 after
921 m_completeResolver.clear(); 997 m_completeResolver.clear();
922 m_showResolver.clear(); 998 m_showResolver.clear();
923 m_abortResolver.clear(); 999 m_abortResolver.clear();
924 m_canMakePaymentResolver.clear(); 1000 m_canMakePaymentResolver.clear();
925 if (m_clientBinding.is_bound()) 1001 if (m_clientBinding.is_bound())
926 m_clientBinding.Close(); 1002 m_clientBinding.Close();
927 m_paymentProvider.reset(); 1003 m_paymentProvider.reset();
928 } 1004 }
929 1005
930 } // namespace blink 1006 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698