Use "= default" to denote default constructor or destructor
[WebKit-https.git] / Source / WebCore / Modules / applepay / ApplePaySession.cpp
1 /*
2  * Copyright (C) 2015-2017 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 "ApplePayLineItem.h"
32 #include "ApplePayPaymentAuthorizationResult.h"
33 #include "ApplePayPaymentAuthorizedEvent.h"
34 #include "ApplePayPaymentMethodSelectedEvent.h"
35 #include "ApplePayPaymentMethodUpdate.h"
36 #include "ApplePayPaymentRequest.h"
37 #include "ApplePayShippingContactSelectedEvent.h"
38 #include "ApplePayShippingContactUpdate.h"
39 #include "ApplePayShippingMethod.h"
40 #include "ApplePayShippingMethodSelectedEvent.h"
41 #include "ApplePayShippingMethodUpdate.h"
42 #include "ApplePayValidateMerchantEvent.h"
43 #include "Document.h"
44 #include "DocumentLoader.h"
45 #include "EventNames.h"
46 #include "JSDOMPromiseDeferred.h"
47 #include "LinkIconCollector.h"
48 #include "LinkIconType.h"
49 #include "MainFrame.h"
50 #include "Page.h"
51 #include "PageConsoleClient.h"
52 #include "PaymentAuthorizationStatus.h"
53 #include "PaymentContact.h"
54 #include "PaymentCoordinator.h"
55 #include "PaymentMerchantSession.h"
56 #include "PaymentMethod.h"
57 #include "PaymentRequestValidator.h"
58 #include "ScriptController.h"
59 #include "SecurityOrigin.h"
60 #include "Settings.h"
61
62 namespace WebCore {
63
64 // The amount follows the regular expression -?[0-9]+(\.[0-9][0-9])?.
65 static bool validateAmount(const String& amountString)
66 {
67     enum class State {
68         Start,
69         Sign,
70         Digit,
71         Dot,
72         DotDigit,
73         End,
74     };
75
76     State state = State::Start;
77
78     for (unsigned i = 0; i < amountString.length(); ++i) {
79         UChar c = amountString[i];
80
81         switch (state) {
82         case State::Start:
83             if (c == '-') {
84                 state = State::Sign;
85                 break;
86             }
87
88             if (!isASCIIDigit(c))
89                 return false;
90             state = State::Digit;
91             break;
92
93         case State::Sign:
94             if (!isASCIIDigit(c))
95                 return false;
96             state = State::Digit;
97             break;
98
99         case State::Digit:
100             if (c == '.') {
101                 state = State::Dot;
102                 break;
103             }
104
105             if (!isASCIIDigit(c))
106                 return false;
107             break;
108
109         case State::Dot:
110             if (!isASCIIDigit(c))
111                 return false;
112
113             state = State::DotDigit;
114             break;
115
116         case State::DotDigit:
117             if (!isASCIIDigit(c))
118                 return false;
119
120             state = State::End;
121             break;
122
123         case State::End:
124             return false;
125         }
126     }
127
128     return state == State::Digit || state == State::DotDigit || state == State::End;
129 }
130
131 static ExceptionOr<ApplePaySessionPaymentRequest::LineItem> convertAndValidateTotal(ApplePayLineItem&& lineItem)
132 {
133     if (!validateAmount(lineItem.amount))
134         return Exception { TypeError, makeString("\"" + lineItem.amount, "\" is not a valid amount.") };
135
136     ApplePaySessionPaymentRequest::LineItem result;
137     result.amount = lineItem.amount;
138     result.type = lineItem.type;
139     result.label = lineItem.label;
140
141     return WTFMove(result);
142 }
143
144 static ExceptionOr<ApplePaySessionPaymentRequest::LineItem> convertAndValidate(ApplePayLineItem&& lineItem)
145 {
146     ApplePaySessionPaymentRequest::LineItem result;
147
148     // It is OK for pending types to not have an amount.
149     if (lineItem.type != ApplePaySessionPaymentRequest::LineItem::Type::Pending) {
150         if (!validateAmount(lineItem.amount))
151             return Exception { TypeError, makeString("\"" + lineItem.amount, "\" is not a valid amount.") };
152
153         result.amount = lineItem.amount;
154     }
155
156     result.type = lineItem.type;
157     result.label = lineItem.label;
158
159     return WTFMove(result);
160 }
161
162 static ExceptionOr<Vector<ApplePaySessionPaymentRequest::LineItem>> convertAndValidate(std::optional<Vector<ApplePayLineItem>>&& lineItems)
163 {
164     Vector<ApplePaySessionPaymentRequest::LineItem> result;
165     if (!lineItems)
166         return WTFMove(result);
167
168     result.reserveInitialCapacity(lineItems->size());
169     
170     for (auto lineItem : lineItems.value()) {
171         auto convertedLineItem = convertAndValidate(WTFMove(lineItem));
172         if (convertedLineItem.hasException())
173             return convertedLineItem.releaseException();
174         result.uncheckedAppend(convertedLineItem.releaseReturnValue());
175     }
176
177     return WTFMove(result);
178 }
179
180 static ExceptionOr<ApplePaySessionPaymentRequest::ShippingMethod> convertAndValidate(ApplePayShippingMethod&& shippingMethod)
181 {
182     if (!validateAmount(shippingMethod.amount))
183         return Exception { TypeError, makeString("\"" + shippingMethod.amount, "\" is not a valid amount.") };
184
185     ApplePaySessionPaymentRequest::ShippingMethod result;
186     result.amount = shippingMethod.amount;
187     result.label = shippingMethod.label;
188     result.detail = shippingMethod.detail;
189     result.identifier = shippingMethod.identifier;
190
191     return WTFMove(result);
192 }
193
194 static ExceptionOr<Vector<ApplePaySessionPaymentRequest::ShippingMethod>> convertAndValidate(Vector<ApplePayShippingMethod>&& shippingMethods)
195 {
196     Vector<ApplePaySessionPaymentRequest::ShippingMethod> result;
197     result.reserveInitialCapacity(shippingMethods.size());
198     
199     for (auto& shippingMethod : shippingMethods) {
200         auto convertedShippingMethod = convertAndValidate(WTFMove(shippingMethod));
201         if (convertedShippingMethod.hasException())
202             return convertedShippingMethod.releaseException();
203         result.uncheckedAppend(convertedShippingMethod.releaseReturnValue());
204     }
205
206     return WTFMove(result);
207 }
208
209 static ExceptionOr<ApplePaySessionPaymentRequest> convertAndValidate(unsigned version, ApplePayPaymentRequest&& paymentRequest)
210 {
211     auto convertedRequest = convertAndValidate(version, paymentRequest);
212     if (convertedRequest.hasException())
213         return convertedRequest.releaseException();
214
215     auto result = convertedRequest.releaseReturnValue();
216     result.setCurrencyCode(paymentRequest.currencyCode);
217
218     auto total = convertAndValidateTotal(WTFMove(paymentRequest.total));
219     if (total.hasException())
220         return total.releaseException();
221     result.setTotal(total.releaseReturnValue());
222
223     auto lineItems = convertAndValidate(WTFMove(paymentRequest.lineItems));
224     if (lineItems.hasException())
225         return lineItems.releaseException();
226     result.setLineItems(lineItems.releaseReturnValue());
227
228     if (paymentRequest.requiredShippingContactFields) {
229         auto requiredShippingContactFields = convertAndValidate(version, WTFMove(*paymentRequest.requiredShippingContactFields));
230         if (requiredShippingContactFields.hasException())
231             return requiredShippingContactFields.releaseException();
232         result.setRequiredShippingContactFields(requiredShippingContactFields.releaseReturnValue());
233     }
234
235     if (paymentRequest.shippingContact)
236         result.setShippingContact(PaymentContact::fromApplePayPaymentContact(version, paymentRequest.shippingContact.value()));
237
238     result.setShippingType(paymentRequest.shippingType);
239
240     if (paymentRequest.shippingMethods) {
241         auto shippingMethods = convertAndValidate(WTFMove(*paymentRequest.shippingMethods));
242         if (shippingMethods.hasException())
243             return shippingMethods.releaseException();
244         result.setShippingMethods(shippingMethods.releaseReturnValue());
245     }
246
247     // FIXME: Merge this validation into the validation we are doing above.
248     auto validatedPaymentRequest = PaymentRequestValidator::validate(result);
249     if (validatedPaymentRequest.hasException())
250         return validatedPaymentRequest.releaseException();
251
252     return WTFMove(result);
253 }
254
255
256 static Vector<PaymentError> convert(const Vector<RefPtr<ApplePayError>>& errors)
257 {
258     Vector<PaymentError> convertedErrors;
259
260     for (auto& error : errors) {
261         PaymentError convertedError;
262
263         convertedError.code = error->code();
264         convertedError.message = error->message();
265         convertedError.contactField = error->contactField();
266
267         convertedErrors.append(convertedError);
268     }
269
270     return convertedErrors;
271 }
272
273 static ExceptionOr<PaymentAuthorizationResult> convertAndValidate(ApplePayPaymentAuthorizationResult&& result)
274 {
275     PaymentAuthorizationResult convertedResult;
276
277     std::optional<ApplePayError::Code> errorCode;
278     std::optional<ApplePayError::ContactField> contactField;
279
280     switch (result.status) {
281     case ApplePaySession::STATUS_SUCCESS:
282         convertedResult.status = PaymentAuthorizationStatus::Success;
283         break;
284
285     case ApplePaySession::STATUS_FAILURE:
286         convertedResult.status = PaymentAuthorizationStatus::Failure;
287         break;
288
289     case ApplePaySession::STATUS_INVALID_BILLING_POSTAL_ADDRESS:
290         convertedResult.status = PaymentAuthorizationStatus::Failure;
291         convertedResult.errors.append({ PaymentError::Code::BillingContactInvalid, { }, std::nullopt });
292         break;
293
294     case ApplePaySession::STATUS_INVALID_SHIPPING_POSTAL_ADDRESS:
295         convertedResult.status = PaymentAuthorizationStatus::Failure;
296         convertedResult.errors.append({ PaymentError::Code::ShippingContactInvalid, { }, PaymentError::ContactField::PostalAddress });
297         break;
298
299     case ApplePaySession::STATUS_INVALID_SHIPPING_CONTACT:
300         convertedResult.status = PaymentAuthorizationStatus::Failure;
301         convertedResult.errors.append({ PaymentError::Code::ShippingContactInvalid, { }, std::nullopt });
302         break;
303
304     case ApplePaySession::STATUS_PIN_REQUIRED:
305         convertedResult.status = PaymentAuthorizationStatus::PINRequired;
306         break;
307
308     case ApplePaySession::STATUS_PIN_INCORRECT:
309         convertedResult.status = PaymentAuthorizationStatus::PINIncorrect;
310         break;
311
312     case ApplePaySession::STATUS_PIN_LOCKOUT:
313         convertedResult.status = PaymentAuthorizationStatus::PINLockout;
314         break;
315
316     default:
317         return Exception { InvalidAccessError };
318     }
319
320     convertedResult.errors.appendVector(convert(result.errors));
321
322     return WTFMove(convertedResult);
323 }
324
325 static ExceptionOr<PaymentMethodUpdate> convertAndValidate(ApplePayPaymentMethodUpdate&& update)
326 {
327     PaymentMethodUpdate convertedUpdate;
328
329     auto convertedNewTotal = convertAndValidateTotal(WTFMove(update.newTotal));
330     if (convertedNewTotal.hasException())
331         return convertedNewTotal.releaseException();
332     convertedUpdate.newTotalAndLineItems.total = convertedNewTotal.releaseReturnValue();
333
334     // FIXME: Merge this validation into the validation we are doing above.
335     auto validatedTotal = PaymentRequestValidator::validateTotal(convertedUpdate.newTotalAndLineItems.total);
336     if (validatedTotal.hasException())
337         return validatedTotal.releaseException();
338
339     auto convertedNewLineItems = convertAndValidate(WTFMove(update.newLineItems));
340     if (convertedNewLineItems.hasException())
341         return convertedNewLineItems.releaseException();
342
343     convertedUpdate.newTotalAndLineItems.lineItems = convertedNewLineItems.releaseReturnValue();
344
345     return WTFMove(convertedUpdate);
346 }
347
348 static ExceptionOr<ShippingContactUpdate> convertAndValidate(ApplePayShippingContactUpdate&& update)
349 {
350     ShippingContactUpdate convertedUpdate;
351
352     convertedUpdate.errors = convert(update.errors);
353
354     auto convertedNewShippingMethods = convertAndValidate(WTFMove(update.newShippingMethods));
355     if (convertedNewShippingMethods.hasException())
356         return convertedNewShippingMethods.releaseException();
357     convertedUpdate.newShippingMethods = convertedNewShippingMethods.releaseReturnValue();
358
359     auto convertedNewTotal = convertAndValidateTotal(WTFMove(update.newTotal));
360     if (convertedNewTotal.hasException())
361         return convertedNewTotal.releaseException();
362     convertedUpdate.newTotalAndLineItems.total = convertedNewTotal.releaseReturnValue();
363
364     // FIXME: Merge this validation into the validation we are doing above.
365     auto validatedTotal = PaymentRequestValidator::validateTotal(convertedUpdate.newTotalAndLineItems.total);
366     if (validatedTotal.hasException())
367         return validatedTotal.releaseException();
368
369     auto convertedNewLineItems = convertAndValidate(WTFMove(update.newLineItems));
370     if (convertedNewLineItems.hasException())
371         return convertedNewLineItems.releaseException();
372     convertedUpdate.newTotalAndLineItems.lineItems = convertedNewLineItems.releaseReturnValue();
373
374     return WTFMove(convertedUpdate);
375 }
376
377 static ExceptionOr<ShippingMethodUpdate> convertAndValidate(ApplePayShippingMethodUpdate&& update)
378 {
379     ShippingMethodUpdate convertedUpdate;
380
381     auto convertedNewTotal = convertAndValidateTotal(WTFMove(update.newTotal));
382     if (convertedNewTotal.hasException())
383         return convertedNewTotal.releaseException();
384
385     convertedUpdate.newTotalAndLineItems.total = convertedNewTotal.releaseReturnValue();
386
387     // FIXME: Merge this validation into the validation we are doing above.
388     auto validatedTotal = PaymentRequestValidator::validateTotal(convertedUpdate.newTotalAndLineItems.total);
389     if (validatedTotal.hasException())
390         return validatedTotal.releaseException();
391
392     auto convertedNewLineItems = convertAndValidate(WTFMove(update.newLineItems));
393     if (convertedNewLineItems.hasException())
394         return convertedNewLineItems.releaseException();
395
396     convertedUpdate.newTotalAndLineItems.lineItems = convertedNewLineItems.releaseReturnValue();
397
398     return WTFMove(convertedUpdate);
399 }
400
401 static bool isSecure(DocumentLoader& documentLoader)
402 {
403     if (!documentLoader.response().url().protocolIs("https"))
404         return false;
405
406     if (!documentLoader.response().certificateInfo() || documentLoader.response().certificateInfo()->containsNonRootSHA1SignedCertificate())
407         return false;
408
409     return true;
410 }
411
412 static ExceptionOr<void> canCallApplePaySessionAPIs(Document& document)
413 {
414     if (!isSecure(*document.loader()))
415         return Exception { InvalidAccessError, "Trying to call an ApplePaySession API from an insecure document." };
416
417     auto& topDocument = document.topDocument();
418     if (&document != &topDocument) {
419         auto& topOrigin = topDocument.topOrigin();
420
421         if (!document.securityOrigin().isSameSchemeHostPort(topOrigin))
422             return Exception { InvalidAccessError, "Trying to call an ApplePaySession API from a document with an different security origin than its top-level frame." };
423
424         for (auto* ancestorDocument = document.parentDocument(); ancestorDocument != &topDocument; ancestorDocument = ancestorDocument->parentDocument()) {
425             if (!isSecure(*ancestorDocument->loader()))
426                 return Exception { InvalidAccessError, "Trying to call an ApplePaySession API from a document with an insecure parent frame." };
427
428             if (!ancestorDocument->securityOrigin().isSameSchemeHostPort(topOrigin))
429                 return Exception { InvalidAccessError, "Trying to call an ApplePaySession API from a document with an different security origin than its top-level frame." };
430         }
431     }
432
433     return { };
434 }
435
436 ExceptionOr<Ref<ApplePaySession>> ApplePaySession::create(Document& document, unsigned version, ApplePayPaymentRequest&& paymentRequest)
437 {
438     auto canCall = canCallApplePaySessionAPIs(document);
439     if (canCall.hasException())
440         return canCall.releaseException();
441
442     if (!ScriptController::processingUserGesture())
443         return Exception { InvalidAccessError, "Must create a new ApplePaySession from a user gesture handler." };
444
445     auto& paymentCoordinator = document.frame()->mainFrame().paymentCoordinator();
446
447     if (!version || !paymentCoordinator.supportsVersion(version))
448         return Exception { InvalidAccessError, makeString("\"" + String::number(version), "\" is not a supported version.") };
449
450     auto convertedPaymentRequest = convertAndValidate(version, WTFMove(paymentRequest));
451     if (convertedPaymentRequest.hasException())
452         return convertedPaymentRequest.releaseException();
453
454     return adoptRef(*new ApplePaySession(document, convertedPaymentRequest.releaseReturnValue()));
455 }
456
457 ApplePaySession::ApplePaySession(Document& document, ApplePaySessionPaymentRequest&& paymentRequest)
458     : ActiveDOMObject(&document)
459     , m_paymentRequest(WTFMove(paymentRequest))
460 {
461     suspendIfNeeded();
462 }
463
464 ApplePaySession::~ApplePaySession() = default;
465
466 ExceptionOr<bool> ApplePaySession::supportsVersion(ScriptExecutionContext& scriptExecutionContext, unsigned version)
467 {
468     if (!version)
469         return Exception { InvalidAccessError };
470
471     auto& document = downcast<Document>(scriptExecutionContext);
472
473     auto canCall = canCallApplePaySessionAPIs(document);
474     if (canCall.hasException())
475         return canCall.releaseException();
476
477     return document.frame()->mainFrame().paymentCoordinator().supportsVersion(version);
478 }
479
480 static bool shouldDiscloseApplePayCapability(Document& document)
481 {
482     auto* page = document.page();
483     if (!page || page->usesEphemeralSession())
484         return false;
485
486     return document.settings().applePayCapabilityDisclosureAllowed();
487 }
488
489 ExceptionOr<bool> ApplePaySession::canMakePayments(ScriptExecutionContext& scriptExecutionContext)
490 {
491     auto& document = downcast<Document>(scriptExecutionContext);
492
493     auto canCall = canCallApplePaySessionAPIs(document);
494     if (canCall.hasException())
495         return canCall.releaseException();
496
497     return document.frame()->mainFrame().paymentCoordinator().canMakePayments();
498 }
499
500 ExceptionOr<void> ApplePaySession::canMakePaymentsWithActiveCard(ScriptExecutionContext& scriptExecutionContext, const String& merchantIdentifier, Ref<DeferredPromise>&& passedPromise)
501 {
502     auto& document = downcast<Document>(scriptExecutionContext);
503
504     auto canCall = canCallApplePaySessionAPIs(document);
505     if (canCall.hasException())
506         return canCall.releaseException();
507
508     RefPtr<DeferredPromise> promise(WTFMove(passedPromise));
509     if (!shouldDiscloseApplePayCapability(document)) {
510         auto& paymentCoordinator = document.frame()->mainFrame().paymentCoordinator();
511         bool canMakePayments = paymentCoordinator.canMakePayments();
512
513         RunLoop::main().dispatch([promise, canMakePayments]() mutable {
514             promise->resolve<IDLBoolean>(canMakePayments);
515         });
516         return { };
517     }
518
519     auto& paymentCoordinator = document.frame()->mainFrame().paymentCoordinator();
520
521     paymentCoordinator.canMakePaymentsWithActiveCard(merchantIdentifier, document.domain(), [promise](bool canMakePayments) mutable {
522         promise->resolve<IDLBoolean>(canMakePayments);
523     });
524     return { };
525 }
526
527 ExceptionOr<void> ApplePaySession::openPaymentSetup(ScriptExecutionContext& scriptExecutionContext, const String& merchantIdentifier, Ref<DeferredPromise>&& passedPromise)
528 {
529     auto& document = downcast<Document>(scriptExecutionContext);
530
531     auto canCall = canCallApplePaySessionAPIs(document);
532     if (canCall.hasException())
533         return canCall.releaseException();
534
535     if (!ScriptController::processingUserGesture())
536         return Exception { InvalidAccessError, "Must call ApplePaySession.openPaymentSetup from a user gesture handler." };
537
538     RefPtr<DeferredPromise> promise(WTFMove(passedPromise));
539     auto& paymentCoordinator = document.frame()->mainFrame().paymentCoordinator();
540
541     paymentCoordinator.openPaymentSetup(merchantIdentifier, document.domain(), [promise](bool result) mutable {
542         promise->resolve<IDLBoolean>(result);
543     });
544
545     return { };
546 }
547
548 ExceptionOr<void> ApplePaySession::begin()
549 {
550     if (!canBegin())
551         return Exception { InvalidAccessError, "Payment session is already active." };
552
553     if (paymentCoordinator().hasActiveSession())
554         return Exception { InvalidAccessError, "Page already has an active payment session." };
555
556     auto& document = *downcast<Document>(scriptExecutionContext());
557
558     Vector<URL> linkIconURLs;
559     for (auto& icon : LinkIconCollector { document }.iconsOfTypes({ LinkIconType::TouchIcon, LinkIconType::TouchPrecomposedIcon }))
560         linkIconURLs.append(icon.url);
561
562     if (!paymentCoordinator().beginPaymentSession(*this, document.url(), linkIconURLs, m_paymentRequest))
563         return Exception { InvalidAccessError, "There is already has an active payment session." };
564
565     m_state = State::Active;
566
567     setPendingActivity(this);
568
569     return { };
570 }
571
572 ExceptionOr<void> ApplePaySession::abort()
573 {
574     if (!canAbort())
575         return Exception { InvalidAccessError };
576
577     m_state = State::Aborted;
578     paymentCoordinator().abortPaymentSession();
579
580     didReachFinalState();
581
582     return { };
583 }
584
585 ExceptionOr<void> ApplePaySession::completeMerchantValidation(JSC::ExecState& state, JSC::JSValue merchantSessionValue)
586 {
587     if (!canCompleteMerchantValidation())
588         return Exception { InvalidAccessError };
589
590     if (!merchantSessionValue.isObject())
591         return Exception { TypeError };
592
593     auto& document = *downcast<Document>(scriptExecutionContext());
594     auto& window = *document.domWindow();
595
596     String errorMessage;
597     auto merchantSession = PaymentMerchantSession::fromJS(state, asObject(merchantSessionValue), errorMessage);
598     if (!merchantSession) {
599         window.printErrorMessage(errorMessage);
600         return Exception { InvalidAccessError };
601     }
602
603     m_merchantValidationState = MerchantValidationState::ValidationComplete;
604     paymentCoordinator().completeMerchantValidation(*merchantSession);
605
606     return { };
607 }
608
609 ExceptionOr<void> ApplePaySession::completeShippingMethodSelection(ApplePayShippingMethodUpdate&& update)
610 {
611     if (!canCompleteShippingMethodSelection())
612         return Exception { InvalidAccessError };
613
614     auto convertedUpdate = convertAndValidate(WTFMove(update));
615     if (convertedUpdate.hasException())
616         return convertedUpdate.releaseException();
617
618     m_state = State::Active;
619     paymentCoordinator().completeShippingMethodSelection(convertedUpdate.releaseReturnValue());
620
621     return { };
622 }
623
624 ExceptionOr<void> ApplePaySession::completeShippingContactSelection(ApplePayShippingContactUpdate&& update)
625 {
626     if (!canCompleteShippingContactSelection())
627         return Exception { InvalidAccessError };
628
629     auto convertedUpdate = convertAndValidate(WTFMove(update));
630     if (convertedUpdate.hasException())
631         return convertedUpdate.releaseException();
632
633     m_state = State::Active;
634     paymentCoordinator().completeShippingContactSelection(convertedUpdate.releaseReturnValue());
635
636     return { };
637 }
638
639 ExceptionOr<void> ApplePaySession::completePaymentMethodSelection(ApplePayPaymentMethodUpdate&& update)
640 {
641     if (!canCompletePaymentMethodSelection())
642         return Exception { InvalidAccessError };
643
644     auto convertedUpdate = convertAndValidate(WTFMove(update));
645     if (convertedUpdate.hasException())
646         return convertedUpdate.releaseException();
647
648     m_state = State::Active;
649     paymentCoordinator().completePaymentMethodSelection(convertedUpdate.releaseReturnValue());
650
651     return { };
652 }
653
654 ExceptionOr<void> ApplePaySession::completePayment(ApplePayPaymentAuthorizationResult&& result)
655 {
656     if (!canCompletePayment())
657         return Exception { InvalidAccessError };
658
659     auto convertedResultOrException = convertAndValidate(WTFMove(result));
660     if (convertedResultOrException.hasException())
661         return convertedResultOrException.releaseException();
662
663     auto&& convertedResult = convertedResultOrException.releaseReturnValue();
664     bool isFinalState = isFinalStateResult(convertedResult);
665
666     paymentCoordinator().completePaymentSession(WTFMove(convertedResult));
667
668     if (!isFinalState) {
669         m_state = State::Active;
670         return { };
671     }
672
673     m_state = State::Completed;
674     unsetPendingActivity(this);
675
676     return { };
677 }
678
679 ExceptionOr<void> ApplePaySession::completeShippingMethodSelection(unsigned short status, ApplePayLineItem&& newTotal, Vector<ApplePayLineItem>&& newLineItems)
680 {
681     ApplePayShippingMethodUpdate update;
682
683     switch (status) {
684     case ApplePaySession::STATUS_SUCCESS:
685         break;
686
687     case ApplePaySession::STATUS_FAILURE:
688     case ApplePaySession::STATUS_INVALID_BILLING_POSTAL_ADDRESS:
689     case ApplePaySession::STATUS_INVALID_SHIPPING_POSTAL_ADDRESS:
690     case ApplePaySession::STATUS_INVALID_SHIPPING_CONTACT:
691     case ApplePaySession::STATUS_PIN_REQUIRED:
692     case ApplePaySession::STATUS_PIN_INCORRECT:
693     case ApplePaySession::STATUS_PIN_LOCKOUT:
694         // This is a fatal error. Cancel the request.
695         m_state = State::CancelRequested;
696         paymentCoordinator().cancelPaymentSession();
697         return { };
698
699     default:
700         return Exception { InvalidAccessError };
701     }
702
703     update.newTotal = WTFMove(newTotal);
704     update.newLineItems = WTFMove(newLineItems);
705
706     return completeShippingMethodSelection(WTFMove(update));
707 }
708
709 ExceptionOr<void> ApplePaySession::completeShippingContactSelection(unsigned short status, Vector<ApplePayShippingMethod>&& newShippingMethods, ApplePayLineItem&& newTotal, Vector<ApplePayLineItem>&& newLineItems)
710 {
711     ApplePayShippingContactUpdate update;
712
713     std::optional<ApplePayError::Code> errorCode;
714     std::optional<ApplePayError::ContactField> contactField;
715
716     switch (status) {
717     case ApplePaySession::STATUS_SUCCESS:
718         break;
719
720     case ApplePaySession::STATUS_FAILURE:
721     case ApplePaySession::STATUS_PIN_REQUIRED:
722     case ApplePaySession::STATUS_PIN_INCORRECT:
723     case ApplePaySession::STATUS_PIN_LOCKOUT:
724         errorCode = ApplePayError::Code::Unknown;
725         break;
726
727     case ApplePaySession::STATUS_INVALID_BILLING_POSTAL_ADDRESS:
728         errorCode = ApplePayError::Code::BillingContactInvalid;
729         break;
730
731     case ApplePaySession::STATUS_INVALID_SHIPPING_POSTAL_ADDRESS:
732         errorCode = ApplePayError::Code::ShippingContactInvalid;
733         contactField = ApplePayError::ContactField::PostalAddress;
734         break;
735
736     case ApplePaySession::STATUS_INVALID_SHIPPING_CONTACT:
737         errorCode = ApplePayError::Code::ShippingContactInvalid;
738         break;
739
740     default:
741         return Exception { InvalidAccessError };
742     }
743
744     if (errorCode)
745         update.errors = { ApplePayError::create(*errorCode, contactField, { }) };
746
747     update.newShippingMethods = WTFMove(newShippingMethods);
748     update.newTotal = WTFMove(newTotal);
749     update.newLineItems = WTFMove(newLineItems);
750
751     return completeShippingContactSelection(WTFMove(update));
752 }
753
754 ExceptionOr<void> ApplePaySession::completePaymentMethodSelection(ApplePayLineItem&& newTotal, Vector<ApplePayLineItem>&& newLineItems)
755 {
756     ApplePayPaymentMethodUpdate update;
757
758     update.newTotal = WTFMove(newTotal);
759     update.newLineItems = WTFMove(newLineItems);
760
761     return completePaymentMethodSelection(WTFMove(update));
762 }
763
764 ExceptionOr<void> ApplePaySession::completePayment(unsigned short status)
765 {
766     ApplePayPaymentAuthorizationResult result;
767     result.status = status;
768
769     return completePayment(WTFMove(result));
770 }
771
772 void ApplePaySession::validateMerchant(const URL& validationURL)
773 {
774     if (m_state == State::Aborted) {
775         // ApplePaySession::abort has been called.
776         return;
777     }
778
779     ASSERT(m_merchantValidationState == MerchantValidationState::Idle);
780     ASSERT(m_state == State::Active);
781
782     if (validationURL.isNull()) {
783         // Something went wrong when getting the validation URL.
784         // FIXME: Maybe we should send an error event here instead?
785         return;
786     }
787
788     m_merchantValidationState = MerchantValidationState::ValidatingMerchant;
789
790     auto event = ApplePayValidateMerchantEvent::create(eventNames().validatemerchantEvent, validationURL);
791     dispatchEvent(event.get());
792 }
793
794 void ApplePaySession::didAuthorizePayment(const Payment& payment)
795 {
796     ASSERT(m_state == State::Active);
797
798     m_state = State::Authorized;
799
800     auto event = ApplePayPaymentAuthorizedEvent::create(eventNames().paymentauthorizedEvent, payment);
801     dispatchEvent(event.get());
802 }
803
804 void ApplePaySession::didSelectShippingMethod(const ApplePaySessionPaymentRequest::ShippingMethod& shippingMethod)
805 {
806     ASSERT(m_state == State::Active);
807
808     if (!hasEventListeners(eventNames().shippingmethodselectedEvent)) {
809         paymentCoordinator().completeShippingMethodSelection({ });
810         return;
811     }
812
813     m_state = State::ShippingMethodSelected;
814     auto event = ApplePayShippingMethodSelectedEvent::create(eventNames().shippingmethodselectedEvent, shippingMethod);
815     dispatchEvent(event.get());
816 }
817
818 void ApplePaySession::didSelectShippingContact(const PaymentContact& shippingContact)
819 {
820     ASSERT(m_state == State::Active);
821
822     if (!hasEventListeners(eventNames().shippingcontactselectedEvent)) {
823         paymentCoordinator().completeShippingContactSelection({ });
824         return;
825     }
826
827     m_state = State::ShippingContactSelected;
828     auto event = ApplePayShippingContactSelectedEvent::create(eventNames().shippingcontactselectedEvent, shippingContact);
829     dispatchEvent(event.get());
830 }
831
832 void ApplePaySession::didSelectPaymentMethod(const PaymentMethod& paymentMethod)
833 {
834     ASSERT(m_state == State::Active);
835
836     if (!hasEventListeners(eventNames().paymentmethodselectedEvent)) {
837         paymentCoordinator().completePaymentMethodSelection({ });
838         return;
839     }
840
841     m_state = State::PaymentMethodSelected;
842     auto event = ApplePayPaymentMethodSelectedEvent::create(eventNames().paymentmethodselectedEvent, paymentMethod);
843     dispatchEvent(event.get());
844 }
845
846 void ApplePaySession::didCancelPaymentSession()
847 {
848     ASSERT(canCancel());
849
850     m_state = State::Canceled;
851
852     auto event = Event::create(eventNames().cancelEvent, false, false);
853     dispatchEvent(event.get());
854
855     didReachFinalState();
856 }
857
858 const char* ApplePaySession::activeDOMObjectName() const
859 {
860     return "ApplePaySession";
861 }
862
863 bool ApplePaySession::canSuspendForDocumentSuspension() const
864 {
865     switch (m_state) {
866     case State::Idle:
867     case State::Aborted:
868     case State::Completed:
869     case State::Canceled:
870         return true;
871
872     case State::Active:
873     case State::Authorized:
874     case State::ShippingMethodSelected:
875     case State::ShippingContactSelected:
876     case State::PaymentMethodSelected:
877     case State::CancelRequested:
878         return false;
879     }
880 }
881
882 void ApplePaySession::stop()
883 {
884     if (!canAbort())
885         return;
886
887     m_state = State::Aborted;
888     paymentCoordinator().abortPaymentSession();
889
890     didReachFinalState();
891 }
892
893 PaymentCoordinator& ApplePaySession::paymentCoordinator() const
894 {
895     return downcast<Document>(*scriptExecutionContext()).frame()->mainFrame().paymentCoordinator();
896 }
897
898 bool ApplePaySession::canBegin() const
899 {
900     switch (m_state) {
901     case State::Idle:
902         return true;
903
904     case State::Active:
905     case State::Aborted:
906     case State::Authorized:
907     case State::Completed:
908     case State::Canceled:
909     case State::ShippingMethodSelected:
910     case State::ShippingContactSelected:
911     case State::PaymentMethodSelected:
912     case State::CancelRequested:
913         return false;
914     }
915 }
916
917 bool ApplePaySession::canAbort() const
918 {
919     switch (m_state) {
920     case State::Idle:
921     case State::Aborted:
922     case State::Completed:
923     case State::Canceled:
924         return false;
925
926     case State::Active:
927     case State::Authorized:
928     case State::ShippingMethodSelected:
929     case State::ShippingContactSelected:
930     case State::PaymentMethodSelected:
931     case State::CancelRequested:
932         return true;
933     }
934 }
935
936 bool ApplePaySession::canCancel() const
937 {
938     switch (m_state) {
939     case State::Idle:
940     case State::Aborted:
941     case State::Completed:
942     case State::Canceled:
943         return false;
944
945     case State::Active:
946     case State::Authorized:
947     case State::ShippingMethodSelected:
948     case State::ShippingContactSelected:
949     case State::PaymentMethodSelected:
950     case State::CancelRequested:
951         return true;
952     }
953 }
954
955 bool ApplePaySession::canCompleteMerchantValidation() const
956 {
957     if (m_state != State::Active)
958         return false;
959
960     if (m_merchantValidationState != MerchantValidationState::ValidatingMerchant)
961         return false;
962
963     return true;
964 }
965
966 bool ApplePaySession::canCompleteShippingMethodSelection() const
967 {
968     switch (m_state) {
969     case State::Idle:
970     case State::Aborted:
971     case State::Active:
972     case State::Completed:
973     case State::Canceled:
974     case State::Authorized:
975     case State::PaymentMethodSelected:
976     case State::ShippingContactSelected:
977     case State::CancelRequested:
978         return false;
979
980     case State::ShippingMethodSelected:
981         return true;
982     }
983 }
984
985 bool ApplePaySession::canCompleteShippingContactSelection() const
986 {
987     switch (m_state) {
988     case State::Idle:
989     case State::Aborted:
990     case State::Active:
991     case State::Completed:
992     case State::Canceled:
993     case State::Authorized:
994     case State::PaymentMethodSelected:
995     case State::ShippingMethodSelected:
996     case State::CancelRequested:
997         return false;
998
999     case State::ShippingContactSelected:
1000         return true;
1001     }
1002 }
1003
1004 bool ApplePaySession::canCompletePaymentMethodSelection() const
1005 {
1006     switch (m_state) {
1007     case State::Idle:
1008     case State::Aborted:
1009     case State::Active:
1010     case State::Completed:
1011     case State::Canceled:
1012     case State::Authorized:
1013     case State::ShippingMethodSelected:
1014     case State::ShippingContactSelected:
1015     case State::CancelRequested:
1016         return false;
1017
1018     case State::PaymentMethodSelected:
1019         return true;
1020     }
1021 }
1022
1023 bool ApplePaySession::canCompletePayment() const
1024 {
1025     switch (m_state) {
1026     case State::Idle:
1027     case State::Aborted:
1028     case State::Active:
1029     case State::Completed:
1030     case State::Canceled:
1031     case State::ShippingMethodSelected:
1032     case State::ShippingContactSelected:
1033     case State::PaymentMethodSelected:
1034     case State::CancelRequested:
1035         return false;
1036
1037     case State::Authorized:
1038         return true;
1039     }
1040 }
1041
1042 bool ApplePaySession::isFinalState() const
1043 {
1044     switch (m_state) {
1045     case State::Idle:
1046     case State::Active:
1047     case State::ShippingMethodSelected:
1048     case State::ShippingContactSelected:
1049     case State::PaymentMethodSelected:
1050     case State::Authorized:
1051     case State::CancelRequested:
1052         return false;
1053
1054     case State::Completed:
1055     case State::Aborted:
1056     case State::Canceled:
1057         return true;
1058     }
1059 }
1060
1061 void ApplePaySession::didReachFinalState()
1062 {
1063     ASSERT(isFinalState());
1064     unsetPendingActivity(this);
1065 }
1066
1067 }
1068
1069 #endif