Move URL from WebCore to WTF
[WebKit-https.git] / Source / WebKit / UIProcess / ApplePay / cocoa / WebPaymentCoordinatorProxyCocoa.mm
1 /*
2  * Copyright (C) 2016-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 "WebPaymentCoordinatorProxyCocoa.h"
28
29 #if ENABLE(APPLE_PAY)
30
31 #import "WebPaymentCoordinatorProxy.h"
32 #import "WebProcessPool.h"
33 #import <WebCore/PaymentAuthorizationStatus.h>
34 #import <WebCore/PaymentHeaders.h>
35 #import <pal/cocoa/PassKitSoftLink.h>
36 #import <wtf/BlockPtr.h>
37 #import <wtf/RunLoop.h>
38 #import <wtf/URL.h>
39
40 #if PLATFORM(IOS) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 101300)
41 SOFT_LINK_FRAMEWORK(Contacts)
42 SOFT_LINK_CONSTANT(Contacts, CNPostalAddressStreetKey, NSString *);
43 SOFT_LINK_CONSTANT(Contacts, CNPostalAddressSubLocalityKey, NSString *);
44 SOFT_LINK_CONSTANT(Contacts, CNPostalAddressCityKey, NSString *);
45 SOFT_LINK_CONSTANT(Contacts, CNPostalAddressSubAdministrativeAreaKey, NSString *);
46 SOFT_LINK_CONSTANT(Contacts, CNPostalAddressStateKey, NSString *);
47 SOFT_LINK_CONSTANT(Contacts, CNPostalAddressPostalCodeKey, NSString *);
48 SOFT_LINK_CONSTANT(Contacts, CNPostalAddressCountryKey, NSString *);
49 SOFT_LINK_CONSTANT(Contacts, CNPostalAddressISOCountryCodeKey, NSString *);
50 SOFT_LINK_CONSTANT(PAL::PassKit, PKPaymentErrorDomain, NSString *);
51 #endif
52
53 @implementation WKPaymentAuthorizationViewControllerDelegate
54
55 - (instancetype)initWithPaymentCoordinatorProxy:(WebKit::WebPaymentCoordinatorProxy&)webPaymentCoordinatorProxy
56 {
57     if (!(self = [super init]))
58         return nullptr;
59
60     _webPaymentCoordinatorProxy = &webPaymentCoordinatorProxy;
61
62     return self;
63 }
64
65 - (void)invalidate
66 {
67     _webPaymentCoordinatorProxy = nullptr;
68     if (_paymentAuthorizedCompletion) {
69 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300) || (PLATFORM(IOS_FAMILY) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000)
70         _paymentAuthorizedCompletion(adoptNS([PAL::allocPKPaymentAuthorizationResultInstance() initWithStatus:PKPaymentAuthorizationStatusFailure errors:@[ ]]).get());
71 #else
72         _paymentAuthorizedCompletion(PKPaymentAuthorizationStatusFailure);
73 #endif
74         _paymentAuthorizedCompletion = nullptr;
75     }
76 }
77
78 - (void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller willFinishWithError:(NSError *)error
79 {
80 }
81
82 - (void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller didRequestMerchantSession:(void(^)(PKPaymentMerchantSession *, NSError *))sessionBlock
83 {
84     ASSERT(!_sessionBlock);
85     _sessionBlock = sessionBlock;
86
87     [PAL::getPKPaymentAuthorizationViewControllerClass() paymentServicesMerchantURL:^(NSURL *merchantURL, NSError *error) {
88         if (error)
89             LOG_ERROR("PKCanMakePaymentsWithMerchantIdentifierAndDomain error %@", error);
90
91         dispatch_async(dispatch_get_main_queue(), ^{
92             ASSERT(_sessionBlock);
93
94             if (!_webPaymentCoordinatorProxy) {
95                 _sessionBlock(nullptr, nullptr);
96                 return;
97             }
98
99             _webPaymentCoordinatorProxy->validateMerchant(merchantURL);
100         });
101     }];
102 }
103
104 static WebCore::ApplePaySessionPaymentRequest::ShippingMethod toShippingMethod(PKShippingMethod *shippingMethod)
105 {
106     ASSERT(shippingMethod);
107
108     WebCore::ApplePaySessionPaymentRequest::ShippingMethod result;
109     result.label = shippingMethod.label;
110     result.detail = shippingMethod.detail;
111     result.amount = shippingMethod.amount.stringValue;
112     result.identifier = shippingMethod.identifier;
113
114     return result;
115 }
116
117 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 101300) || PLATFORM(IOS_FAMILY)
118
119 - (void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller didAuthorizePayment:(PKPayment *)payment handler:(void (^)(PKPaymentAuthorizationResult *result))completion
120 {
121 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300) || (PLATFORM(IOS_FAMILY) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000)
122     if (!_webPaymentCoordinatorProxy) {
123         completion(adoptNS([PAL::allocPKPaymentAuthorizationResultInstance() initWithStatus:PKPaymentAuthorizationStatusFailure errors:@[ ]]).get());
124         return;
125     }
126
127     ASSERT(!_paymentAuthorizedCompletion);
128     _paymentAuthorizedCompletion = completion;
129
130     _webPaymentCoordinatorProxy->didAuthorizePayment(WebCore::Payment(payment));
131 #endif
132 }
133
134 - (void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller didSelectPaymentMethod:(PKPaymentMethod *)paymentMethod handler:(void (^)(PKPaymentRequestPaymentMethodUpdate *update))completion
135 {
136 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300) || (PLATFORM(IOS_FAMILY) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000)
137     if (!_webPaymentCoordinatorProxy) {
138         completion(adoptNS([PAL::allocPKPaymentRequestPaymentMethodUpdateInstance() initWithPaymentSummaryItems:@[ ]]).get());
139         return;
140     }
141
142     ASSERT(!_didSelectPaymentMethodCompletion);
143     _didSelectPaymentMethodCompletion = completion;
144     _webPaymentCoordinatorProxy->didSelectPaymentMethod(WebCore::PaymentMethod(paymentMethod));
145 #endif
146 }
147
148 - (void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller didSelectShippingMethod:(PKShippingMethod *)shippingMethod handler:(void (^)(PKPaymentRequestShippingMethodUpdate *update))completion {
149 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300) || (PLATFORM(IOS_FAMILY) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000)
150     if (!_webPaymentCoordinatorProxy) {
151         completion(adoptNS([PAL::allocPKPaymentRequestShippingMethodUpdateInstance() initWithPaymentSummaryItems:@[ ]]).get());
152         return;
153     }
154
155     ASSERT(!_didSelectShippingMethodCompletion);
156     _didSelectShippingMethodCompletion = completion;
157     _webPaymentCoordinatorProxy->didSelectShippingMethod(toShippingMethod(shippingMethod));
158 #endif
159 }
160
161 - (void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller didSelectShippingContact:(PKContact *)contact handler:(void (^)(PKPaymentRequestShippingContactUpdate *update))completion
162 {
163 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300) || (PLATFORM(IOS_FAMILY) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000)
164     if (!_webPaymentCoordinatorProxy) {
165         completion(adoptNS([PAL::allocPKPaymentRequestShippingContactUpdateInstance() initWithErrors:@[ ] paymentSummaryItems:@[ ] shippingMethods:@[ ]]).get());
166         return;
167     }
168
169     ASSERT(!_didSelectShippingContactCompletion);
170     _didSelectShippingContactCompletion = completion;
171     _webPaymentCoordinatorProxy->didSelectShippingContact(WebCore::PaymentContact(contact));
172 #endif
173 }
174
175 #endif
176
177 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101300) || (PLATFORM(IOS_FAMILY) && __IPHONE_OS_VERSION_MIN_REQUIRED < 110000)
178
179 - (void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller didAuthorizePayment:(PKPayment *)payment completion:(void (^)(PKPaymentAuthorizationStatus))completion
180 {
181     if (!_webPaymentCoordinatorProxy) {
182         completion(PKPaymentAuthorizationStatusFailure);
183         return;
184     }
185
186     ASSERT(!_paymentAuthorizedCompletion);
187     _paymentAuthorizedCompletion = completion;
188
189     _webPaymentCoordinatorProxy->didAuthorizePayment(WebCore::Payment(payment));
190 }
191
192 - (void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller didSelectShippingMethod:(PKShippingMethod *)shippingMethod completion:(void (^)(PKPaymentAuthorizationStatus status, NSArray<PKPaymentSummaryItem *> *summaryItems))completion
193 {
194     if (!_webPaymentCoordinatorProxy) {
195         completion(PKPaymentAuthorizationStatusFailure, @[ ]);
196         return;
197     }
198
199     ASSERT(!_didSelectShippingMethodCompletion);
200     _didSelectShippingMethodCompletion = completion;
201     _webPaymentCoordinatorProxy->didSelectShippingMethod(toShippingMethod(shippingMethod));
202 }
203
204 - (void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller didSelectPaymentMethod:(PKPaymentMethod *)paymentMethod completion:(void (^)(NSArray<PKPaymentSummaryItem *> *summaryItems))completion
205 {
206     if (!_webPaymentCoordinatorProxy) {
207         completion(@[ ]);
208         return;
209     }
210
211     ASSERT(!_didSelectPaymentMethodCompletion);
212     _didSelectPaymentMethodCompletion = completion;
213
214     _webPaymentCoordinatorProxy->didSelectPaymentMethod(WebCore::PaymentMethod(paymentMethod));
215 }
216
217 - (void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller didSelectShippingContact:(PKContact *)contact completion:(void (^)(PKPaymentAuthorizationStatus status, NSArray<PKShippingMethod *> *shippingMethods, NSArray<PKPaymentSummaryItem *> *summaryItems))completion
218 {
219     if (!_webPaymentCoordinatorProxy) {
220         completion(PKPaymentAuthorizationStatusFailure, @[ ], @[ ]);
221         return;
222     }
223
224     ASSERT(!_didSelectShippingContactCompletion);
225     _didSelectShippingContactCompletion = completion;
226     _webPaymentCoordinatorProxy->didSelectShippingContact(WebCore::PaymentContact(contact));
227 }
228
229 #endif
230
231 - (void)paymentAuthorizationViewControllerDidFinish:(PKPaymentAuthorizationViewController *)controller
232 {
233     if (!_webPaymentCoordinatorProxy)
234         return;
235
236     if (!_didReachFinalState)
237         _webPaymentCoordinatorProxy->didCancelPaymentSession();
238
239     _webPaymentCoordinatorProxy->hidePaymentUI();
240 }
241
242 @end
243
244 // FIXME: Once rdar://problem/24420024 has been fixed, import PKPaymentRequest_Private.h instead.
245 @interface PKPaymentRequest ()
246 @property (nonatomic, retain) NSURL *originatingURL;
247 @end
248
249 @interface PKPaymentRequest ()
250 // FIXME: Remove this once it's in an SDK.
251 @property (nonatomic, strong) NSArray *thumbnailURLs;
252 @property (nonatomic, strong) NSURL *thumbnailURL;
253
254 @property (nonatomic, assign) BOOL expectsMerchantSession;
255 @end
256
257 namespace WebKit {
258
259 bool WebPaymentCoordinatorProxy::platformCanMakePayments()
260 {
261     return [PAL::getPKPaymentAuthorizationViewControllerClass() canMakePayments];
262 }
263
264 void WebPaymentCoordinatorProxy::platformCanMakePaymentsWithActiveCard(const String& merchantIdentifier, const String& domainName, WTF::Function<void (bool)>&& completionHandler)
265 {
266 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300) || (PLATFORM(IOS_FAMILY) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000)
267     PKCanMakePaymentsWithMerchantIdentifierDomainAndSourceApplication(merchantIdentifier, domainName, m_webPageProxy.process().processPool().configuration().sourceApplicationSecondaryIdentifier(), BlockPtr<void(BOOL, NSError *)>::fromCallable([completionHandler = WTFMove(completionHandler)](BOOL canMakePayments, NSError *error) mutable {
268         if (error)
269             LOG_ERROR("PKCanMakePaymentsWithMerchantIdentifierAndDomain error %@", error);
270
271         RunLoop::main().dispatch([completionHandler = WTFMove(completionHandler), canMakePayments] {
272             completionHandler(canMakePayments);
273         });
274     }).get());
275 #else
276     PKCanMakePaymentsWithMerchantIdentifierAndDomain(merchantIdentifier, domainName, BlockPtr<void(BOOL, NSError *)>::fromCallable([completionHandler = WTFMove(completionHandler)](BOOL canMakePayments, NSError *error) mutable {
277         if (error)
278             LOG_ERROR("PKCanMakePaymentsWithMerchantIdentifierAndDomain error %@", error);
279
280         RunLoop::main().dispatch([completionHandler = WTFMove(completionHandler), canMakePayments] {
281             completionHandler(canMakePayments);
282         });
283     }).get());
284 #endif
285 }
286
287 void WebPaymentCoordinatorProxy::platformOpenPaymentSetup(const String& merchantIdentifier, const String& domainName, WTF::Function<void (bool)>&& completionHandler)
288 {
289     auto passLibrary = adoptNS([PAL::allocPKPassLibraryInstance() init]);
290     [passLibrary openPaymentSetupForMerchantIdentifier:merchantIdentifier domain:domainName completion:BlockPtr<void (BOOL)>::fromCallable([completionHandler = WTFMove(completionHandler)](BOOL result) mutable {
291         RunLoop::main().dispatch([completionHandler = WTFMove(completionHandler), result] {
292             completionHandler(result);
293         });
294     }).get()];
295 }
296
297 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300) || (PLATFORM(IOS_FAMILY) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000)
298 static RetainPtr<NSSet> toPKContactFields(const WebCore::ApplePaySessionPaymentRequest::ContactFields& contactFields)
299 {
300     Vector<NSString *> result;
301
302     if (contactFields.postalAddress)
303         result.append(PAL::get_PassKit_PKContactFieldPostalAddress());
304     if (contactFields.phone)
305         result.append(PAL::get_PassKit_PKContactFieldPhoneNumber());
306     if (contactFields.email)
307         result.append(PAL::get_PassKit_PKContactFieldEmailAddress());
308     if (contactFields.name)
309         result.append(PAL::get_PassKit_PKContactFieldName());
310     if (contactFields.phoneticName)
311         result.append(PAL::get_PassKit_PKContactFieldPhoneticName());
312
313     return adoptNS([[NSSet alloc] initWithObjects:result.data() count:result.size()]);
314 }
315 #else
316 static PKAddressField toPKAddressField(const WebCore::ApplePaySessionPaymentRequest::ContactFields& contactFields)
317 {
318     PKAddressField result = 0;
319
320     if (contactFields.postalAddress)
321         result |= PKAddressFieldPostalAddress;
322     if (contactFields.phone)
323         result |= PKAddressFieldPhone;
324     if (contactFields.email)
325         result |= PKAddressFieldEmail;
326     if (contactFields.name)
327         result |= PKAddressFieldName;
328
329     return result;
330 }
331 #endif
332
333 static PKPaymentSummaryItemType toPKPaymentSummaryItemType(WebCore::ApplePaySessionPaymentRequest::LineItem::Type type)
334 {
335     switch (type) {
336     case WebCore::ApplePaySessionPaymentRequest::LineItem::Type::Final:
337         return PKPaymentSummaryItemTypeFinal;
338
339     case WebCore::ApplePaySessionPaymentRequest::LineItem::Type::Pending:
340         return PKPaymentSummaryItemTypePending;
341     }
342 }
343
344 static NSDecimalNumber *toDecimalNumber(const String& amount)
345 {
346     if (!amount)
347         return [NSDecimalNumber zero];
348     return [NSDecimalNumber decimalNumberWithString:amount locale:@{ NSLocaleDecimalSeparator : @"." }];
349 }
350
351 static RetainPtr<PKPaymentSummaryItem> toPKPaymentSummaryItem(const WebCore::ApplePaySessionPaymentRequest::LineItem& lineItem)
352 {
353     return [PAL::getPKPaymentSummaryItemClass() summaryItemWithLabel:lineItem.label amount:toDecimalNumber(lineItem.amount) type:toPKPaymentSummaryItemType(lineItem.type)];
354 }
355
356 static RetainPtr<NSArray> toPKPaymentSummaryItems(const WebCore::ApplePaySessionPaymentRequest::TotalAndLineItems& totalAndLineItems)
357 {
358     auto paymentSummaryItems = adoptNS([[NSMutableArray alloc] init]);
359     for (auto& lineItem : totalAndLineItems.lineItems) {
360         if (auto summaryItem = toPKPaymentSummaryItem(lineItem))
361             [paymentSummaryItems addObject:summaryItem.get()];
362     }
363
364     if (auto totalItem = toPKPaymentSummaryItem(totalAndLineItems.total))
365         [paymentSummaryItems addObject:totalItem.get()];
366
367     return paymentSummaryItems;
368 }
369
370 static PKMerchantCapability toPKMerchantCapabilities(const WebCore::ApplePaySessionPaymentRequest::MerchantCapabilities& merchantCapabilities)
371 {
372     PKMerchantCapability result = 0;
373     if (merchantCapabilities.supports3DS)
374         result |= PKMerchantCapability3DS;
375     if (merchantCapabilities.supportsEMV)
376         result |= PKMerchantCapabilityEMV;
377     if (merchantCapabilities.supportsCredit)
378         result |= PKMerchantCapabilityCredit;
379     if (merchantCapabilities.supportsDebit)
380         result |= PKMerchantCapabilityDebit;
381
382     return result;
383 }
384
385 static RetainPtr<NSArray> toSupportedNetworks(const Vector<String>& supportedNetworks)
386 {
387     auto result = adoptNS([[NSMutableArray alloc] initWithCapacity:supportedNetworks.size()]);
388     for (auto& supportedNetwork : supportedNetworks)
389         [result addObject:supportedNetwork];
390     return result;
391 }
392
393 static PKShippingType toPKShippingType(WebCore::ApplePaySessionPaymentRequest::ShippingType shippingType)
394 {
395     switch (shippingType) {
396     case WebCore::ApplePaySessionPaymentRequest::ShippingType::Shipping:
397         return PKShippingTypeShipping;
398
399     case WebCore::ApplePaySessionPaymentRequest::ShippingType::Delivery:
400         return PKShippingTypeDelivery;
401
402     case WebCore::ApplePaySessionPaymentRequest::ShippingType::StorePickup:
403         return PKShippingTypeStorePickup;
404
405     case WebCore::ApplePaySessionPaymentRequest::ShippingType::ServicePickup:
406         return PKShippingTypeServicePickup;
407     }
408 }
409
410 static RetainPtr<PKShippingMethod> toPKShippingMethod(const WebCore::ApplePaySessionPaymentRequest::ShippingMethod& shippingMethod)
411 {
412     RetainPtr<PKShippingMethod> result = [PAL::getPKShippingMethodClass() summaryItemWithLabel:shippingMethod.label amount:toDecimalNumber(shippingMethod.amount)];
413     [result setIdentifier:shippingMethod.identifier];
414     [result setDetail:shippingMethod.detail];
415
416     return result;
417 }
418     
419 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300) || (PLATFORM(IOS_FAMILY) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000)
420 static RetainPtr<NSSet> toNSSet(const Vector<String>& strings)
421 {
422     if (strings.isEmpty())
423         return nil;
424
425     auto mutableSet = adoptNS([[NSMutableSet alloc] initWithCapacity:strings.size()]);
426     for (auto& string : strings)
427         [mutableSet addObject:string];
428
429     return WTFMove(mutableSet);
430 }
431 #endif
432
433 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300 && __MAC_OS_X_VERSION_MAX_ALLOWED >= 101304) || PLATFORM(IOS_FAMILY)
434 static PKPaymentRequestAPIType toAPIType(WebCore::ApplePaySessionPaymentRequest::Requester requester)
435 {
436     switch (requester) {
437     case WebCore::ApplePaySessionPaymentRequest::Requester::ApplePayJS:
438         return PKPaymentRequestAPITypeWebJS;
439     case WebCore::ApplePaySessionPaymentRequest::Requester::PaymentRequest:
440         return PKPaymentRequestAPITypeWebPaymentRequest;
441     }
442 }
443 #endif
444
445 RetainPtr<PKPaymentRequest> toPKPaymentRequest(WebPageProxy& webPageProxy, const URL& originatingURL, const Vector<URL>& linkIconURLs, const WebCore::ApplePaySessionPaymentRequest& paymentRequest)
446 {
447     auto result = adoptNS([PAL::allocPKPaymentRequestInstance() init]);
448
449     [result setOriginatingURL:originatingURL];
450
451     if ([result respondsToSelector:@selector(setThumbnailURLs:)]) {
452         auto thumbnailURLs = adoptNS([[NSMutableArray alloc] init]);
453         for (auto& linkIconURL : linkIconURLs)
454             [thumbnailURLs addObject:static_cast<NSURL *>(linkIconURL)];
455
456         [result setThumbnailURLs:thumbnailURLs.get()];
457     } else if (!linkIconURLs.isEmpty())
458         [result setThumbnailURL:linkIconURLs[0]];
459
460 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300 && __MAC_OS_X_VERSION_MAX_ALLOWED >= 101304) || PLATFORM(IOS_FAMILY)
461     [result setAPIType:toAPIType(paymentRequest.requester())];
462 #endif
463
464     [result setCountryCode:paymentRequest.countryCode()];
465     [result setCurrencyCode:paymentRequest.currencyCode()];
466     [result setBillingContact:paymentRequest.billingContact().pkContact()];
467     [result setShippingContact:paymentRequest.shippingContact().pkContact()];
468 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300) || (PLATFORM(IOS_FAMILY) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000)
469     [result setRequiredBillingContactFields:toPKContactFields(paymentRequest.requiredBillingContactFields()).get()];
470     [result setRequiredShippingContactFields:toPKContactFields(paymentRequest.requiredShippingContactFields()).get()];
471 #else
472     [result setRequiredBillingAddressFields:toPKAddressField(paymentRequest.requiredBillingContactFields())];
473     [result setRequiredShippingAddressFields:toPKAddressField(paymentRequest.requiredShippingContactFields())];
474 #endif
475
476     [result setSupportedNetworks:toSupportedNetworks(paymentRequest.supportedNetworks()).get()];
477     [result setMerchantCapabilities:toPKMerchantCapabilities(paymentRequest.merchantCapabilities())];
478
479     [result setShippingType:toPKShippingType(paymentRequest.shippingType())];
480
481     auto shippingMethods = adoptNS([[NSMutableArray alloc] init]);
482     for (auto& shippingMethod : paymentRequest.shippingMethods())
483         [shippingMethods addObject:toPKShippingMethod(shippingMethod).get()];
484     [result setShippingMethods:shippingMethods.get()];
485
486     auto paymentSummaryItems = adoptNS([[NSMutableArray alloc] init]);
487     for (auto& lineItem : paymentRequest.lineItems()) {
488         if (auto summaryItem = toPKPaymentSummaryItem(lineItem))
489             [paymentSummaryItems addObject:summaryItem.get()];
490     }
491
492     if (auto totalItem = toPKPaymentSummaryItem(paymentRequest.total()))
493         [paymentSummaryItems addObject:totalItem.get()];
494
495     [result setPaymentSummaryItems:paymentSummaryItems.get()];
496
497     [result setExpectsMerchantSession:YES];
498
499     if (!paymentRequest.applicationData().isNull()) {
500         auto applicationData = adoptNS([[NSData alloc] initWithBase64EncodedString:paymentRequest.applicationData() options:0]);
501         [result setApplicationData:applicationData.get()];
502     }
503
504 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300) || (PLATFORM(IOS_FAMILY) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000)
505     [result setSupportedCountries:toNSSet(paymentRequest.supportedCountries()).get()];
506 #endif
507
508     // FIXME: Instead of using respondsToSelector, this should use a proper #if version check.
509     auto& configuration = webPageProxy.process().processPool().configuration();
510
511     if (!configuration.sourceApplicationBundleIdentifier().isEmpty() && [result respondsToSelector:@selector(setSourceApplicationBundleIdentifier:)])
512         [result setSourceApplicationBundleIdentifier:configuration.sourceApplicationBundleIdentifier()];
513
514     if (!configuration.sourceApplicationSecondaryIdentifier().isEmpty() && [result respondsToSelector:@selector(setSourceApplicationSecondaryIdentifier:)])
515         [result setSourceApplicationSecondaryIdentifier:configuration.sourceApplicationSecondaryIdentifier()];
516
517 #if PLATFORM(IOS_FAMILY)
518     if (!configuration.ctDataConnectionServiceType().isEmpty() && [result respondsToSelector:@selector(setCTDataConnectionServiceType:)])
519         [result setCTDataConnectionServiceType:configuration.ctDataConnectionServiceType()];
520 #endif
521
522     return result;
523 }
524
525 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300) || (PLATFORM(IOS_FAMILY) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000)
526 static PKPaymentAuthorizationStatus toPKPaymentAuthorizationStatus(WebCore::PaymentAuthorizationStatus status)
527 {
528     switch (status) {
529     case WebCore::PaymentAuthorizationStatus::Success:
530         return PKPaymentAuthorizationStatusSuccess;
531     case WebCore::PaymentAuthorizationStatus::Failure:
532         return PKPaymentAuthorizationStatusFailure;
533     case WebCore::PaymentAuthorizationStatus::PINRequired:
534         return PKPaymentAuthorizationStatusPINRequired;
535     case WebCore::PaymentAuthorizationStatus::PINIncorrect:
536         return PKPaymentAuthorizationStatusPINIncorrect;
537     case WebCore::PaymentAuthorizationStatus::PINLockout:
538         return PKPaymentAuthorizationStatusPINLockout;
539     }
540 }
541
542 static PKPaymentErrorCode toPKPaymentErrorCode(WebCore::PaymentError::Code code)
543 {
544     switch (code) {
545     case WebCore::PaymentError::Code::Unknown:
546         return PKPaymentUnknownError;
547     case WebCore::PaymentError::Code::ShippingContactInvalid:
548         return PKPaymentShippingContactInvalidError;
549     case WebCore::PaymentError::Code::BillingContactInvalid:
550         return PKPaymentBillingContactInvalidError;
551     case WebCore::PaymentError::Code::AddressUnserviceable:
552         return PKPaymentShippingAddressUnserviceableError;
553     }
554 }
555
556 static RetainPtr<NSError> toNSError(const WebCore::PaymentError& error)
557 {
558     auto userInfo = adoptNS([[NSMutableDictionary alloc] init]);
559     [userInfo setObject:error.message forKey:NSLocalizedDescriptionKey];
560
561     if (error.contactField) {
562         NSString *pkContactField = nil;
563         NSString *postalAddressKey = nil;
564
565         switch (*error.contactField) {
566         case WebCore::PaymentError::ContactField::PhoneNumber:
567             pkContactField = PAL::get_PassKit_PKContactFieldPhoneNumber();
568             break;
569
570         case WebCore::PaymentError::ContactField::EmailAddress:
571             pkContactField = PAL::get_PassKit_PKContactFieldEmailAddress();
572             break;
573
574         case WebCore::PaymentError::ContactField::Name:
575             pkContactField = PAL::get_PassKit_PKContactFieldName();
576             break;
577
578         case WebCore::PaymentError::ContactField::PhoneticName:
579             pkContactField = PAL::get_PassKit_PKContactFieldPhoneticName();
580             break;
581
582         case WebCore::PaymentError::ContactField::PostalAddress:
583             pkContactField = PAL::get_PassKit_PKContactFieldPostalAddress();
584             break;
585
586         case WebCore::PaymentError::ContactField::AddressLines:
587             pkContactField = PAL::get_PassKit_PKContactFieldPostalAddress();
588             postalAddressKey = getCNPostalAddressStreetKey();
589             break;
590
591         case WebCore::PaymentError::ContactField::SubLocality:
592             pkContactField = PAL::get_PassKit_PKContactFieldPostalAddress();
593             postalAddressKey = getCNPostalAddressSubLocalityKey();
594             break;
595
596         case WebCore::PaymentError::ContactField::Locality:
597             pkContactField = PAL::get_PassKit_PKContactFieldPostalAddress();
598             postalAddressKey = getCNPostalAddressCityKey();
599             break;
600
601         case WebCore::PaymentError::ContactField::PostalCode:
602             pkContactField = PAL::get_PassKit_PKContactFieldPostalAddress();
603             postalAddressKey = getCNPostalAddressPostalCodeKey();
604             break;
605
606         case WebCore::PaymentError::ContactField::SubAdministrativeArea:
607             pkContactField = PAL::get_PassKit_PKContactFieldPostalAddress();
608             postalAddressKey = getCNPostalAddressSubAdministrativeAreaKey();
609             break;
610
611         case WebCore::PaymentError::ContactField::AdministrativeArea:
612             pkContactField = PAL::get_PassKit_PKContactFieldPostalAddress();
613             postalAddressKey = getCNPostalAddressStateKey();
614             break;
615
616         case WebCore::PaymentError::ContactField::Country:
617             pkContactField = PAL::get_PassKit_PKContactFieldPostalAddress();
618             postalAddressKey = getCNPostalAddressCountryKey();
619             break;
620
621         case WebCore::PaymentError::ContactField::CountryCode:
622             pkContactField = PAL::get_PassKit_PKContactFieldPostalAddress();
623             postalAddressKey = getCNPostalAddressISOCountryCodeKey();
624             break;
625         }
626
627         [userInfo setObject:pkContactField forKey:PAL::get_PassKit_PKPaymentErrorContactFieldUserInfoKey()];
628         if (postalAddressKey)
629             [userInfo setObject:postalAddressKey forKey:PAL::get_PassKit_PKPaymentErrorPostalAddressUserInfoKey()];
630     }
631
632     return adoptNS([[NSError alloc] initWithDomain:getPKPaymentErrorDomain() code:toPKPaymentErrorCode(error.code) userInfo:userInfo.get()]);
633 }
634
635 static RetainPtr<NSArray> toNSErrors(const Vector<WebCore::PaymentError>& errors)
636 {
637     auto result = adoptNS([[NSMutableArray alloc] init]);
638
639     for (auto error : errors) {
640         if (auto nsError = toNSError(error))
641             [result addObject:nsError.get()];
642     }
643
644     return result;
645 }
646 #else
647 static PKPaymentAuthorizationStatus toPKPaymentAuthorizationStatus(const std::optional<WebCore::PaymentAuthorizationResult>& result)
648 {
649     if (!result)
650         return PKPaymentAuthorizationStatusSuccess;
651
652     if (result->errors.size() == 1) {
653         auto& error = result->errors[0];
654         switch (error.code) {
655         case WebCore::PaymentError::Code::Unknown:
656         case WebCore::PaymentError::Code::AddressUnserviceable:
657             return PKPaymentAuthorizationStatusFailure;
658
659         case WebCore::PaymentError::Code::BillingContactInvalid:
660             return PKPaymentAuthorizationStatusInvalidBillingPostalAddress;
661
662         case WebCore::PaymentError::Code::ShippingContactInvalid:
663             if (error.contactField && error.contactField == WebCore::PaymentError::ContactField::PostalAddress)
664                 return PKPaymentAuthorizationStatusInvalidShippingPostalAddress;
665
666             return PKPaymentAuthorizationStatusInvalidShippingContact;
667         }
668     }
669
670     switch (result->status) {
671     case WebCore::PaymentAuthorizationStatus::Success:
672         return PKPaymentAuthorizationStatusSuccess;
673     case WebCore::PaymentAuthorizationStatus::Failure:
674         return PKPaymentAuthorizationStatusFailure;
675     case WebCore::PaymentAuthorizationStatus::PINRequired:
676         return PKPaymentAuthorizationStatusPINRequired;
677     case WebCore::PaymentAuthorizationStatus::PINIncorrect:
678         return PKPaymentAuthorizationStatusPINIncorrect;
679     case WebCore::PaymentAuthorizationStatus::PINLockout:
680         return PKPaymentAuthorizationStatusPINLockout;
681     }
682
683     return PKPaymentAuthorizationStatusFailure;
684 }
685 #endif
686
687 void WebPaymentCoordinatorProxy::platformCompletePaymentSession(const std::optional<WebCore::PaymentAuthorizationResult>& result)
688 {
689     ASSERT(m_paymentAuthorizationViewController);
690     ASSERT(m_paymentAuthorizationViewControllerDelegate);
691
692     m_paymentAuthorizationViewControllerDelegate->_didReachFinalState = WebCore::isFinalStateResult(result);
693
694 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300) || (PLATFORM(IOS_FAMILY) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000)
695     auto status = result ? result->status : WebCore::PaymentAuthorizationStatus::Success;
696     auto pkPaymentAuthorizationResult = adoptNS([PAL::allocPKPaymentAuthorizationResultInstance() initWithStatus:toPKPaymentAuthorizationStatus(status) errors:result ? toNSErrors(result->errors).get() : @[ ]]);
697     m_paymentAuthorizationViewControllerDelegate->_paymentAuthorizedCompletion(pkPaymentAuthorizationResult.get());
698 #else
699     m_paymentAuthorizationViewControllerDelegate->_paymentAuthorizedCompletion(toPKPaymentAuthorizationStatus(result));
700 #endif
701     m_paymentAuthorizationViewControllerDelegate->_paymentAuthorizedCompletion = nullptr;
702 }
703
704 void WebPaymentCoordinatorProxy::platformCompleteMerchantValidation(const WebCore::PaymentMerchantSession& paymentMerchantSession)
705 {
706     ASSERT(m_paymentAuthorizationViewController);
707     ASSERT(m_paymentAuthorizationViewControllerDelegate);
708
709     m_paymentAuthorizationViewControllerDelegate->_sessionBlock(paymentMerchantSession.pkPaymentMerchantSession(), nullptr);
710     m_paymentAuthorizationViewControllerDelegate->_sessionBlock = nullptr;
711 }
712
713 void WebPaymentCoordinatorProxy::platformCompleteShippingMethodSelection(const std::optional<WebCore::ShippingMethodUpdate>& update)
714 {
715     ASSERT(m_paymentAuthorizationViewController);
716     ASSERT(m_paymentAuthorizationViewControllerDelegate);
717
718     if (update)
719         m_paymentAuthorizationViewControllerDelegate->_paymentSummaryItems = toPKPaymentSummaryItems(update->newTotalAndLineItems);
720
721 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300) || (PLATFORM(IOS_FAMILY) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000)
722     auto pkShippingMethodUpdate = adoptNS([PAL::allocPKPaymentRequestShippingMethodUpdateInstance() initWithPaymentSummaryItems:m_paymentAuthorizationViewControllerDelegate->_paymentSummaryItems.get()]);
723     m_paymentAuthorizationViewControllerDelegate->_didSelectShippingMethodCompletion(pkShippingMethodUpdate.get());
724 #else
725     m_paymentAuthorizationViewControllerDelegate->_didSelectShippingMethodCompletion(PKPaymentAuthorizationStatusSuccess, m_paymentAuthorizationViewControllerDelegate->_paymentSummaryItems.get());
726 #endif
727     m_paymentAuthorizationViewControllerDelegate->_didSelectShippingMethodCompletion = nullptr;
728 }
729
730 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101300) || (PLATFORM(IOS_FAMILY) && __IPHONE_OS_VERSION_MIN_REQUIRED < 110000)
731 static PKPaymentAuthorizationStatus toPKPaymentAuthorizationStatus(const std::optional<WebCore::ShippingContactUpdate>& update)
732 {
733     if (!update || update->errors.isEmpty())
734         return PKPaymentAuthorizationStatusSuccess;
735
736     if (update->errors.size() == 1) {
737         auto& error = update->errors[0];
738         switch (error.code) {
739         case WebCore::PaymentError::Code::Unknown:
740         case WebCore::PaymentError::Code::AddressUnserviceable:
741             return PKPaymentAuthorizationStatusFailure;
742
743         case WebCore::PaymentError::Code::BillingContactInvalid:
744             return PKPaymentAuthorizationStatusInvalidBillingPostalAddress;
745
746         case WebCore::PaymentError::Code::ShippingContactInvalid:
747             if (error.contactField && error.contactField == WebCore::PaymentError::ContactField::PostalAddress)
748                 return PKPaymentAuthorizationStatusInvalidShippingPostalAddress;
749
750             return PKPaymentAuthorizationStatusInvalidShippingContact;
751         }
752     }
753
754     return PKPaymentAuthorizationStatusFailure;
755 }
756 #endif
757
758 void WebPaymentCoordinatorProxy::platformCompleteShippingContactSelection(const std::optional<WebCore::ShippingContactUpdate>& update)
759 {
760     ASSERT(m_paymentAuthorizationViewController);
761     ASSERT(m_paymentAuthorizationViewControllerDelegate);
762
763     if (update) {
764         m_paymentAuthorizationViewControllerDelegate->_paymentSummaryItems = toPKPaymentSummaryItems(update->newTotalAndLineItems);
765
766         auto shippingMethods = adoptNS([[NSMutableArray alloc] init]);
767         for (auto& shippingMethod : update->newShippingMethods)
768             [shippingMethods addObject:toPKShippingMethod(shippingMethod).get()];
769
770         m_paymentAuthorizationViewControllerDelegate->_shippingMethods = WTFMove(shippingMethods);
771     }
772
773 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300) || (PLATFORM(IOS_FAMILY) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000)
774     auto pkShippingContactUpdate = adoptNS([PAL::allocPKPaymentRequestShippingContactUpdateInstance() initWithErrors:update ? toNSErrors(update->errors).get() : @[ ] paymentSummaryItems:m_paymentAuthorizationViewControllerDelegate->_paymentSummaryItems.get() shippingMethods:m_paymentAuthorizationViewControllerDelegate->_shippingMethods.get()]);
775     m_paymentAuthorizationViewControllerDelegate->_didSelectShippingContactCompletion(pkShippingContactUpdate.get());
776 #else
777     m_paymentAuthorizationViewControllerDelegate->_didSelectShippingContactCompletion(toPKPaymentAuthorizationStatus(update), m_paymentAuthorizationViewControllerDelegate->_shippingMethods.get(), m_paymentAuthorizationViewControllerDelegate->_paymentSummaryItems.get());
778 #endif
779     m_paymentAuthorizationViewControllerDelegate->_didSelectShippingContactCompletion = nullptr;
780 }
781
782 void WebPaymentCoordinatorProxy::platformCompletePaymentMethodSelection(const std::optional<WebCore::PaymentMethodUpdate>& update)
783 {
784     ASSERT(m_paymentAuthorizationViewController);
785     ASSERT(m_paymentAuthorizationViewControllerDelegate);
786
787     if (update)
788         m_paymentAuthorizationViewControllerDelegate->_paymentSummaryItems = toPKPaymentSummaryItems(update->newTotalAndLineItems);
789
790 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300) || (PLATFORM(IOS_FAMILY) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000)
791     auto pkPaymentMethodUpdate = adoptNS([PAL::allocPKPaymentRequestPaymentMethodUpdateInstance() initWithPaymentSummaryItems:m_paymentAuthorizationViewControllerDelegate->_paymentSummaryItems.get()]);
792     m_paymentAuthorizationViewControllerDelegate->_didSelectPaymentMethodCompletion(pkPaymentMethodUpdate.get());
793 #else
794     m_paymentAuthorizationViewControllerDelegate->_didSelectPaymentMethodCompletion(m_paymentAuthorizationViewControllerDelegate->_paymentSummaryItems.get());
795 #endif
796     m_paymentAuthorizationViewControllerDelegate->_didSelectPaymentMethodCompletion = nullptr;
797 }
798
799 Vector<String> WebPaymentCoordinatorProxy::platformAvailablePaymentNetworks()
800 {
801     NSArray<PKPaymentNetwork> *availableNetworks = [PAL::getPKPaymentRequestClass() availableNetworks];
802     Vector<String> result;
803     result.reserveInitialCapacity(availableNetworks.count);
804     for (PKPaymentNetwork network in availableNetworks)
805         result.uncheckedAppend(network);
806     return result;
807 }
808
809 } // namespace WebKit
810
811 #endif // ENABLE(APPLE_PAY)