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