425cbe57564dbc1ce4c21e6aa2bb26507b179f07
[WebKit-https.git] / Source / WebCore / Modules / applepay / ApplePaySession.cpp
1 /*
2  * Copyright (C) 2015, 2016 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "ApplePaySession.h"
28
29 #if ENABLE(APPLE_PAY)
30
31 #include "ApplePayPaymentAuthorizedEvent.h"
32 #include "ApplePayPaymentMethodSelectedEvent.h"
33 #include "ApplePayShippingContactSelectedEvent.h"
34 #include "ApplePayShippingMethodSelectedEvent.h"
35 #include "ApplePayValidateMerchantEvent.h"
36 #include "ArrayValue.h"
37 #include "DOMWindow.h"
38 #include "Dictionary.h"
39 #include "Document.h"
40 #include "DocumentLoader.h"
41 #include "EventNames.h"
42 #include "JSDOMPromise.h"
43 #include "JSMainThreadExecState.h"
44 #include "LinkIconCollector.h"
45 #include "LinkIconType.h"
46 #include "MainFrame.h"
47 #include "Page.h"
48 #include "PageConsoleClient.h"
49 #include "PaymentAuthorizationStatus.h"
50 #include "PaymentContact.h"
51 #include "PaymentCoordinator.h"
52 #include "PaymentMerchantSession.h"
53 #include "PaymentMethod.h"
54 #include "PaymentRequestValidator.h"
55 #include "ScriptController.h"
56 #include "SecurityOrigin.h"
57 #include "Settings.h"
58
59 namespace WebCore {
60
61 static bool parseDigit(UChar digit, bool isNegative, int64_t& amount)
62 {
63     if (!isASCIIDigit(digit))
64         return false;
65
66     int64_t digitValue = (digit - '0');
67
68     const int64_t maxMultiplier = std::numeric_limits<int64_t>::max() / 10;
69
70     // Check overflow.
71     if (amount > maxMultiplier || (amount == maxMultiplier && digitValue > (std::numeric_limits<int64_t>::max() % 10) + isNegative))
72         return false;
73
74     amount = amount * 10 + digitValue;
75     return true;
76 }
77
78 // The amount follows the regular expression -?[0-9]+(\.[0-9][0-9])?.
79 static std::optional<int64_t> parseAmount(const String& amountString)
80 {
81     int64_t amount = 0;
82
83     bool isNegative = false;
84
85     enum class State {
86         Start,
87         Sign,
88         Digit,
89         Dot,
90         DotDigit,
91         End,
92     };
93
94     State state = State::Start;
95
96     for (unsigned i = 0; i < amountString.length(); ++i) {
97         UChar c = amountString[i];
98
99         switch (state) {
100         case State::Start:
101             if (c == '-') {
102                 isNegative = true;
103                 state = State::Sign;
104                 break;
105             }
106
107             if (!parseDigit(c, isNegative, amount))
108                 return std::nullopt;
109             state = State::Digit;
110             break;
111
112         case State::Sign:
113             if (!parseDigit(c, isNegative, amount))
114                 return std::nullopt;
115             state = State::Digit;
116             break;
117
118         case State::Digit:
119             if (c == '.') {
120                 state = State::Dot;
121                 break;
122             }
123
124             if (!parseDigit(c, isNegative, amount))
125                 return std::nullopt;
126             break;
127
128         case State::Dot:
129             if (!parseDigit(c, isNegative, amount))
130                 return std::nullopt;
131
132             state = State::DotDigit;
133             break;
134
135         case State::DotDigit:
136             if (!parseDigit(c, isNegative, amount))
137                 return std::nullopt;
138
139             state = State::End;
140             break;
141             
142         case State::End:
143             return std::nullopt;
144         }
145     }
146     
147     if (state != State::Digit && state != State::DotDigit && state != State::End)
148         return std::nullopt;
149
150     if (state == State::DotDigit) {
151         // There was a single digit after the decimal point.
152         // FIXME: Handle this overflowing.
153         amount *= 10;
154     } else if (state == State::Digit) {
155         // There was no decimal point.
156         // FIXME: Handle this overflowing.
157         amount *= 100;
158     }
159
160     if (isNegative)
161         amount = -amount;
162
163     return amount;
164 }
165
166 static std::optional<PaymentRequest::ContactFields> createContactFields(DOMWindow& window, const ArrayValue& contactFieldsArray)
167 {
168     PaymentRequest::ContactFields result;
169
170     size_t contactFieldsCount;
171     if (!contactFieldsArray.length(contactFieldsCount))
172         return std::nullopt;
173
174     for (size_t i = 0; i < contactFieldsCount; ++i) {
175         String contactField;
176         if (!contactFieldsArray.get(i, contactField))
177             return std::nullopt;
178
179         if (contactField == "postalAddress")
180             result.postalAddress = true;
181         else if (contactField == "phone")
182             result.phone = true;
183         else if (contactField == "email")
184             result.email = true;
185         else if (contactField == "name")
186             result.name = true;
187         else {
188             auto message = makeString("\"" + contactField, "\" is not a valid contact field.");
189             window.printErrorMessage(message);
190             return std::nullopt;
191         }
192     }
193
194     return result;
195 }
196
197 static std::optional<PaymentRequest::LineItem::Type> toLineItemType(const String& type)
198 {
199     if (type == "pending")
200         return PaymentRequest::LineItem::Type::Pending;
201     if (type == "final")
202         return PaymentRequest::LineItem::Type::Final;
203
204     return std::nullopt;
205 }
206
207 static bool isValidLineItemPropertyName(const String& propertyName)
208 {
209     const char* validPropertyNames[] = {
210         "type",
211         "label",
212         "amount",
213     };
214
215     for (auto& validPropertyName : validPropertyNames) {
216         if (propertyName == validPropertyName)
217             return true;
218     }
219
220     return false;
221 }
222
223 static std::optional<PaymentRequest::LineItem> createLineItem(DOMWindow& window, const Dictionary& total)
224 {
225     Vector<String> propertyNames;
226     total.getOwnPropertyNames(propertyNames);
227
228     for (auto& propertyName : propertyNames) {
229         if (!isValidLineItemPropertyName(propertyName)) {
230             auto message = makeString("\"" + propertyName, "\" is not a valid line item property name.");
231             window.printErrorMessage(message);
232             return std::nullopt;
233         }
234     }
235
236     // Line item type defaults to Final.
237     PaymentRequest::LineItem result;
238
239     if (auto typeString = total.get<String>("type")) {
240         auto type = toLineItemType(*typeString);
241         if (!type) {
242             auto message = makeString("\"" + *typeString, "\" is not a valid line item type.");
243             window.printErrorMessage(message);
244             return std::nullopt;
245         }
246
247         result.type = *type;
248     }
249
250     if (auto label = total.get<String>("label"))
251         result.label = *label;
252     if (auto amountString = total.get<String>("amount")) {
253         if (auto amount = parseAmount(*amountString))
254             result.amount = *amount;
255         else {
256             auto message = makeString("\"" + *amountString, "\" is not a valid amount.");
257             window.printErrorMessage(message);
258             return std::nullopt;
259         }
260     }
261
262     return result;
263 }
264
265 static std::optional<Vector<PaymentRequest::LineItem>> createLineItems(DOMWindow& window, const ArrayValue& lineItemsArray)
266 {
267     Vector<PaymentRequest::LineItem> result;
268
269     size_t lineItemCount;
270     if (!lineItemsArray.length(lineItemCount))
271         return std::nullopt;
272
273     for (size_t i = 0; i < lineItemCount; ++i) {
274         Dictionary lineItemDictionary;
275         if (!lineItemsArray.get(i, lineItemDictionary))
276             return std::nullopt;
277
278         if (auto lineItem = createLineItem(window, lineItemDictionary))
279             result.append(*lineItem);
280     }
281
282     return result;
283 }
284
285 static std::optional<PaymentRequest::MerchantCapabilities> createMerchantCapabilities(DOMWindow& window, const ArrayValue& merchantCapabilitiesArray)
286 {
287     PaymentRequest::MerchantCapabilities result;
288
289     size_t merchantCapabilitiesCount;
290     if (!merchantCapabilitiesArray.length(merchantCapabilitiesCount))
291         return std::nullopt;
292
293     for (size_t i = 0; i < merchantCapabilitiesCount; ++i) {
294         String merchantCapability;
295         if (!merchantCapabilitiesArray.get(i, merchantCapability))
296             return std::nullopt;
297
298         if (merchantCapability == "supports3DS")
299             result.supports3DS = true;
300         else if (merchantCapability == "supportsEMV")
301             result.supportsEMV = true;
302         else if (merchantCapability == "supportsCredit")
303             result.supportsCredit = true;
304         else if (merchantCapability == "supportsDebit")
305             result.supportsDebit = true;
306         else {
307             auto message = makeString("\"" + merchantCapability, "\" is not a valid merchant capability.");
308             window.printErrorMessage(message);
309             return std::nullopt;
310         }
311     }
312
313     return result;
314 }
315
316 static std::optional<Vector<String>> createSupportedNetworks(unsigned version, DOMWindow& window, const ArrayValue& supportedNetworksArray)
317 {
318     Vector<String> result;
319
320     size_t supportedNetworksCount;
321     if (!supportedNetworksArray.length(supportedNetworksCount))
322         return std::nullopt;
323
324     for (size_t i = 0; i < supportedNetworksCount; ++i) {
325         String supportedNetwork;
326         if (!supportedNetworksArray.get(i, supportedNetwork))
327             return std::nullopt;
328
329         if (!PaymentRequest::isValidSupportedNetwork(version, supportedNetwork)) {
330             auto message = makeString("\"" + supportedNetwork, "\" is not a valid payment network.");
331             window.printErrorMessage(message);
332             return std::nullopt;
333         }
334
335         result.append(WTFMove(supportedNetwork));
336     }
337
338     return result;
339 }
340
341 static std::optional<PaymentRequest::ShippingType> toShippingType(const String& shippingTypeString)
342 {
343     if (shippingTypeString == "shipping")
344         return PaymentRequest::ShippingType::Shipping;
345     if (shippingTypeString == "delivery")
346         return PaymentRequest::ShippingType::Delivery;
347     if (shippingTypeString == "storePickup")
348         return PaymentRequest::ShippingType::StorePickup;
349     if (shippingTypeString == "servicePickup")
350         return PaymentRequest::ShippingType::ServicePickup;
351
352     return std::nullopt;
353 }
354
355 static bool isValidShippingMethodPropertyName(const String& propertyName)
356 {
357     const char* validPropertyNames[] = {
358         "label",
359         "detail",
360         "amount",
361         "identifier",
362     };
363
364     for (auto& validPropertyName : validPropertyNames) {
365         if (propertyName == validPropertyName)
366             return true;
367     }
368
369     return false;
370 }
371
372 static std::optional<PaymentRequest::ShippingMethod> createShippingMethod(DOMWindow& window, const Dictionary& shippingMethodDictionary)
373 {
374     Vector<String> propertyNames;
375     shippingMethodDictionary.getOwnPropertyNames(propertyNames);
376
377     for (auto& propertyName : propertyNames) {
378         if (!isValidShippingMethodPropertyName(propertyName)) {
379             auto message = makeString("\"" + propertyName, "\" is not a valid shipping method property name.");
380             window.printErrorMessage(message);
381             return std::nullopt;
382         }
383     }
384
385     PaymentRequest::ShippingMethod result;
386
387     auto label = shippingMethodDictionary.get<String>("label");
388     if (!label) {
389         window.printErrorMessage("Missing shipping method label.");
390         return std::nullopt;
391     }
392     result.label = *label;
393
394     auto detail = shippingMethodDictionary.get<String>("detail");
395     if (!detail) {
396         window.printErrorMessage("Missing shipping method detail.");
397         return std::nullopt;
398     }
399     result.detail = *detail;
400
401     auto amountString = shippingMethodDictionary.get<String>("amount");
402     if (!amountString) {
403         window.printErrorMessage("Missing shipping method amount.");
404         return std::nullopt;
405     }
406
407     if (auto amount = parseAmount(*amountString))
408         result.amount = *amount;
409     else {
410         auto message = makeString("\"" + *amountString, "\" is not a valid amount.");
411         window.printErrorMessage(message);
412         return std::nullopt;
413     }
414
415     auto identifier = shippingMethodDictionary.get<String>("identifier");
416     if (!identifier) {
417         window.printErrorMessage("Missing shipping method identifier.");
418         return std::nullopt;
419     }
420     result.identifier = *identifier;
421
422     return result;
423 }
424
425 static std::optional<Vector<PaymentRequest::ShippingMethod>> createShippingMethods(DOMWindow& window, const ArrayValue& shippingMethodsArray)
426 {
427     Vector<PaymentRequest::ShippingMethod> result;
428
429     size_t shippingMethodCount;
430     if (!shippingMethodsArray.length(shippingMethodCount))
431         return std::nullopt;
432
433     for (size_t i = 0; i < shippingMethodCount; ++i) {
434         Dictionary shippingMethodDictionary;
435         if (!shippingMethodsArray.get(i, shippingMethodDictionary))
436             return std::nullopt;
437
438         if (auto shippingMethod = createShippingMethod(window, shippingMethodDictionary))
439             result.append(*shippingMethod);
440         else
441             return std::nullopt;
442     }
443
444     return result;
445 }
446
447 static bool isValidPaymentRequestPropertyName(const String& propertyName)
448 {
449     const char* validPropertyNames[] = {
450         "merchantCapabilities",
451         "supportedNetworks",
452         "countryCode",
453         "currencyCode",
454         "requiredBillingContactFields",
455         "billingContact",
456         "requiredShippingContactFields",
457         "shippingContact",
458         "shippingType",
459         "shippingMethods",
460         "total",
461         "lineItems",
462         "applicationData",
463     };
464
465     for (auto& validPropertyName : validPropertyNames) {
466         if (propertyName == validPropertyName)
467             return true;
468     }
469
470     return false;
471 }
472
473 static std::optional<PaymentRequest> createPaymentRequest(unsigned version, DOMWindow& window, const Dictionary& dictionary)
474 {
475     PaymentRequest paymentRequest;
476
477     Vector<String> propertyNames;
478     dictionary.getOwnPropertyNames(propertyNames);
479
480     for (auto& propertyName : propertyNames) {
481         if (propertyName == "requiredShippingAddressFields") {
482             window.printErrorMessage("\"requiredShippingAddressFields\" has been deprecated. Please switch to \"requiredShippingContactFields\" instead.");
483             return std::nullopt;
484         }
485
486         if (propertyName == "requiredBillingAddressFields") {
487             window.printErrorMessage("\"requiredBillingAddressFields\" has been deprecated. Please switch to \"requiredBillingContactFields\" instead.");
488             return std::nullopt;
489         }
490
491         if (!isValidPaymentRequestPropertyName(propertyName)) {
492             auto message = makeString("\"" + propertyName, "\" is not a valid payment request property name.");
493             window.printErrorMessage(message);
494             return std::nullopt;
495         }
496     }
497
498     if (auto merchantCapabilitiesArray = dictionary.get<ArrayValue>("merchantCapabilities")) {
499         auto merchantCapabilities = createMerchantCapabilities(window, *merchantCapabilitiesArray);
500         if (!merchantCapabilities)
501             return std::nullopt;
502
503         paymentRequest.setMerchantCapabilities(*merchantCapabilities);
504     }
505
506     if (auto supportedNetworksArray = dictionary.get<ArrayValue>("supportedNetworks")) {
507         auto supportedNetworks = createSupportedNetworks(version, window, *supportedNetworksArray);
508         if (!supportedNetworks)
509             return std::nullopt;
510
511         paymentRequest.setSupportedNetworks(*supportedNetworks);
512     }
513
514     if (auto countryCode = dictionary.get<String>("countryCode"))
515         paymentRequest.setCountryCode(*countryCode);
516     if (auto currencyCode = dictionary.get<String>("currencyCode"))
517         paymentRequest.setCurrencyCode(*currencyCode);
518
519     if (auto requiredBillingContactFieldsArray = dictionary.get<ArrayValue>("requiredBillingContactFields")) {
520         auto requiredBillingContactFields = createContactFields(window, *requiredBillingContactFieldsArray);
521         if (!requiredBillingContactFields)
522             return std::nullopt;
523
524         paymentRequest.setRequiredBillingContactFields(*requiredBillingContactFields);
525     }
526
527     if (auto billingContactValue = dictionary.get<JSC::JSValue>("billingContact")) {
528         String errorMessage;
529         auto billingContact = PaymentContact::fromJS(*JSMainThreadExecState::currentState(), *billingContactValue, errorMessage);
530         if (!billingContact) {
531             window.printErrorMessage(errorMessage);
532             return std::nullopt;
533         }
534
535         paymentRequest.setBillingContact(*billingContact);
536     }
537
538     if (auto requiredShippingContactFieldsArray = dictionary.get<ArrayValue>("requiredShippingContactFields")) {
539         auto requiredShippingContactFields = createContactFields(window, *requiredShippingContactFieldsArray);
540         if (!requiredShippingContactFields)
541             return std::nullopt;
542
543         paymentRequest.setRequiredShippingContactFields(*requiredShippingContactFields);
544     }
545
546     if (auto shippingContactValue = dictionary.get<JSC::JSValue>("shippingContact")) {
547         String errorMessage;
548         auto shippingContact = PaymentContact::fromJS(*JSMainThreadExecState::currentState(), *shippingContactValue, errorMessage);
549         if (!shippingContact) {
550             window.printErrorMessage(errorMessage);
551             return std::nullopt;
552         }
553
554         paymentRequest.setShippingContact(*shippingContact);
555     }
556
557     if (auto shippingTypeString = dictionary.get<String>("shippingType")) {
558         auto shippingType = toShippingType(*shippingTypeString);
559
560         if (!shippingType) {
561             auto message = makeString("\"" + *shippingTypeString, "\" is not a valid shipping type.");
562             window.printErrorMessage(message);
563             return std::nullopt;
564         }
565         paymentRequest.setShippingType(*shippingType);
566     }
567
568     if (auto shippingMethodsArray = dictionary.get<ArrayValue>("shippingMethods")) {
569         auto shippingMethods = createShippingMethods(window, *shippingMethodsArray);
570         if (!shippingMethods)
571             return std::nullopt;
572
573         paymentRequest.setShippingMethods(*shippingMethods);
574     }
575
576     if (auto totalDictionary = dictionary.get<Dictionary>("total")) {
577         auto total = createLineItem(window, *totalDictionary);
578         if (!total)
579             return std::nullopt;
580
581         paymentRequest.setTotal(*total);
582     }
583
584     if (auto lineItemsArray = dictionary.get<ArrayValue>("lineItems")) {
585         if (auto lineItems = createLineItems(window, *lineItemsArray))
586             paymentRequest.setLineItems(*lineItems);
587     }
588
589     if (auto applicationData = dictionary.get<String>("applicationData"))
590         paymentRequest.setApplicationData(*applicationData);
591
592     return paymentRequest;
593 }
594
595 static bool isSecure(DocumentLoader& documentLoader)
596 {
597     if (!documentLoader.response().url().protocolIs("https"))
598         return false;
599
600     if (!documentLoader.response().certificateInfo() || documentLoader.response().certificateInfo()->containsNonRootSHA1SignedCertificate())
601         return false;
602
603     return true;
604 }
605
606 static bool canCallApplePaySessionAPIs(Document& document, String& errorMessage)
607 {
608     if (!isSecure(*document.loader())) {
609         errorMessage = "Trying to call an ApplePaySession API from an insecure document.";
610         return false;
611     }
612
613     auto& topDocument = document.topDocument();
614     if (&document != &topDocument) {
615         auto& topOrigin = *topDocument.topOrigin();
616
617         if (!document.securityOrigin()->isSameSchemeHostPort(&topOrigin)) {
618             errorMessage = "Trying to call an ApplePaySession API from a document with an different security origin than its top-level frame.";
619             return false;
620         }
621
622         for (auto* ancestorDocument = document.parentDocument(); ancestorDocument != &topDocument; ancestorDocument = ancestorDocument->parentDocument()) {
623             if (!isSecure(*ancestorDocument->loader())) {
624                 errorMessage = "Trying to call an ApplePaySession API from a document with an insecure parent frame.";
625                 return false;
626             }
627
628             if (!ancestorDocument->securityOrigin()->isSameSchemeHostPort(&topOrigin)) {
629                 errorMessage = "Trying to call an ApplePaySession API from a document with an different security origin than its top-level frame.";
630                 return false;
631             }
632         }
633     }
634
635     return true;
636 }
637
638 ExceptionOr<Ref<ApplePaySession>> ApplePaySession::create(Document& document, unsigned version, const Dictionary& dictionary)
639 {
640     DOMWindow& window = *document.domWindow();
641
642     String errorMessage;
643     if (!canCallApplePaySessionAPIs(document, errorMessage)) {
644         window.printErrorMessage(errorMessage);
645         return Exception { INVALID_ACCESS_ERR };
646     }
647
648     if (!ScriptController::processingUserGesture()) {
649         window.printErrorMessage("Must create a new ApplePaySession from a user gesture handler.");
650         return Exception { INVALID_ACCESS_ERR };
651     }
652
653     auto& paymentCoordinator = document.frame()->mainFrame().paymentCoordinator();
654
655     if (!version || !paymentCoordinator.supportsVersion(version)) {
656         window.printErrorMessage(makeString("\"" + String::number(version), "\" is not a supported version."));
657         return Exception { INVALID_ACCESS_ERR };
658     }
659
660     auto paymentRequest = createPaymentRequest(version, window, dictionary);
661     if (!paymentRequest)
662         return Exception { TYPE_MISMATCH_ERR };
663
664     if (!PaymentRequestValidator(window).validate(*paymentRequest))
665         return Exception { INVALID_ACCESS_ERR };
666
667     return adoptRef(*new ApplePaySession(document, WTFMove(*paymentRequest)));
668 }
669
670 ApplePaySession::ApplePaySession(Document& document, PaymentRequest&& paymentRequest)
671     : ActiveDOMObject(&document)
672     , m_paymentRequest(WTFMove(paymentRequest))
673 {
674     suspendIfNeeded();
675 }
676
677 ApplePaySession::~ApplePaySession()
678 {
679 }
680
681 ExceptionOr<bool> ApplePaySession::supportsVersion(ScriptExecutionContext& scriptExecutionContext, unsigned version)
682 {
683     if (!version)
684         return Exception { INVALID_ACCESS_ERR };
685
686     auto& document = downcast<Document>(scriptExecutionContext);
687     DOMWindow& window = *document.domWindow();
688
689     String errorMessage;
690     if (!canCallApplePaySessionAPIs(document, errorMessage)) {
691         window.printErrorMessage(errorMessage);
692         return Exception { INVALID_ACCESS_ERR };
693     }
694
695     return document.frame()->mainFrame().paymentCoordinator().supportsVersion(version);
696 }
697
698 static bool shouldDiscloseApplePayCapability(Document& document)
699 {
700     auto* page = document.page();
701     if (!page || page->usesEphemeralSession())
702         return false;
703
704     return document.frame()->settings().applePayCapabilityDisclosureAllowed();
705 }
706
707 ExceptionOr<bool> ApplePaySession::canMakePayments(ScriptExecutionContext& scriptExecutionContext)
708 {
709     auto& document = downcast<Document>(scriptExecutionContext);
710     DOMWindow& window = *document.domWindow();
711
712     String errorMessage;
713     if (!canCallApplePaySessionAPIs(document, errorMessage)) {
714         window.printErrorMessage(errorMessage);
715         return Exception { INVALID_ACCESS_ERR };
716     }
717
718     return document.frame()->mainFrame().paymentCoordinator().canMakePayments();
719 }
720
721 ExceptionOr<void> ApplePaySession::canMakePaymentsWithActiveCard(ScriptExecutionContext& scriptExecutionContext, const String& merchantIdentifier, Ref<DeferredPromise>&& passedPromise)
722 {
723     auto& document = downcast<Document>(scriptExecutionContext);
724     DOMWindow& window = *document.domWindow();
725
726     String errorMessage;
727     if (!canCallApplePaySessionAPIs(document, errorMessage)) {
728         window.printErrorMessage(errorMessage);
729         return Exception { INVALID_ACCESS_ERR };
730     }
731
732     RefPtr<DeferredPromise> promise(WTFMove(passedPromise));
733     if (!shouldDiscloseApplePayCapability(document)) {
734         auto& paymentCoordinator = document.frame()->mainFrame().paymentCoordinator();
735         bool canMakePayments = paymentCoordinator.canMakePayments();
736
737         RunLoop::main().dispatch([promise, canMakePayments]() mutable {
738             promise->resolve<IDLBoolean>(canMakePayments);
739         });
740         return { };
741     }
742
743     auto& paymentCoordinator = document.frame()->mainFrame().paymentCoordinator();
744
745     paymentCoordinator.canMakePaymentsWithActiveCard(merchantIdentifier, document.domain(), [promise](bool canMakePayments) mutable {
746         promise->resolve<IDLBoolean>(canMakePayments);
747     });
748     return { };
749 }
750
751 ExceptionOr<void> ApplePaySession::openPaymentSetup(ScriptExecutionContext& scriptExecutionContext, const String& merchantIdentifier, Ref<DeferredPromise>&& passedPromise)
752 {
753     auto& document = downcast<Document>(scriptExecutionContext);
754     DOMWindow& window = *document.domWindow();
755
756     String errorMessage;
757     if (!canCallApplePaySessionAPIs(document, errorMessage)) {
758         window.printErrorMessage(errorMessage);
759         return Exception { INVALID_ACCESS_ERR };
760     }
761
762     if (!ScriptController::processingUserGesture()) {
763         window.printErrorMessage("Must call ApplePaySession.openPaymentSetup from a user gesture handler.");
764         return Exception { INVALID_ACCESS_ERR };
765     }
766
767     RefPtr<DeferredPromise> promise(WTFMove(passedPromise));
768     auto& paymentCoordinator = document.frame()->mainFrame().paymentCoordinator();
769
770     paymentCoordinator.openPaymentSetup(merchantIdentifier, document.domain(), [promise](bool result) mutable {
771         promise->resolve<IDLBoolean>(result);
772     });
773
774     return { };
775 }
776
777 ExceptionOr<void> ApplePaySession::begin()
778 {
779     auto& document = *downcast<Document>(scriptExecutionContext());
780     auto& window = *document.domWindow();
781
782     if (!canBegin()) {
783         window.printErrorMessage("Payment session is already active.");
784         return Exception { INVALID_ACCESS_ERR };
785     }
786
787     if (paymentCoordinator().hasActiveSession()) {
788         window.printErrorMessage("Page already has an active payment session.");
789         return Exception { INVALID_ACCESS_ERR };
790     }
791
792     Vector<URL> linkIconURLs;
793     for (auto& icon : LinkIconCollector { document }.iconsOfTypes({ LinkIconType::TouchIcon, LinkIconType::TouchPrecomposedIcon }))
794         linkIconURLs.append(icon.url);
795
796     if (!paymentCoordinator().beginPaymentSession(*this, document.url(), linkIconURLs, m_paymentRequest)) {
797         window.printErrorMessage("There is already has an active payment session.");
798         return Exception { INVALID_ACCESS_ERR };
799     }
800
801     m_state = State::Active;
802
803     setPendingActivity(this);
804
805     return { };
806 }
807
808 ExceptionOr<void> ApplePaySession::abort()
809 {
810     if (!canAbort())
811         return Exception { INVALID_ACCESS_ERR };
812
813     m_state = State::Aborted;
814     paymentCoordinator().abortPaymentSession();
815
816     didReachFinalState();
817
818     return { };
819 }
820
821 ExceptionOr<void> ApplePaySession::completeMerchantValidation(const Dictionary& merchantSessionDictionary)
822 {
823     if (!canCompleteMerchantValidation())
824         return Exception { INVALID_ACCESS_ERR };
825
826     if (!merchantSessionDictionary.initializerObject())
827         return Exception { TypeError };
828
829     auto& document = *downcast<Document>(scriptExecutionContext());
830     auto& window = *document.domWindow();
831
832     String errorMessage;
833     auto merchantSession = PaymentMerchantSession::fromJS(*merchantSessionDictionary.execState(), merchantSessionDictionary.initializerObject(), errorMessage);
834     if (!merchantSession) {
835         window.printErrorMessage(errorMessage);
836         return Exception { INVALID_ACCESS_ERR };
837     }
838
839     m_merchantValidationState = MerchantValidationState::ValidationComplete;
840     paymentCoordinator().completeMerchantValidation(*merchantSession);
841
842     return { };
843 }
844
845 static std::optional<PaymentAuthorizationStatus> toPaymentAuthorizationStatus(unsigned short status)
846 {
847     switch (status) {
848     case ApplePaySession::STATUS_SUCCESS:
849         return PaymentAuthorizationStatus::Success;
850
851     case ApplePaySession::STATUS_FAILURE:
852         return PaymentAuthorizationStatus::Failure;
853
854     case ApplePaySession::STATUS_INVALID_BILLING_POSTAL_ADDRESS:
855         return PaymentAuthorizationStatus::InvalidBillingPostalAddress;
856
857     case ApplePaySession::STATUS_INVALID_SHIPPING_POSTAL_ADDRESS:
858         return PaymentAuthorizationStatus::InvalidShippingPostalAddress;
859
860     case ApplePaySession::STATUS_INVALID_SHIPPING_CONTACT:
861         return PaymentAuthorizationStatus::InvalidShippingContact;
862
863     case ApplePaySession::STATUS_PIN_REQUIRED:
864         return PaymentAuthorizationStatus::PINRequired;
865
866     case ApplePaySession::STATUS_PIN_INCORRECT:
867         return PaymentAuthorizationStatus::PINIncorrect;
868
869     case ApplePaySession::STATUS_PIN_LOCKOUT:
870         return PaymentAuthorizationStatus::PINLockout;
871
872     default:
873         return std::nullopt;
874     }
875 }
876
877 ExceptionOr<void> ApplePaySession::completeShippingMethodSelection(unsigned short status, const Dictionary& newTotalDictionary, const ArrayValue& newLineItemsArray)
878 {
879     if (!canCompleteShippingMethodSelection())
880         return Exception { INVALID_ACCESS_ERR };
881
882     auto authorizationStatus = toPaymentAuthorizationStatus(status);
883     if (!authorizationStatus)
884         return Exception { INVALID_ACCESS_ERR };
885
886     auto& window = *downcast<Document>(scriptExecutionContext())->domWindow();
887     auto newTotal = createLineItem(window, newTotalDictionary);
888     if (!newTotal)
889         return Exception { INVALID_ACCESS_ERR };
890
891     if (!PaymentRequestValidator(window).validateTotal(*newTotal))
892         return Exception { INVALID_ACCESS_ERR };
893
894     auto newLineItems = createLineItems(window, newLineItemsArray);
895     if (!newLineItems)
896         return Exception { INVALID_ACCESS_ERR };
897
898     m_state = State::Active;
899     PaymentRequest::TotalAndLineItems totalAndLineItems;
900     totalAndLineItems.total = *newTotal;
901     totalAndLineItems.lineItems = *newLineItems;
902     paymentCoordinator().completeShippingMethodSelection(*authorizationStatus, totalAndLineItems);
903
904     return { };
905 }
906
907 ExceptionOr<void> ApplePaySession::completeShippingContactSelection(unsigned short status, const ArrayValue& newShippingMethodsArray, const Dictionary& newTotalDictionary, const ArrayValue& newLineItemsArray)
908 {
909     if (!canCompleteShippingContactSelection())
910         return Exception { INVALID_ACCESS_ERR };
911
912     auto authorizationStatus = toPaymentAuthorizationStatus(status);
913     if (!authorizationStatus)
914         return Exception { INVALID_ACCESS_ERR };
915
916     auto& window = *downcast<Document>(scriptExecutionContext())->domWindow();
917
918     auto newShippingMethods = createShippingMethods(window, newShippingMethodsArray);
919     if (!newShippingMethods)
920         return Exception { INVALID_ACCESS_ERR };
921
922     auto newTotal = createLineItem(window, newTotalDictionary);
923     if (!newTotal)
924         return Exception { INVALID_ACCESS_ERR };
925
926     if (!PaymentRequestValidator(window).validateTotal(*newTotal))
927         return Exception { INVALID_ACCESS_ERR };
928
929     auto newLineItems = createLineItems(window, newLineItemsArray);
930     if (!newLineItems)
931         return Exception { INVALID_ACCESS_ERR };
932
933     m_state = State::Active;
934     PaymentRequest::TotalAndLineItems totalAndLineItems;
935     totalAndLineItems.total = *newTotal;
936     totalAndLineItems.lineItems = *newLineItems;
937     paymentCoordinator().completeShippingContactSelection(*authorizationStatus, *newShippingMethods, totalAndLineItems);
938
939     return { };
940 }
941
942 ExceptionOr<void> ApplePaySession::completePaymentMethodSelection(const Dictionary& newTotalDictionary, const ArrayValue& newLineItemsArray)
943 {
944     if (!canCompletePaymentMethodSelection())
945         return Exception { INVALID_ACCESS_ERR };
946
947     auto& window = *downcast<Document>(*scriptExecutionContext()).domWindow();
948     auto newTotal = createLineItem(window, newTotalDictionary);
949     if (!newTotal)
950         return Exception { INVALID_ACCESS_ERR };
951
952     if (!PaymentRequestValidator(window).validateTotal(*newTotal))
953         return Exception { INVALID_ACCESS_ERR };
954
955     auto newLineItems = createLineItems(window, newLineItemsArray);
956     if (!newLineItems)
957         return Exception { INVALID_ACCESS_ERR };
958
959     m_state = State::Active;
960     PaymentRequest::TotalAndLineItems totalAndLineItems;
961     totalAndLineItems.total = *newTotal;
962     totalAndLineItems.lineItems = *newLineItems;
963     paymentCoordinator().completePaymentMethodSelection(totalAndLineItems);
964
965     return { };
966 }
967
968 ExceptionOr<void> ApplePaySession::completePayment(unsigned short status)
969 {
970     if (!canCompletePayment())
971         return Exception { INVALID_ACCESS_ERR };
972
973     auto authorizationStatus = toPaymentAuthorizationStatus(status);
974     if (!authorizationStatus)
975         return Exception { INVALID_ACCESS_ERR };
976
977     paymentCoordinator().completePaymentSession(*authorizationStatus);
978
979     if (!isFinalStateStatus(*authorizationStatus)) {
980         m_state = State::Active;
981         return { };
982     }
983
984     m_state = State::Completed;
985     unsetPendingActivity(this);
986     return { };
987 }
988
989 void ApplePaySession::validateMerchant(const URL& validationURL)
990 {
991     if (m_state == State::Aborted) {
992         // ApplePaySession::abort has been called.
993         return;
994     }
995
996     ASSERT(m_merchantValidationState == MerchantValidationState::Idle);
997     ASSERT(m_state == State::Active);
998
999     if (validationURL.isNull()) {
1000         // Something went wrong when getting the validation URL.
1001         // FIXME: Maybe we should send an error event here instead?
1002         return;
1003     }
1004
1005     m_merchantValidationState = MerchantValidationState::ValidatingMerchant;
1006
1007     auto event = ApplePayValidateMerchantEvent::create(eventNames().validatemerchantEvent, validationURL);
1008     dispatchEvent(event.get());
1009 }
1010
1011 void ApplePaySession::didAuthorizePayment(const Payment& payment)
1012 {
1013     ASSERT(m_state == State::Active);
1014
1015     m_state = State::Authorized;
1016
1017     auto event = ApplePayPaymentAuthorizedEvent::create(eventNames().paymentauthorizedEvent, payment);
1018     dispatchEvent(event.get());
1019 }
1020
1021 void ApplePaySession::didSelectShippingMethod(const PaymentRequest::ShippingMethod& shippingMethod)
1022 {
1023     ASSERT(m_state == State::Active);
1024
1025     if (!hasEventListeners(eventNames().shippingmethodselectedEvent)) {
1026         paymentCoordinator().completeShippingMethodSelection(PaymentAuthorizationStatus::Success, { });
1027         return;
1028     }
1029
1030     m_state = State::ShippingMethodSelected;
1031     auto event = ApplePayShippingMethodSelectedEvent::create(eventNames().shippingmethodselectedEvent, shippingMethod);
1032     dispatchEvent(event.get());
1033 }
1034
1035 void ApplePaySession::didSelectShippingContact(const PaymentContact& shippingContact)
1036 {
1037     ASSERT(m_state == State::Active);
1038
1039     if (!hasEventListeners(eventNames().shippingcontactselectedEvent)) {
1040         paymentCoordinator().completeShippingContactSelection(PaymentAuthorizationStatus::Success, { }, { });
1041         return;
1042     }
1043
1044     m_state = State::ShippingContactSelected;
1045     auto event = ApplePayShippingContactSelectedEvent::create(eventNames().shippingcontactselectedEvent, shippingContact);
1046     dispatchEvent(event.get());
1047 }
1048
1049 void ApplePaySession::didSelectPaymentMethod(const PaymentMethod& paymentMethod)
1050 {
1051     ASSERT(m_state == State::Active);
1052
1053     if (!hasEventListeners(eventNames().paymentmethodselectedEvent)) {
1054         paymentCoordinator().completePaymentMethodSelection({ });
1055         return;
1056     }
1057
1058     m_state = State::PaymentMethodSelected;
1059     auto event = ApplePayPaymentMethodSelectedEvent::create(eventNames().paymentmethodselectedEvent, paymentMethod);
1060     dispatchEvent(event.get());
1061 }
1062
1063 void ApplePaySession::didCancelPayment()
1064 {
1065     ASSERT(canCancel());
1066
1067     m_state = State::Canceled;
1068
1069     auto event = Event::create(eventNames().cancelEvent, false, false);
1070     dispatchEvent(event.get());
1071
1072     didReachFinalState();
1073 }
1074
1075 const char* ApplePaySession::activeDOMObjectName() const
1076 {
1077     return "ApplePaySession";
1078 }
1079
1080 bool ApplePaySession::canSuspendForDocumentSuspension() const
1081 {
1082     switch (m_state) {
1083     case State::Idle:
1084     case State::Aborted:
1085     case State::Completed:
1086     case State::Canceled:
1087         return true;
1088
1089     case State::Active:
1090     case State::Authorized:
1091     case State::ShippingMethodSelected:
1092     case State::ShippingContactSelected:
1093     case State::PaymentMethodSelected:
1094         return false;
1095     }
1096 }
1097
1098 void ApplePaySession::stop()
1099 {
1100     if (!canAbort())
1101         return;
1102
1103     m_state = State::Aborted;
1104     paymentCoordinator().abortPaymentSession();
1105
1106     didReachFinalState();
1107 }
1108
1109 PaymentCoordinator& ApplePaySession::paymentCoordinator() const
1110 {
1111     return downcast<Document>(*scriptExecutionContext()).frame()->mainFrame().paymentCoordinator();
1112 }
1113
1114 bool ApplePaySession::canBegin() const
1115 {
1116     switch (m_state) {
1117     case State::Idle:
1118         return true;
1119
1120     case State::Active:
1121     case State::Aborted:
1122     case State::Authorized:
1123     case State::Completed:
1124     case State::Canceled:
1125     case State::ShippingMethodSelected:
1126     case State::ShippingContactSelected:
1127     case State::PaymentMethodSelected:
1128         return false;
1129     }
1130 }
1131
1132 bool ApplePaySession::canAbort() const
1133 {
1134     switch (m_state) {
1135     case State::Idle:
1136     case State::Aborted:
1137     case State::Completed:
1138     case State::Canceled:
1139         return false;
1140
1141     case State::Active:
1142     case State::Authorized:
1143     case State::ShippingMethodSelected:
1144     case State::ShippingContactSelected:
1145     case State::PaymentMethodSelected:
1146         return true;
1147     }
1148 }
1149
1150 bool ApplePaySession::canCancel() const
1151 {
1152     switch (m_state) {
1153     case State::Idle:
1154     case State::Aborted:
1155     case State::Completed:
1156     case State::Canceled:
1157         return false;
1158
1159     case State::Active:
1160     case State::Authorized:
1161     case State::ShippingMethodSelected:
1162     case State::ShippingContactSelected:
1163     case State::PaymentMethodSelected:
1164         return true;
1165     }
1166 }
1167
1168 bool ApplePaySession::canCompleteMerchantValidation() const
1169 {
1170     if (m_state != State::Active)
1171         return false;
1172
1173     if (m_merchantValidationState != MerchantValidationState::ValidatingMerchant)
1174         return false;
1175
1176     return true;
1177 }
1178
1179 bool ApplePaySession::canCompleteShippingMethodSelection() const
1180 {
1181     switch (m_state) {
1182     case State::Idle:
1183     case State::Aborted:
1184     case State::Active:
1185     case State::Completed:
1186     case State::Canceled:
1187     case State::Authorized:
1188     case State::PaymentMethodSelected:
1189     case State::ShippingContactSelected:
1190         return false;
1191
1192     case State::ShippingMethodSelected:
1193         return true;
1194     }
1195 }
1196
1197 bool ApplePaySession::canCompleteShippingContactSelection() const
1198 {
1199     switch (m_state) {
1200     case State::Idle:
1201     case State::Aborted:
1202     case State::Active:
1203     case State::Completed:
1204     case State::Canceled:
1205     case State::Authorized:
1206     case State::PaymentMethodSelected:
1207     case State::ShippingMethodSelected:
1208         return false;
1209
1210     case State::ShippingContactSelected:
1211         return true;
1212     }
1213 }
1214
1215 bool ApplePaySession::canCompletePaymentMethodSelection() const
1216 {
1217     switch (m_state) {
1218     case State::Idle:
1219     case State::Aborted:
1220     case State::Active:
1221     case State::Completed:
1222     case State::Canceled:
1223     case State::Authorized:
1224     case State::ShippingMethodSelected:
1225     case State::ShippingContactSelected:
1226         return false;
1227
1228     case State::PaymentMethodSelected:
1229         return true;
1230     }
1231 }
1232
1233 bool ApplePaySession::canCompletePayment() const
1234 {
1235     switch (m_state) {
1236     case State::Idle:
1237     case State::Aborted:
1238     case State::Active:
1239     case State::Completed:
1240     case State::Canceled:
1241     case State::ShippingMethodSelected:
1242     case State::ShippingContactSelected:
1243     case State::PaymentMethodSelected:
1244         return false;
1245
1246     case State::Authorized:
1247         return true;
1248     }
1249 }
1250
1251 bool ApplePaySession::isFinalState() const
1252 {
1253     switch (m_state) {
1254     case State::Idle:
1255     case State::Active:
1256     case State::ShippingMethodSelected:
1257     case State::ShippingContactSelected:
1258     case State::PaymentMethodSelected:
1259     case State::Authorized:
1260         return false;
1261
1262     case State::Completed:
1263     case State::Aborted:
1264     case State::Canceled:
1265         return true;
1266     }
1267 }
1268
1269 void ApplePaySession::didReachFinalState()
1270 {
1271     ASSERT(isFinalState());
1272     unsetPendingActivity(this);
1273 }
1274
1275 }
1276
1277 #endif