WKPaymentAuthorizationDelegate.mm   [plain text]

/*
* Copyright (C) 2016-2019 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/

#import "config.h"
#import "WKPaymentAuthorizationDelegate.h"

#if USE(PASSKIT) && ENABLE(APPLE_PAY)

#import <WebCore/Payment.h>
#import <WebCore/PaymentMethod.h>
#import <WebCore/PaymentSessionError.h>

@implementation WKPaymentAuthorizationDelegate {
BOOL _didReachFinalState;
RetainPtr<NSArray<PKPaymentSummaryItem *>> _summaryItems;
RetainPtr<NSArray<PKShippingMethod *>> _shippingMethods;
RetainPtr<NSError> _sessionError;
WeakPtr<WebKit::PaymentAuthorizationPresenter> _presenter;
WebKit::DidAuthorizePaymentCompletion _didAuthorizePaymentCompletion;
WebKit::DidRequestMerchantSessionCompletion _didRequestMerchantSessionCompletion;
WebKit::DidSelectPaymentMethodCompletion _didSelectPaymentMethodCompletion;
WebKit::DidSelectShippingContactCompletion _didSelectShippingContactCompletion;
WebKit::DidSelectShippingMethodCompletion _didSelectShippingMethodCompletion;
}

- (NSArray<PKPaymentSummaryItem *> *)summaryItems
{
return _summaryItems.get();
}

- (NSArray<PKShippingMethod *> *)shippingMethods
{
return _shippingMethods.get();
}

- (void)completeMerchantValidation:(PKPaymentMerchantSession *)session error:(NSError *)error
{
std::exchange(_didRequestMerchantSessionCompletion, nil)(session, error);
}

- (void)completePaymentMethodSelection:(PKPaymentRequestPaymentMethodUpdate *)paymentMethodUpdate
{
PKPaymentRequestPaymentMethodUpdate *update = paymentMethodUpdate ?: [[PAL::allocPKPaymentRequestPaymentMethodUpdateInstance() initWithPaymentSummaryItems:_summaryItems.get()] autorelease];
_summaryItems = adoptNS([update.paymentSummaryItems copy]);
std::exchange(_didSelectPaymentMethodCompletion, nil)(update);
}

- (void)completePaymentSession:(PKPaymentAuthorizationStatus)status errors:(NSArray<NSError *> *)errors didReachFinalState:(BOOL)didReachFinalState
{
_didReachFinalState = didReachFinalState;
auto result = adoptNS([PAL::allocPKPaymentAuthorizationResultInstance() initWithStatus:status errors:errors]);
std::exchange(_didAuthorizePaymentCompletion, nil)(result.get());
}
- (void)completeShippingContactSelection:(PKPaymentRequestShippingContactUpdate *)shippingContactUpdate
{
PKPaymentRequestShippingContactUpdate *update = shippingContactUpdate ?: [[PAL::allocPKPaymentRequestShippingContactUpdateInstance() initWithErrors:@[] paymentSummaryItems:_summaryItems.get() shippingMethods:_shippingMethods.get()] autorelease];
_summaryItems = adoptNS([update.paymentSummaryItems copy]);
_shippingMethods = adoptNS([update.shippingMethods copy]);
std::exchange(_didSelectShippingContactCompletion, nil)(update);
}

- (void)completeShippingMethodSelection:(PKPaymentRequestShippingMethodUpdate *)shippingMethodUpdate
{
PKPaymentRequestShippingMethodUpdate *update = shippingMethodUpdate ?: [[PAL::allocPKPaymentRequestShippingMethodUpdateInstance() initWithPaymentSummaryItems:_summaryItems.get()] autorelease];
_summaryItems = adoptNS([update.paymentSummaryItems copy]);
std::exchange(_didSelectShippingMethodCompletion, nil)(update);
}

- (void)invalidate
{
if (_didAuthorizePaymentCompletion)
[self completePaymentSession:PKPaymentAuthorizationStatusFailure errors:@[] didReachFinalState:YES];
}

@end

@implementation WKPaymentAuthorizationDelegate (Protected)

- (instancetype)_initWithRequest:(PKPaymentRequest *)request presenter:(WebKit::PaymentAuthorizationPresenter&)presenter
{
if (!(self = [super init]))
return nil;

_presenter = makeWeakPtr(presenter);
_request = request;
_shippingMethods = request.shippingMethods;
_summaryItems = request.paymentSummaryItems;
return self;
}

- (void)_didAuthorizePayment:(PKPayment *)payment completion:(WebKit::DidAuthorizePaymentCompletion::BlockType)completion
{
ASSERT(!_didAuthorizePaymentCompletion);
_didAuthorizePaymentCompletion = completion;

auto presenter = _presenter.get();
if (!presenter)
return [self completePaymentSession:PKPaymentAuthorizationStatusFailure errors:@[ ] didReachFinalState:YES];

presenter->client().presenterDidAuthorizePayment(*presenter, WebCore::Payment(payment));
}

- (void)_didFinish
{
if (auto presenter = _presenter.get())
presenter->client().presenterDidFinish(*presenter, { std::exchange(_sessionError, nil) }, _didReachFinalState);
}

- (void)_didRequestMerchantSession:(WebKit::DidRequestMerchantSessionCompletion::BlockType)completion
{
ASSERT(!_didRequestMerchantSessionCompletion);
_didRequestMerchantSessionCompletion = completion;

[self _getPaymentServicesMerchantURL:^(NSURL *merchantURL, NSError *error) {
if (error)
LOG_ERROR("PKCanMakePaymentsWithMerchantIdentifierAndDomain error %@", error);

dispatch_async(dispatch_get_main_queue(), ^{
ASSERT(_didRequestMerchantSessionCompletion);

auto presenter = _presenter.get();
if (!presenter) {
_didRequestMerchantSessionCompletion(nil, nil);
return;
}

presenter->client().presenterWillValidateMerchant(*presenter, merchantURL);
});
}];
}

- (void)_didSelectPaymentMethod:(PKPaymentMethod *)paymentMethod completion:(WebKit::DidSelectPaymentMethodCompletion::BlockType)completion
{
ASSERT(!_didSelectPaymentMethodCompletion);
_didSelectPaymentMethodCompletion = completion;

auto presenter = _presenter.get();
if (!presenter)
return [self completePaymentMethodSelection:nil];

presenter->client().presenterDidSelectPaymentMethod(*presenter, WebCore::PaymentMethod(paymentMethod));
}

- (void)_didSelectShippingContact:(PKContact *)contact completion:(WebKit::DidSelectShippingContactCompletion::BlockType)completion
{
ASSERT(!_didSelectShippingContactCompletion);
_didSelectShippingContactCompletion = completion;

auto presenter = _presenter.get();
if (!presenter)
return [self completeShippingContactSelection:nil];

presenter->client().presenterDidSelectShippingContact(*presenter, WebCore::PaymentContact(contact));
}

static WebCore::ApplePaySessionPaymentRequest::ShippingMethod toShippingMethod(PKShippingMethod *shippingMethod)
{
ASSERT(shippingMethod);

WebCore::ApplePaySessionPaymentRequest::ShippingMethod result;
result.amount = shippingMethod.amount.stringValue;
result.detail = shippingMethod.detail;
result.identifier = shippingMethod.identifier;
result.label = shippingMethod.label;

return result;
}

- (void)_didSelectShippingMethod:(PKShippingMethod *)shippingMethod completion:(WebKit::DidSelectShippingMethodCompletion::BlockType)completion
{
ASSERT(!_didSelectShippingMethodCompletion);
_didSelectShippingMethodCompletion = completion;

auto presenter = _presenter.get();
if (!presenter)
return [self completeShippingMethodSelection:nil];

presenter->client().presenterDidSelectShippingMethod(*presenter, toShippingMethod(shippingMethod));
}

- (void) NO_RETURN_DUE_TO_ASSERT _getPaymentServicesMerchantURL:(void(^)(NSURL *, NSError *))completion
{
ASSERT_NOT_REACHED();
completion(nil, nil);
}

- (void)_willFinishWithError:(NSError *)error
{
if (![error.domain isEqualToString:PAL::get_PassKit_PKPassKitErrorDomain()])
return;

_sessionError = error;
}

@end

#endif // USE(PASSKIT) && ENABLE(APPLE_PAY)