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