0774fc6e15b6f9853c2d0b3e148ba5cfc371c834
[WebKit-https.git] / Source / WebCore / Modules / paymentrequest / PaymentRequest.cpp
1 /*
2  * Copyright (C) 2017-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 "PaymentRequest.h"
28
29 #if ENABLE(PAYMENT_REQUEST)
30
31 #include "ApplePayPaymentHandler.h"
32 #include "Document.h"
33 #include "EventNames.h"
34 #include "JSDOMPromise.h"
35 #include "JSPaymentDetailsUpdate.h"
36 #include "JSPaymentResponse.h"
37 #include "Page.h"
38 #include "PaymentAddress.h"
39 #include "PaymentCoordinator.h"
40 #include "PaymentCurrencyAmount.h"
41 #include "PaymentDetailsInit.h"
42 #include "PaymentHandler.h"
43 #include "PaymentMethodChangeEvent.h"
44 #include "PaymentMethodData.h"
45 #include "PaymentOptions.h"
46 #include "PaymentRequestUpdateEvent.h"
47 #include "PaymentValidationErrors.h"
48 #include "ScriptController.h"
49 #include <JavaScriptCore/JSONObject.h>
50 #include <JavaScriptCore/ThrowScope.h>
51 #include <wtf/ASCIICType.h>
52 #include <wtf/IsoMallocInlines.h>
53 #include <wtf/RunLoop.h>
54 #include <wtf/Scope.h>
55 #include <wtf/UUID.h>
56
57 namespace WebCore {
58
59 WTF_MAKE_ISO_ALLOCATED_IMPL(PaymentRequest);
60
61 // Implements the IsWellFormedCurrencyCode abstract operation from ECMA 402
62 // https://tc39.github.io/ecma402/#sec-iswellformedcurrencycode
63 static bool isWellFormedCurrencyCode(const String& currency)
64 {
65     if (currency.length() == 3)
66         return currency.isAllSpecialCharacters<isASCIIAlpha>();
67     return false;
68 }
69
70 // Implements the "valid decimal monetary value" validity checker
71 // https://www.w3.org/TR/payment-request/#dfn-valid-decimal-monetary-value
72 static bool isValidDecimalMonetaryValue(StringView value)
73 {
74     enum class State {
75         Start,
76         Sign,
77         Digit,
78         Dot,
79         DotDigit,
80     };
81
82     auto state = State::Start;
83     for (auto character : value.codeUnits()) {
84         switch (state) {
85         case State::Start:
86             if (character == '-') {
87                 state = State::Sign;
88                 break;
89             }
90
91             if (isASCIIDigit(character)) {
92                 state = State::Digit;
93                 break;
94             }
95
96             return false;
97
98         case State::Sign:
99             if (isASCIIDigit(character)) {
100                 state = State::Digit;
101                 break;
102             }
103
104             return false;
105
106         case State::Digit:
107             if (character == '.') {
108                 state = State::Dot;
109                 break;
110             }
111
112             if (isASCIIDigit(character)) {
113                 state = State::Digit;
114                 break;
115             }
116
117             return false;
118
119         case State::Dot:
120             if (isASCIIDigit(character)) {
121                 state = State::DotDigit;
122                 break;
123             }
124
125             return false;
126
127         case State::DotDigit:
128             if (isASCIIDigit(character)) {
129                 state = State::DotDigit;
130                 break;
131             }
132
133             return false;
134         }
135     }
136
137     if (state == State::Digit || state == State::DotDigit)
138         return true;
139
140     return false;
141 }
142
143 // Implements the "check and canonicalize amount" validity checker
144 // https://www.w3.org/TR/payment-request/#dfn-check-and-canonicalize-amount
145 static ExceptionOr<void> checkAndCanonicalizeAmount(PaymentCurrencyAmount& amount)
146 {
147     if (!isWellFormedCurrencyCode(amount.currency))
148         return Exception { RangeError, makeString("\"", amount.currency, "\" is not a valid currency code.") };
149
150     if (!isValidDecimalMonetaryValue(amount.value))
151         return Exception { TypeError, makeString("\"", amount.value, "\" is not a valid decimal monetary value.") };
152
153     amount.currency = amount.currency.convertToASCIIUppercase();
154     return { };
155 }
156
157 // Implements the "check and canonicalize total" validity checker
158 // https://www.w3.org/TR/payment-request/#dfn-check-and-canonicalize-total
159 static ExceptionOr<void> checkAndCanonicalizeTotal(PaymentCurrencyAmount& total)
160 {
161     auto exception = checkAndCanonicalizeAmount(total);
162     if (exception.hasException())
163         return exception;
164
165     if (total.value[0] == '-')
166         return Exception { TypeError, "Total currency values cannot be negative."_s };
167
168     return { };
169 }
170
171 // Implements "validate a standardized payment method identifier"
172 // https://www.w3.org/TR/payment-method-id/#validity-0
173 static bool isValidStandardizedPaymentMethodIdentifier(StringView identifier)
174 {
175     enum class State {
176         Start,
177         Hyphen,
178         LowerAlpha,
179         Digit,
180     };
181
182     auto state = State::Start;
183     for (auto character : identifier.codeUnits()) {
184         switch (state) {
185         case State::Start:
186         case State::Hyphen:
187             if (isASCIILower(character)) {
188                 state = State::LowerAlpha;
189                 break;
190             }
191
192             return false;
193
194         case State::LowerAlpha:
195         case State::Digit:
196             if (isASCIILower(character)) {
197                 state = State::LowerAlpha;
198                 break;
199             }
200
201             if (isASCIIDigit(character)) {
202                 state = State::Digit;
203                 break;
204             }
205
206             if (character == '-') {
207                 state = State::Hyphen;
208                 break;
209             }
210
211             return false;
212         }
213     }
214
215     return state == State::LowerAlpha || state == State::Digit;
216 }
217
218 // Implements "validate a URL-based payment method identifier"
219 // https://www.w3.org/TR/payment-method-id/#validation
220 static bool isValidURLBasedPaymentMethodIdentifier(const URL& url)
221 {
222     if (!url.protocolIs("https"))
223         return false;
224
225     if (!url.user().isEmpty() || !url.pass().isEmpty())
226         return false;
227
228     return true;
229 }
230
231 // Implements "validate a payment method identifier"
232 // https://www.w3.org/TR/payment-method-id/#validity
233 Optional<PaymentRequest::MethodIdentifier> convertAndValidatePaymentMethodIdentifier(const String& identifier)
234 {
235     URL url { URL(), identifier };
236     if (!url.isValid()) {
237         if (isValidStandardizedPaymentMethodIdentifier(identifier))
238             return { identifier };
239         return WTF::nullopt;
240     }
241
242     if (isValidURLBasedPaymentMethodIdentifier(url))
243         return { WTFMove(url) };
244
245     return WTF::nullopt;
246 }
247
248 enum class ShouldValidatePaymentMethodIdentifier {
249     No,
250     Yes,
251 };
252
253 static ExceptionOr<std::tuple<String, Vector<String>>> checkAndCanonicalizeDetails(JSC::ExecState& execState, PaymentDetailsBase& details, bool requestShipping, ShouldValidatePaymentMethodIdentifier shouldValidatePaymentMethodIdentifier)
254 {
255     for (auto& item : details.displayItems) {
256         auto exception = checkAndCanonicalizeAmount(item.amount);
257         if (exception.hasException())
258             return exception.releaseException();
259     }
260
261     String selectedShippingOption;
262     if (requestShipping) {
263         HashSet<String> seenShippingOptionIDs;
264         for (auto& shippingOption : details.shippingOptions) {
265             auto exception = checkAndCanonicalizeAmount(shippingOption.amount);
266             if (exception.hasException())
267                 return exception.releaseException();
268
269             auto addResult = seenShippingOptionIDs.add(shippingOption.id);
270             if (!addResult.isNewEntry)
271                 return Exception { TypeError, "Shipping option IDs must be unique." };
272
273             if (shippingOption.selected)
274                 selectedShippingOption = shippingOption.id;
275         }
276     }
277
278     Vector<String> serializedModifierData;
279     serializedModifierData.reserveInitialCapacity(details.modifiers.size());
280     for (auto& modifier : details.modifiers) {
281         if (shouldValidatePaymentMethodIdentifier == ShouldValidatePaymentMethodIdentifier::Yes) {
282             auto paymentMethodIdentifier = convertAndValidatePaymentMethodIdentifier(modifier.supportedMethods);
283             if (!paymentMethodIdentifier)
284                 return Exception { RangeError, makeString('"', modifier.supportedMethods, "\" is an invalid payment method identifier.") };
285         }
286
287         if (modifier.total) {
288             auto exception = checkAndCanonicalizeTotal(modifier.total->amount);
289             if (exception.hasException())
290                 return exception.releaseException();
291         }
292
293         for (auto& item : modifier.additionalDisplayItems) {
294             auto exception = checkAndCanonicalizeAmount(item.amount);
295             if (exception.hasException())
296                 return exception.releaseException();
297         }
298
299         String serializedData;
300         if (modifier.data) {
301             auto scope = DECLARE_THROW_SCOPE(execState.vm());
302             serializedData = JSONStringify(&execState, modifier.data.get(), 0);
303             if (scope.exception())
304                 return Exception { ExistingExceptionError };
305             modifier.data.clear();
306         }
307         serializedModifierData.uncheckedAppend(WTFMove(serializedData));
308     }
309
310     return std::make_tuple(WTFMove(selectedShippingOption), WTFMove(serializedModifierData));
311 }
312
313 // Implements the PaymentRequest Constructor
314 // https://www.w3.org/TR/payment-request/#constructor
315 ExceptionOr<Ref<PaymentRequest>> PaymentRequest::create(Document& document, Vector<PaymentMethodData>&& methodData, PaymentDetailsInit&& details, PaymentOptions&& options)
316 {
317     auto canCreateSession = PaymentHandler::canCreateSession(document);
318     if (canCreateSession.hasException())
319         return canCreateSession.releaseException();
320
321     if (details.id.isNull())
322         details.id = createCanonicalUUIDString();
323
324     if (methodData.isEmpty())
325         return Exception { TypeError, "At least one payment method is required."_s };
326
327     Vector<Method> serializedMethodData;
328     serializedMethodData.reserveInitialCapacity(methodData.size());
329     for (auto& paymentMethod : methodData) {
330         auto identifier = convertAndValidatePaymentMethodIdentifier(paymentMethod.supportedMethods);
331         if (!identifier)
332             return Exception { RangeError, makeString('"', paymentMethod.supportedMethods, "\" is an invalid payment method identifier.") };
333
334         String serializedData;
335         if (paymentMethod.data) {
336             auto scope = DECLARE_THROW_SCOPE(document.execState()->vm());
337             serializedData = JSONStringify(document.execState(), paymentMethod.data.get(), 0);
338             if (scope.exception())
339                 return Exception { ExistingExceptionError };
340         }
341         serializedMethodData.uncheckedAppend({ WTFMove(*identifier), WTFMove(serializedData) });
342     }
343
344     auto totalResult = checkAndCanonicalizeTotal(details.total.amount);
345     if (totalResult.hasException())
346         return totalResult.releaseException();
347
348     auto detailsResult = checkAndCanonicalizeDetails(*document.execState(), details, options.requestShipping, ShouldValidatePaymentMethodIdentifier::No);
349     if (detailsResult.hasException())
350         return detailsResult.releaseException();
351
352     auto shippingOptionAndModifierData = detailsResult.releaseReturnValue();
353     return adoptRef(*new PaymentRequest(document, WTFMove(options), WTFMove(details), WTFMove(std::get<1>(shippingOptionAndModifierData)), WTFMove(serializedMethodData), WTFMove(std::get<0>(shippingOptionAndModifierData))));
354 }
355
356 bool PaymentRequest::enabledForContext(ScriptExecutionContext& context)
357 {
358     return PaymentHandler::enabledForContext(context);
359 }
360
361 PaymentRequest::PaymentRequest(Document& document, PaymentOptions&& options, PaymentDetailsInit&& details, Vector<String>&& serializedModifierData, Vector<Method>&& serializedMethodData, String&& selectedShippingOption)
362     : ActiveDOMObject { document }
363     , m_options { WTFMove(options) }
364     , m_details { WTFMove(details) }
365     , m_serializedModifierData { WTFMove(serializedModifierData) }
366     , m_serializedMethodData { WTFMove(serializedMethodData) }
367     , m_shippingOption { WTFMove(selectedShippingOption) }
368 {
369     suspendIfNeeded();
370 }
371
372 PaymentRequest::~PaymentRequest()
373 {
374     ASSERT(!hasPendingActivity());
375     ASSERT(!m_activePaymentHandler);
376 }
377
378 static ExceptionOr<JSC::JSValue> parse(ScriptExecutionContext& context, const String& string)
379 {
380     auto scope = DECLARE_THROW_SCOPE(context.vm());
381     JSC::JSValue data = JSONParse(context.execState(), string);
382     if (scope.exception())
383         return Exception { ExistingExceptionError };
384     return WTFMove(data);
385 }
386
387 // https://www.w3.org/TR/payment-request/#show()-method
388 void PaymentRequest::show(Document& document, RefPtr<DOMPromise>&& detailsPromise, ShowPromise&& promise)
389 {
390     if (!document.frame()) {
391         promise.reject(Exception { AbortError });
392         return;
393     }
394
395     if (!UserGestureIndicator::processingUserGesture()) {
396         promise.reject(Exception { SecurityError, "show() must be triggered by user activation." });
397         return;
398     }
399
400     if (m_state != State::Created) {
401         promise.reject(Exception { InvalidStateError });
402         return;
403     }
404
405     if (PaymentHandler::hasActiveSession(document)) {
406         promise.reject(Exception { AbortError });
407         m_state = State::Closed;
408         return;
409     }
410
411     m_state = State::Interactive;
412     ASSERT(!m_showPromise);
413     m_showPromise = WTFMove(promise);
414
415     RefPtr<PaymentHandler> selectedPaymentHandler;
416     for (auto& paymentMethod : m_serializedMethodData) {
417         auto data = parse(document, paymentMethod.serializedData);
418         if (data.hasException()) {
419             settleShowPromise(data.releaseException());
420             return;
421         }
422
423         auto handler = PaymentHandler::create(document, *this, paymentMethod.identifier);
424         if (!handler)
425             continue;
426
427         auto result = handler->convertData(data.releaseReturnValue());
428         if (result.hasException()) {
429             settleShowPromise(result.releaseException());
430             return;
431         }
432
433         if (!selectedPaymentHandler)
434             selectedPaymentHandler = WTFMove(handler);
435     }
436
437     if (!selectedPaymentHandler) {
438         settleShowPromise(Exception { NotSupportedError });
439         return;
440     }
441
442     auto exception = selectedPaymentHandler->show(document);
443     if (exception.hasException()) {
444         settleShowPromise(exception.releaseException());
445         return;
446     }
447
448     ASSERT(!m_activePaymentHandler);
449     m_activePaymentHandler = PaymentHandlerWithPendingActivity { selectedPaymentHandler.releaseNonNull(), makePendingActivity(*this) };
450
451     if (!detailsPromise)
452         return;
453
454     exception = updateWith(UpdateReason::ShowDetailsResolved, detailsPromise.releaseNonNull());
455     ASSERT(!exception.hasException());
456 }
457
458 void PaymentRequest::abortWithException(Exception&& exception)
459 {
460     ASSERT(m_state == State::Interactive);
461     closeActivePaymentHandler();
462
463     if (m_response)
464         m_response->abortWithException(WTFMove(exception));
465     else
466         settleShowPromise(WTFMove(exception));
467 }
468
469 void PaymentRequest::settleShowPromise(ExceptionOr<PaymentResponse&>&& result)
470 {
471     if (auto showPromise = std::exchange(m_showPromise, WTF::nullopt))
472         showPromise->settle(WTFMove(result));
473 }
474
475 void PaymentRequest::closeActivePaymentHandler()
476 {
477     if (auto activePaymentHandler = std::exchange(m_activePaymentHandler, WTF::nullopt))
478         activePaymentHandler->paymentHandler->hide();
479
480     m_isUpdating = false;
481     m_state = State::Closed;
482 }
483
484 void PaymentRequest::stop()
485 {
486     closeActivePaymentHandler();
487     settleShowPromise(Exception { AbortError });
488 }
489
490 // https://www.w3.org/TR/payment-request/#abort()-method
491 void PaymentRequest::abort(AbortPromise&& promise)
492 {
493     if (m_response && m_response->hasRetryPromise()) {
494         promise.reject(Exception { InvalidStateError });
495         return;
496     }
497
498     if (m_state != State::Interactive) {
499         promise.reject(Exception { InvalidStateError });
500         return;
501     }
502
503     abortWithException(Exception { AbortError });
504     promise.resolve();
505 }
506
507 // https://www.w3.org/TR/payment-request/#canmakepayment()-method
508 void PaymentRequest::canMakePayment(Document& document, CanMakePaymentPromise&& promise)
509 {
510     if (m_state != State::Created) {
511         promise.reject(Exception { InvalidStateError });
512         return;
513     }
514
515     for (auto& paymentMethod : m_serializedMethodData) {
516         auto handler = PaymentHandler::create(document, *this, paymentMethod.identifier);
517         if (!handler)
518             continue;
519
520         handler->canMakePayment(document, [promise = WTFMove(promise)](bool canMakePayment) mutable {
521             promise.resolve(canMakePayment);
522         });
523         return;
524     }
525
526     promise.resolve(false);
527 }
528
529 const String& PaymentRequest::id() const
530 {
531     return m_details.id;
532 }
533
534 Optional<PaymentShippingType> PaymentRequest::shippingType() const
535 {
536     if (m_options.requestShipping)
537         return m_options.shippingType;
538     return WTF::nullopt;
539 }
540
541 bool PaymentRequest::canSuspendForDocumentSuspension() const
542 {
543     return !hasPendingActivity();
544 }
545
546 void PaymentRequest::shippingAddressChanged(Ref<PaymentAddress>&& shippingAddress)
547 {
548     whenDetailsSettled([this, protectedThis = makeRefPtr(this), shippingAddress = makeRefPtr(shippingAddress.get())]() mutable {
549         m_shippingAddress = WTFMove(shippingAddress);
550         dispatchEvent(PaymentRequestUpdateEvent::create(eventNames().shippingaddresschangeEvent));
551     });
552 }
553
554 void PaymentRequest::shippingOptionChanged(const String& shippingOption)
555 {
556     whenDetailsSettled([this, protectedThis = makeRefPtr(this), shippingOption]() mutable {
557         m_shippingOption = shippingOption;
558         dispatchEvent(PaymentRequestUpdateEvent::create(eventNames().shippingoptionchangeEvent));
559     });
560 }
561
562 void PaymentRequest::paymentMethodChanged(const String& methodName, PaymentMethodChangeEvent::MethodDetailsFunction&& methodDetailsFunction)
563 {
564     whenDetailsSettled([this, protectedThis = makeRefPtr(this), methodName, methodDetailsFunction = WTFMove(methodDetailsFunction)]() mutable {
565         auto& eventName = eventNames().paymentmethodchangeEvent;
566         if (hasEventListeners(eventName))
567             dispatchEvent(PaymentMethodChangeEvent::create(eventName, methodName, WTFMove(methodDetailsFunction)));
568         else
569             activePaymentHandler()->detailsUpdated(UpdateReason::PaymentMethodChanged, { }, { }, { }, { });
570     });
571 }
572
573 ExceptionOr<void> PaymentRequest::updateWith(UpdateReason reason, Ref<DOMPromise>&& promise)
574 {
575     if (m_state != State::Interactive)
576         return Exception { InvalidStateError };
577
578     if (m_isUpdating)
579         return Exception { InvalidStateError };
580
581     m_isUpdating = true;
582
583     ASSERT(!m_detailsPromise);
584     m_detailsPromise = WTFMove(promise);
585     m_detailsPromise->whenSettled([this, protectedThis = makeRefPtr(this), reason]() {
586         settleDetailsPromise(reason);
587     });
588
589     return { };
590 }
591
592 ExceptionOr<void> PaymentRequest::completeMerchantValidation(Event& event, Ref<DOMPromise>&& merchantSessionPromise)
593 {
594     if (m_state != State::Interactive)
595         return Exception { InvalidStateError };
596
597     event.stopPropagation();
598     event.stopImmediatePropagation();
599
600     m_merchantSessionPromise = WTFMove(merchantSessionPromise);
601     m_merchantSessionPromise->whenSettled([this, protectedThis = makeRefPtr(this)]() {
602         if (m_state != State::Interactive)
603             return;
604
605         if (m_merchantSessionPromise->status() == DOMPromise::Status::Rejected) {
606             abortWithException(Exception { AbortError });
607             return;
608         }
609
610         auto exception = activePaymentHandler()->merchantValidationCompleted(m_merchantSessionPromise->result());
611         if (exception.hasException()) {
612             abortWithException(exception.releaseException());
613             return;
614         }
615     });
616
617     return { };
618 }
619
620 void PaymentRequest::settleDetailsPromise(UpdateReason reason)
621 {
622     auto scopeExit = makeScopeExit([&] {
623         m_isUpdating = false;
624         m_isCancelPending = false;
625         m_detailsPromise = nullptr;
626     });
627
628     if (m_state != State::Interactive)
629         return;
630
631     if (m_isCancelPending || m_detailsPromise->status() == DOMPromise::Status::Rejected) {
632         abortWithException(Exception { AbortError });
633         return;
634     }
635
636     auto& context = *m_detailsPromise->scriptExecutionContext();
637     auto throwScope = DECLARE_THROW_SCOPE(context.vm());
638     auto detailsUpdate = convertDictionary<PaymentDetailsUpdate>(*context.execState(), m_detailsPromise->result());
639     if (throwScope.exception()) {
640         abortWithException(Exception { ExistingExceptionError });
641         return;
642     }
643
644     auto totalResult = checkAndCanonicalizeTotal(detailsUpdate.total.amount);
645     if (totalResult.hasException()) {
646         abortWithException(totalResult.releaseException());
647         return;
648     }
649
650     auto detailsResult = checkAndCanonicalizeDetails(*context.execState(), detailsUpdate, m_options.requestShipping, ShouldValidatePaymentMethodIdentifier::Yes);
651     if (detailsResult.hasException()) {
652         abortWithException(detailsResult.releaseException());
653         return;
654     }
655
656     auto shippingOptionAndModifierData = detailsResult.releaseReturnValue();
657
658     m_details.total = WTFMove(detailsUpdate.total);
659     m_details.displayItems = WTFMove(detailsUpdate.displayItems);
660     if (m_options.requestShipping) {
661         m_details.shippingOptions = WTFMove(detailsUpdate.shippingOptions);
662         m_shippingOption = WTFMove(std::get<0>(shippingOptionAndModifierData));
663     }
664
665     m_details.modifiers = WTFMove(detailsUpdate.modifiers);
666     m_serializedModifierData = WTFMove(std::get<1>(shippingOptionAndModifierData));
667
668     auto result = activePaymentHandler()->detailsUpdated(reason, WTFMove(detailsUpdate.error), WTFMove(detailsUpdate.shippingAddressErrors), WTFMove(detailsUpdate.payerErrors), detailsUpdate.paymentMethodErrors.get());
669     if (result.hasException()) {
670         abortWithException(result.releaseException());
671         return;
672     }
673 }
674
675 void PaymentRequest::whenDetailsSettled(std::function<void()>&& callback)
676 {
677     auto whenSettledFunction = [this, callback = WTFMove(callback)] {
678         ASSERT(m_state == State::Interactive);
679         ASSERT(!m_isUpdating);
680         ASSERT(!m_isCancelPending);
681         ASSERT_UNUSED(this, this);
682         callback();
683     };
684
685     if (!m_detailsPromise) {
686         whenSettledFunction();
687         return;
688     }
689
690     m_detailsPromise->whenSettled([this, protectedThis = makeRefPtr(this), whenSettledFunction = WTFMove(whenSettledFunction)] {
691         if (m_state == State::Interactive)
692             whenSettledFunction();
693     });
694 }
695
696 void PaymentRequest::accept(const String& methodName, PaymentResponse::DetailsFunction&& detailsFunction, Ref<PaymentAddress>&& shippingAddress, const String& payerName, const String& payerEmail, const String& payerPhone)
697 {
698     ASSERT(!m_isUpdating);
699     ASSERT(m_state == State::Interactive);
700
701     bool isRetry = m_response;
702     if (!isRetry) {
703         m_response = PaymentResponse::create(scriptExecutionContext(), *this);
704         m_response->setRequestId(m_details.id);
705     }
706
707     m_response->setMethodName(methodName);
708     m_response->setDetailsFunction(WTFMove(detailsFunction));
709     m_response->setShippingAddress(m_options.requestShipping ? shippingAddress.ptr() : nullptr);
710     m_response->setShippingOption(m_options.requestShipping ? m_shippingOption : String { });
711     m_response->setPayerName(m_options.requestPayerName ? payerName : String { });
712     m_response->setPayerEmail(m_options.requestPayerEmail ? payerEmail : String { });
713     m_response->setPayerPhone(m_options.requestPayerPhone ? payerPhone : String { });
714
715     if (!isRetry)
716         settleShowPromise(*m_response);
717     else {
718         ASSERT(m_response->hasRetryPromise());
719         m_response->settleRetryPromise();
720     }
721
722     m_state = State::Closed;
723 }
724
725 ExceptionOr<void> PaymentRequest::complete(Optional<PaymentComplete>&& result)
726 {
727     ASSERT(m_state == State::Closed);
728     if (!m_activePaymentHandler)
729         return Exception { AbortError };
730
731     activePaymentHandler()->complete(WTFMove(result));
732     m_activePaymentHandler = WTF::nullopt;
733     return { };
734 }
735
736 ExceptionOr<void> PaymentRequest::retry(PaymentValidationErrors&& errors)
737 {
738     ASSERT(m_state == State::Closed);
739     if (!m_activePaymentHandler)
740         return Exception { AbortError };
741
742     m_state = State::Interactive;
743     return activePaymentHandler()->retry(WTFMove(errors));
744 }
745
746 void PaymentRequest::cancel()
747 {
748     m_activePaymentHandler = WTF::nullopt;
749
750     if (m_isUpdating) {
751         m_isCancelPending = true;
752         return;
753     }
754
755     abortWithException(Exception { AbortError });
756 }
757
758 } // namespace WebCore
759
760 #endif // ENABLE(PAYMENT_REQUEST)