a06176d45314e50ecd9c71a6ae3c54c5ed82ca7e
[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     for (auto& paymentMethod : m_serializedMethodData) {
493         auto data = parse(document, paymentMethod.serializedData);
494         if (data.hasException())
495             continue;
496
497         auto handler = PaymentHandler::create(document, *this, paymentMethod.identifier);
498         if (!handler)
499             continue;
500
501         auto exception = handler->convertData(data.releaseReturnValue());
502         if (exception.hasException())
503             continue;
504
505         handler->canMakePayment([promise = WTFMove(promise)](bool canMakePayment) mutable {
506             promise.resolve(canMakePayment);
507         });
508         return;
509     }
510
511     promise.resolve(false);
512 }
513
514 const String& PaymentRequest::id() const
515 {
516     return m_details.id;
517 }
518
519 std::optional<PaymentShippingType> PaymentRequest::shippingType() const
520 {
521     if (m_options.requestShipping)
522         return m_options.shippingType;
523     return std::nullopt;
524 }
525
526 bool PaymentRequest::canSuspendForDocumentSuspension() const
527 {
528     switch (m_state) {
529     case State::Created:
530         ASSERT(!m_activePaymentHandler);
531         return true;
532     case State::Interactive:
533     case State::Closed:
534         return !m_activePaymentHandler;
535     }
536 }
537
538 void PaymentRequest::shippingAddressChanged(Ref<PaymentAddress>&& shippingAddress)
539 {
540     whenDetailsSettled([this, protectedThis = makeRefPtr(this), shippingAddress = makeRefPtr(shippingAddress.get())]() mutable {
541         m_shippingAddress = WTFMove(shippingAddress);
542         dispatchEvent(PaymentRequestUpdateEvent::create(eventNames().shippingaddresschangeEvent, *this));
543     });
544 }
545
546 void PaymentRequest::shippingOptionChanged(const String& shippingOption)
547 {
548     whenDetailsSettled([this, protectedThis = makeRefPtr(this), shippingOption]() mutable {
549         m_shippingOption = shippingOption;
550         dispatchEvent(PaymentRequestUpdateEvent::create(eventNames().shippingoptionchangeEvent, *this));
551     });
552 }
553
554 void PaymentRequest::paymentMethodChanged()
555 {
556     whenDetailsSettled([this, protectedThis = makeRefPtr(this)] {
557         m_activePaymentHandler->detailsUpdated(UpdateReason::PaymentMethodChanged, { });
558     });
559 }
560
561 ExceptionOr<void> PaymentRequest::updateWith(UpdateReason reason, Ref<DOMPromise>&& promise)
562 {
563     if (m_state != State::Interactive)
564         return Exception { InvalidStateError };
565
566     if (m_isUpdating)
567         return Exception { InvalidStateError };
568
569     m_isUpdating = true;
570
571     ASSERT(!m_detailsPromise);
572     m_detailsPromise = WTFMove(promise);
573     m_detailsPromise->whenSettled([this, protectedThis = makeRefPtr(this), reason]() {
574         settleDetailsPromise(reason);
575     });
576
577     return { };
578 }
579
580 ExceptionOr<void> PaymentRequest::completeMerchantValidation(Event& event, Ref<DOMPromise>&& merchantSessionPromise)
581 {
582     if (m_state != State::Interactive)
583         return Exception { InvalidStateError };
584
585     event.stopPropagation();
586     event.stopImmediatePropagation();
587
588     m_merchantSessionPromise = WTFMove(merchantSessionPromise);
589     m_merchantSessionPromise->whenSettled([this, protectedThis = makeRefPtr(this)]() {
590         if (m_state != State::Interactive)
591             return;
592
593         if (m_merchantSessionPromise->status() == DOMPromise::Status::Rejected) {
594             stop();
595             return;
596         }
597
598         auto exception = m_activePaymentHandler->merchantValidationCompleted(m_merchantSessionPromise->result());
599         if (exception.hasException()) {
600             abortWithException(exception.releaseException());
601             return;
602         }
603     });
604
605     return { };
606 }
607
608 void PaymentRequest::settleDetailsPromise(UpdateReason reason)
609 {
610     auto scopeExit = makeScopeExit([&] {
611         m_isUpdating = false;
612         m_detailsPromise = nullptr;
613     });
614
615     if (m_state != State::Interactive)
616         return;
617
618     if (m_detailsPromise->status() == DOMPromise::Status::Rejected) {
619         stop();
620         return;
621     }
622
623     auto& context = *m_detailsPromise->scriptExecutionContext();
624     auto throwScope = DECLARE_THROW_SCOPE(context.vm());
625     auto paymentDetailsUpdate = convertDictionary<PaymentDetailsUpdate>(*context.execState(), m_detailsPromise->result());
626     if (throwScope.exception()) {
627         abortWithException(Exception { ExistingExceptionError });
628         return;
629     }
630
631     auto totalResult = checkAndCanonicalizeTotal(paymentDetailsUpdate.total.amount);
632     if (totalResult.hasException()) {
633         abortWithException(totalResult.releaseException());
634         return;
635     }
636
637     auto detailsResult = checkAndCanonicalizeDetails(*context.execState(), paymentDetailsUpdate, m_options.requestShipping, ShouldValidatePaymentMethodIdentifier::Yes);
638     if (detailsResult.hasException()) {
639         abortWithException(detailsResult.releaseException());
640         return;
641     }
642
643     auto shippingOptionAndModifierData = detailsResult.releaseReturnValue();
644
645     m_details.total = WTFMove(paymentDetailsUpdate.total);
646     m_details.displayItems = WTFMove(paymentDetailsUpdate.displayItems);
647     if (m_options.requestShipping) {
648         m_details.shippingOptions = WTFMove(paymentDetailsUpdate.shippingOptions);
649         m_shippingOption = WTFMove(std::get<0>(shippingOptionAndModifierData));
650     }
651
652     m_details.modifiers = WTFMove(paymentDetailsUpdate.modifiers);
653     m_serializedModifierData = WTFMove(std::get<1>(shippingOptionAndModifierData));
654
655     auto result = m_activePaymentHandler->detailsUpdated(reason, paymentDetailsUpdate.error);
656     if (result.hasException()) {
657         abortWithException(result.releaseException());
658         return;
659     }
660 }
661
662 void PaymentRequest::whenDetailsSettled(std::function<void()>&& callback)
663 {
664     if (!m_detailsPromise) {
665         ASSERT(m_state == State::Interactive);
666         ASSERT(!m_isUpdating);
667         callback();
668         return;
669     }
670
671     m_detailsPromise->whenSettled([this, protectedThis = makeRefPtr(this), callback = WTFMove(callback)] {
672         if (m_state != State::Interactive)
673             return;
674
675         ASSERT(!m_isUpdating);
676         callback();
677     });
678 }
679
680 void PaymentRequest::accept(const String& methodName, JSC::Strong<JSC::JSObject>&& details, Ref<PaymentAddress>&& shippingAddress, const String& payerName, const String& payerEmail, const String& payerPhone)
681 {
682     ASSERT(m_state == State::Interactive);
683
684     auto response = PaymentResponse::create(*this);
685     response->setRequestId(m_details.id);
686     response->setMethodName(methodName);
687     response->setDetails(WTFMove(details));
688
689     if (m_options.requestShipping) {
690         response->setShippingAddress(shippingAddress.ptr());
691         response->setShippingOption(m_shippingOption);
692     }
693
694     if (m_options.requestPayerName)
695         response->setPayerName(payerName);
696
697     if (m_options.requestPayerEmail)
698         response->setPayerEmail(payerEmail);
699
700     if (m_options.requestPayerPhone)
701         response->setPayerPhone(payerPhone);
702
703     m_showPromise->resolve(response.get());
704     m_state = State::Closed;
705 }
706
707 void PaymentRequest::complete(std::optional<PaymentComplete>&& result)
708 {
709     ASSERT(m_state == State::Closed);
710     std::exchange(m_activePaymentHandler, nullptr)->complete(WTFMove(result));
711 }
712
713 void PaymentRequest::cancel()
714 {
715     if (m_state != State::Interactive)
716         return;
717
718     if (m_isUpdating)
719         return;
720
721     m_activePaymentHandler = nullptr;
722     stop();
723 }
724
725 } // namespace WebCore
726
727 #endif // ENABLE(PAYMENT_REQUEST)