Commit 0f998783 by David Muzi

Merge pull request #90 from Shopify/bugfix/applepay-race-condition

Prevent race condition during Apple Pay Checkout
parents 76b37ee0 61d03c98
......@@ -27,12 +27,13 @@
#import "CheckoutViewController.h"
#import "GetCompletionStatusOperation.h"
#import "SummaryItemsTableViewCell.h"
#import "ProductListViewController.h"
@import Buy;
@import PassKit;
@import SafariServices;
NSString * const CheckoutCallbackNotification = @"CheckoutCallbackNotification";
NSString * const MerchantId = @"";
@interface CheckoutViewController () <GetCompletionStatusOperationDelegate, SFSafariViewControllerDelegate, PKPaymentAuthorizationViewControllerDelegate>
......@@ -291,7 +292,7 @@ NSString * const MerchantId = @"";
{
PKPaymentRequest *paymentRequest = [[PKPaymentRequest alloc] init];
[paymentRequest setMerchantIdentifier:MerchantId];
[paymentRequest setMerchantIdentifier:MERCHANT_ID];
[paymentRequest setRequiredBillingAddressFields:PKAddressFieldAll];
[paymentRequest setRequiredShippingAddressFields:self.checkout.requiresShipping ? PKAddressFieldAll : PKAddressFieldEmail|PKAddressFieldPhone];
[paymentRequest setSupportedNetworks:@[PKPaymentNetworkVisa, PKPaymentNetworkMasterCard]];
......
......@@ -27,6 +27,10 @@
@import UIKit;
@import Buy;
#warning - Enter your merchant ID
// Adding a merchant ID will show Apple Pay in the BUYProductViewController (on supported devices)
#define MERCHANT_ID @""
@interface ProductListViewController : UITableViewController
- (instancetype)initWithClient:(BUYClient *)client collection:(BUYCollection*)collection;
......
......@@ -30,10 +30,6 @@
#import "ProductViewControllerThemeStyleTableViewCell.h"
#import "ProductViewControllerThemeTintColorTableViewCell.h"
#warning - Enter your merchant ID
// Adding a merchant ID will show Apple Pay in the BUYProductViewController (on supported devices)
#define MERCHANT_ID @""
@interface ProductListViewController () <UIViewControllerPreviewingDelegate>
@property (nonatomic, strong) BUYClient *client;
......
......@@ -90,8 +90,6 @@ static NSString *const kBUYClientPathCollectionPublications = @"collection_publi
self.queue = dispatch_get_main_queue();
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
// ensure requests are sent serially
config.HTTPMaximumConnectionsPerHost = 1;
config.HTTPAdditionalHeaders = @{@"User-Agent": [NSString stringWithFormat:@"Mobile Buy SDK iOS/%@", BUYVersionString]};
self.session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
......
......@@ -89,12 +89,6 @@ NSString * BUYURLKey = @"url";
if (error == nil) {
self.shop = shop;
}
else {
if ([self.delegate respondsToSelector:@selector(controllerFailedToStartApplePayProcess:)]) {
[self.delegate controllerFailedToStartApplePayProcess:self];
}
}
self.isLoadingShop = NO;
......@@ -145,22 +139,65 @@ NSString * BUYURLKey = @"url";
// Default to the failure state, since cancelling a payment would not update the state and thus appear as a success
self.paymentAuthorizationStatus = PKPaymentAuthorizationStatusFailure;
if (self.shop == nil && self.isLoadingShop == NO) {
// since requests are sent serially, this will return before the checkout is created
[self loadShopWithCallback:nil];
/**
* To perform an Apple Pay checkout, we need both the BUYShop object, and a BUYCheckout
* We will download both in parallel, and continue with the checkout when they both succeed
*/
dispatch_group_t group = dispatch_group_create();
__block NSError *checkoutError = nil;
// download the shop
if (self.shop != nil) {
dispatch_group_enter(group);
[self loadShopWithCallback:^(BOOL success, NSError *error) {
if (error) {
checkoutError = error;
if ([self.delegate respondsToSelector:@selector(controllerFailedToStartApplePayProcess:)]) {
[self.delegate controllerFailedToStartApplePayProcess:self];
}
}
dispatch_group_leave(group);
}];
}
// create the checkout on Shopify
dispatch_group_enter(group);
[self handleCheckout:checkout completion:^(BUYCheckout *checkout, NSError *error) {
self.checkout = checkout;
if (error == nil) {
if (error) {
checkoutError = error;
if ([self.delegate respondsToSelector:@selector(controller:failedToCreateCheckout:)]) {
[self.delegate controller:self failedToCreateCheckout:error];
}
}
else {
self.checkout = checkout;
}
dispatch_group_leave(group);
}];
// When we have both the shop and checkout, we can request the payment with Apple Pay
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
if (self.checkout && self.shop) {
if ([self.delegate respondsToSelector:@selector(controllerWillCheckoutViaApplePay:)]) {
[self.delegate controllerWillCheckoutViaApplePay:self];
}
self.applePayHelper = [[BUYApplePayHelpers alloc] initWithClient:self.client checkout:checkout shop:self.shop];
self.applePayHelper = [[BUYApplePayHelpers alloc] initWithClient:self.client checkout:self.checkout shop:self.shop];
[self requestPayment];
}
[self handleCheckoutCompletion:checkout error:error];
}];
else {
if ([self.delegate respondsToSelector:@selector(controller:failedToCreateCheckout:)]) {
[self.delegate controller:self failedToCreateCheckout:checkoutError];
}
}
});
}
- (void)startWebCheckout:(BUYCheckout *)checkout
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment