[Payment Request] Crash in PaymentRequest::canMakePayment() when Apple Pay payment...
[WebKit-https.git] / Source / WebCore / Modules / paymentrequest / PaymentRequest.cpp
1 /*
2  * Copyright (C) 2017-2018 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 "PaymentAddress.h"
38 #include "PaymentCurrencyAmount.h"
39 #include "PaymentDetailsInit.h"
40 #include "PaymentHandler.h"
41 #include "PaymentMethodData.h"
42 #include "PaymentOptions.h"
43 #include "PaymentRequestUpdateEvent.h"
44 #include "PaymentResponse.h"
45 #include "ScriptController.h"
46 #include <JavaScriptCore/JSONObject.h>
47 #include <JavaScriptCore/ThrowScope.h>
48 #include <wtf/ASCIICType.h>
49 #include <wtf/RunLoop.h>
50 #include <wtf/Scope.h>
51 #include <wtf/UUID.h>
52
53 namespace WebCore {
54
55 // Implements the IsWellFormedCurrencyCode abstract operation from ECMA 402
56 // https://tc39.github.io/ecma402/#sec-iswellformedcurrencycode
57 static bool isWellFormedCurrencyCode(const String& currency)
58 {
59     if (currency.length() == 3)
60         return currency.isAllSpecialCharacters<isASCIIAlpha>();
61     return false;
62 }
63
64 // Implements the "valid decimal monetary value" validity checker
65 // https://www.w3.org/TR/payment-request/#dfn-valid-decimal-monetary-value
66 static bool isValidDecimalMonetaryValue(StringView value)
67 {
68     enum class State {
69         Start,
70         Sign,
71         Digit,
72         Dot,
73         DotDigit,
74     };
75
76     auto state = State::Start;
77     for (auto character : value.codeUnits()) {
78         switch (state) {
79         case State::Start:
80             if (character == '-') {
81                 state = State::Sign;
82                 break;
83             }
84
85             if (isASCIIDigit(character)) {
86                 state = State::Digit;
87                 break;
88             }
89
90             return false;
91
92         case State::Sign:
93             if (isASCIIDigit(character)) {
94                 state = State::Digit;
95                 break;
96             }
97
98             return false;
99
100         case State::Digit:
101             if (character == '.') {
102                 state = State::Dot;
103                 break;
104             }
105
106             if (isASCIIDigit(character)) {
107                 state = State::Digit;
108                 break;
109             }
110
111             return false;
112
113         case State::Dot:
114             if (isASCIIDigit(character)) {
115                 state = State::DotDigit;
116                 break;
117             }
118
119             return false;
120
121         case State::DotDigit:
122             if (isASCIIDigit(character)) {
123                 state = State::DotDigit;
124                 break;
125             }
126
127             return false;
128         }
129     }
130
131     if (state == State::Digit || state == State::DotDigit)
132         return true;
133
134     return false;
135 }
136
137 // Implements the "check and canonicalize amount" validity checker
138 // https://www.w3.org/TR/payment-request/#dfn-check-and-canonicalize-amount
139 static ExceptionOr<void> checkAndCanonicalizeAmount(PaymentCurrencyAmount& amount)
140 {
141     if (amount.currencySystem != "urn:iso:std:iso:4217")
142         return { };
143
144     if (!isWellFormedCurrencyCode(amount.currency))
145         return Exception { RangeError, makeString("\"", amount.currency, "\" is not a valid currency code.") };
146
147     if (!isValidDecimalMonetaryValue(amount.value))
148         return Exception { TypeError, makeString("\"", amount.value, "\" is not a valid decimal monetary value.") };
149
150     amount.currency = amount.currency.convertToASCIIUppercase();
151     return { };
152 }
153
154 // Implements the "check and canonicalize total" validity checker
155 // https://www.w3.org/TR/payment-request/#dfn-check-and-canonicalize-total
156 static ExceptionOr<void> checkAndCanonicalizeTotal(PaymentCurrencyAmount& total)
157 {
158     if (total.currencySystem != "urn:iso:std:iso:4217")
159         return { };
160
161     auto exception = checkAndCanonicalizeAmount(total);
162     if (exception.hasException())
163         return exception;
164
165     if (total.value[0] == '-')
166         return Exception { TypeError, ASCIILiteral("Total currency values cannot be negative.") };
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 std::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 std::nullopt;
240     }
241
242     if (isValidURLBasedPaymentMethodIdentifier(url))
243         return { WTFMove(url) };
244
245     return std::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, ASCIILiteral("At least one payment method is required.") };
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 PaymentRequest::PaymentRequest(Document& document, PaymentOptions&& options, PaymentDetailsInit&& details, Vector<String>&& serializedModifierData, Vector<Method>&& serializedMethodData, String&& selectedShippingOption)
357     : ActiveDOMObject { &document }
358     , m_options { WTFMove(options) }
359     , m_details { WTFMove(details) }
360     , m_serializedModifierData { WTFMove(serializedModifierData) }
361     , m_serializedMethodData { WTFMove(serializedMethodData) }
362     , m_shippingOption { WTFMove(selectedShippingOption) }
363 {
364     suspendIfNeeded();
365 }
366
367 PaymentRequest::~PaymentRequest()
368 {
369     ASSERT(!hasPendingActivity());
370     ASSERT(!m_activePaymentHandler);
371 }
372
373 static ExceptionOr<JSC::JSValue> parse(ScriptExecutionContext& context, const String& string)
374 {
375     auto scope = DECLARE_THROW_SCOPE(context.vm());
376     JSC::JSValue data = JSONParse(context.execState(), string);
377     if (scope.exception())
378         return Exception { ExistingExceptionError };
379     return WTFMove(data);
380 }
381
382 // https://www.w3.org/TR/payment-request/#show()-method
383 void PaymentRequest::show(Document& document, RefPtr<DOMPromise>&& detailsPromise, ShowPromise&& promise)
384 {
385     if (!document.frame()) {
386         promise.reject(Exception { AbortError });
387         return;
388     }
389
390     if (!UserGestureIndicator::processingUserGesture()) {
391         promise.reject(Exception { SecurityError, "show() must be triggered by user activation." });
392         return;
393     }
394
395     if (m_state != State::Created) {
396         promise.reject(Exception { InvalidStateError });
397         return;
398     }
399
400     if (PaymentHandler::hasActiveSession(document)) {
401         promise.reject(Exception { AbortError });
402         return;
403     }
404
405     m_state = State::Interactive;
406     ASSERT(!m_showPromise);
407     m_showPromise = WTFMove(promise);
408
409     RefPtr<PaymentHandler> selectedPaymentHandler;
410     for (auto& paymentMethod : m_serializedMethodData) {
411         auto data = parse(document, paymentMethod.serializedData);
412         if (data.hasException()) {
413             m_showPromise->reject(data.releaseException());
414             return;
415         }
416
417         auto handler = PaymentHandler::create(document, *this, paymentMethod.identifier);
418         if (!handler)
419             continue;
420
421         auto result = handler->convertData(data.releaseReturnValue());
422         if (result.hasException()) {
423             m_showPromise->reject(result.releaseException());
424             return;
425         }
426
427         if (!selectedPaymentHandler)
428             selectedPaymentHandler = WTFMove(handler);
429     }
430
431     if (!selectedPaymentHandler) {
432         m_showPromise->reject(Exception { NotSupportedError });
433         return;
434     }
435
436     auto exception = selectedPaymentHandler->show();
437     if (exception.hasException()) {
438         m_showPromise->reject(exception.releaseException());
439         return;
440     }
441
442     ASSERT(!m_activePaymentHandler);
443     m_activePaymentHandler = WTFMove(selectedPaymentHandler);
444     setPendingActivity(this); // unsetPendingActivity() is called below in stop()
445
446     if (!detailsPromise)
447         return;
448
449     exception = updateWith(UpdateReason::ShowDetailsResolved, detailsPromise.releaseNonNull());
450     ASSERT(!exception.hasException());
451 }
452
453 void PaymentRequest::abortWithException(Exception&& exception)
454 {
455     if (m_state != State::Interactive)
456         return;
457
458     if (auto paymentHandler = std::exchange(m_activePaymentHandler, nullptr)) {
459         unsetPendingActivity(this);
460         paymentHandler->hide();
461     }
462
463     ASSERT(m_state == State::Interactive);
464     m_state = State::Closed;
465     m_showPromise->reject(WTFMove(exception));
466 }
467
468 void PaymentRequest::stop()
469 {
470     abortWithException(Exception { AbortError });
471 }
472
473 // https://www.w3.org/TR/payment-request/#abort()-method
474 ExceptionOr<void> PaymentRequest::abort(AbortPromise&& promise)
475 {
476     if (m_state != State::Interactive)
477         return Exception { InvalidStateError };
478
479     stop();
480     promise.resolve();
481     return { };
482 }
483
484 // https://www.w3.org/TR/payment-request/#canmakepayment()-method
485 void PaymentRequest::canMakePayment(Document& document, CanMakePaymentPromise&& promise)
486 {
487     if (m_state != State::Created) {
488         promise.reject(Exception { InvalidStateError });
489         return;
490     }
491
492     auto scope = DECLARE_CATCH_SCOPE(document.execState()->vm());
493     for (auto& paymentMethod : m_serializedMethodData) {
494         auto data = parse(document, paymentMethod.serializedData);
495         ASSERT(!!scope.exception() == data.hasException());
496         if (data.hasException()) {
497             scope.clearException();
498             continue;
499         }
500
501         auto handler = PaymentHandler::create(document, *this, paymentMethod.identifier);
502         if (!handler)
503             continue;
504
505         auto exception = handler->convertData(data.releaseReturnValue());
506         ASSERT(!!scope.exception() == exception.hasException());
507         if (exception.hasException()) {
508             scope.clearException();
509             continue;
510         }
511
512         handler->canMakePayment([promise = WTFMove(promise)](bool canMakePayment) mutable {
513             promise.resolve(canMakePayment);
514         });
515         return;
516     }
517
518     promise.resolve(false);
519 }
520
521 const String& PaymentRequest::id() const
522 {
523     return m_details.id;
524 }
525
526 std::optional<PaymentShippingType> PaymentRequest::shippingType() const
527 {
528     if (m_options.requestShipping)
529         return m_options.shippingType;
530     return std::nullopt;
531 }
532
533 bool PaymentRequest::canSuspendForDocumentSuspension() const
534 {
535     switch (m_state) {
536     case State::Created:
537         ASSERT(!m_activePaymentHandler);
538         return true;
539     case State::Interactive:
540     case State::Closed:
541         return !m_activePaymentHandler;
542     }
543 }
544
545 void PaymentRequest::shippingAddressChanged(Ref<PaymentAddress>&& shippingAddress)
546 {
547     whenDetailsSettled([this, protectedThis = makeRefPtr(this), shippingAddress = makeRefPtr(shippingAddress.get())]() mutable {
548         m_shippingAddress = WTFMove(shippingAddress);
549         dispatchEvent(PaymentRequestUpdateEvent::create(eventNames().shippingaddresschangeEvent, *this));
550     });
551 }
552
553 void PaymentRequest::shippingOptionChanged(const String& shippingOption)
554 {
555     whenDetailsSettled([this, protectedThis = makeRefPtr(this), shippingOption]() mutable {
556         m_shippingOption = shippingOption;
557         dispatchEvent(PaymentRequestUpdateEvent::create(eventNames().shippingoptionchangeEvent, *this));
558     });
559 }
560
561 void PaymentRequest::paymentMethodChanged()
562 {
563     whenDetailsSettled([this, protectedThis = makeRefPtr(this)] {
564         m_activePaymentHandler->detailsUpdated(UpdateReason::PaymentMethodChanged, { });
565     });
566 }
567
568 ExceptionOr<void> PaymentRequest::updateWith(UpdateReason reason, Ref<DOMPromise>&& promise)
569 {
570     if (m_state != State::Interactive)
571         return Exception { InvalidStateError };
572
573     if (m_isUpdating)
574         return Exception { InvalidStateError };
575
576     m_isUpdating = true;
577
578     ASSERT(!m_detailsPromise);
579     m_detailsPromise = WTFMove(promise);
580     m_detailsPromise->whenSettled([this, protectedThis = makeRefPtr(this), reason]() {
581         settleDetailsPromise(reason);
582     });
583
584     return { };
585 }
586
587 ExceptionOr<void> PaymentRequest::completeMerchantValidation(Event& event, Ref<DOMPromise>&& merchantSessionPromise)
588 {
589     if (m_state != State::Interactive)
590         return Exception { InvalidStateError };
591
592     event.stopPropagation();
593     event.stopImmediatePropagation();
594
595     m_merchantSessionPromise = WTFMove(merchantSessionPromise);
596     m_merchantSessionPromise->whenSettled([this, protectedThis = makeRefPtr(this)]() {
597         if (m_state != State::Interactive)
598             return;
599
600         if (m_merchantSessionPromise->status() == DOMPromise::Status::Rejected) {
601             stop();
602             return;
603         }
604
605         auto exception = m_activePaymentHandler->merchantValidationCompleted(m_merchantSessionPromise->result());
606         if (exception.hasException()) {
607             abortWithException(exception.releaseException());
608             return;
609         }
610     });
611
612     return { };
613 }
614
615 void PaymentRequest::settleDetailsPromise(UpdateReason reason)
616 {
617     auto scopeExit = makeScopeExit([&] {
618         m_isUpdating = false;
619         m_detailsPromise = nullptr;
620     });
621
622     if (m_state != State::Interactive)
623         return;
624
625     if (m_detailsPromise->status() == DOMPromise::Status::Rejected) {
626         stop();
627         return;
628     }
629
630     auto& context = *m_detailsPromise->scriptExecutionContext();
631     auto throwScope = DECLARE_THROW_SCOPE(context.vm());
632     auto paymentDetailsUpdate = convertDictionary<PaymentDetailsUpdate>(*context.execState(), m_detailsPromise->result());
633     if (throwScope.exception()) {
634         abortWithException(Exception { ExistingExceptionError });
635         return;
636     }
637
638     auto totalResult = checkAndCanonicalizeTotal(paymentDetailsUpdate.total.amount);
639     if (totalResult.hasException()) {
640         abortWithException(totalResult.releaseException());
641         return;
642     }
643
644     auto detailsResult = checkAndCanonicalizeDetails(*context.execState(), paymentDetailsUpdate, m_options.requestShipping, ShouldValidatePaymentMethodIdentifier::Yes);
645     if (detailsResult.hasException()) {
646         abortWithException(detailsResult.releaseException());
647         return;
648     }
649
650     auto shippingOptionAndModifierData = detailsResult.releaseReturnValue();
651
652     m_details.total = WTFMove(paymentDetailsUpdate.total);
653     m_details.displayItems = WTFMove(paymentDetailsUpdate.displayItems);
654     if (m_options.requestShipping) {
655         m_details.shippingOptions = WTFMove(paymentDetailsUpdate.shippingOptions);
656         m_shippingOption = WTFMove(std::get<0>(shippingOptionAndModifierData));
657     }
658
659     m_details.modifiers = WTFMove(paymentDetailsUpdate.modifiers);
660     m_serializedModifierData = WTFMove(std::get<1>(shippingOptionAndModifierData));
661
662     auto result = m_activePaymentHandler->detailsUpdated(reason, paymentDetailsUpdate.error);
663     if (result.hasException()) {
664         abortWithException(result.releaseException());
665         return;
666     }
667 }
668
669 void PaymentRequest::whenDetailsSettled(std::function<void()>&& callback)
670 {
671     if (!m_detailsPromise) {
672         ASSERT(m_state == State::Interactive);
673         ASSERT(!m_isUpdating);
674         callback();
675         return;
676     }
677
678     m_detailsPromise->whenSettled([this, protectedThis = makeRefPtr(this), callback = WTFMove(callback)] {
679         if (m_state != State::Interactive)
680             return;
681
682         ASSERT(!m_isUpdating);
683         callback();
684     });
685 }
686
687 void PaymentRequest::accept(const String& methodName, JSC::Strong<JSC::JSObject>&& details, Ref<PaymentAddress>&& shippingAddress, const String& payerName, const String& payerEmail, const String& payerPhone)
688 {
689     ASSERT(m_state == State::Interactive);
690
691     auto response = PaymentResponse::create(*this);
692     response->setRequestId(m_details.id);
693     response->setMethodName(methodName);
694     response->setDetails(WTFMove(details));
695
696     if (m_options.requestShipping) {
697         response->setShippingAddress(shippingAddress.ptr());
698         response->setShippingOption(m_shippingOption);
699     }
700
701     if (m_options.requestPayerName)
702         response->setPayerName(payerName);
703
704     if (m_options.requestPayerEmail)
705         response->setPayerEmail(payerEmail);
706
707     if (m_options.requestPayerPhone)
708         response->setPayerPhone(payerPhone);
709
710     m_showPromise->resolve(response.get());
711     m_state = State::Closed;
712 }
713
714 void PaymentRequest::complete(std::optional<PaymentComplete>&& result)
715 {
716     ASSERT(m_state == State::Closed);
717     std::exchange(m_activePaymentHandler, nullptr)->complete(WTFMove(result));
718 }
719
720 void PaymentRequest::cancel()
721 {
722     if (m_state != State::Interactive)
723         return;
724
725     if (m_isUpdating)
726         return;
727
728     m_activePaymentHandler = nullptr;
729     stop();
730 }
731
732 } // namespace WebCore
733
734 #endif // ENABLE(PAYMENT_REQUEST)