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 @@ ...@@ -27,12 +27,13 @@
#import "CheckoutViewController.h" #import "CheckoutViewController.h"
#import "GetCompletionStatusOperation.h" #import "GetCompletionStatusOperation.h"
#import "SummaryItemsTableViewCell.h" #import "SummaryItemsTableViewCell.h"
#import "ProductListViewController.h"
@import Buy; @import Buy;
@import PassKit; @import PassKit;
@import SafariServices; @import SafariServices;
NSString * const CheckoutCallbackNotification = @"CheckoutCallbackNotification"; NSString * const CheckoutCallbackNotification = @"CheckoutCallbackNotification";
NSString * const MerchantId = @"";
@interface CheckoutViewController () <GetCompletionStatusOperationDelegate, SFSafariViewControllerDelegate, PKPaymentAuthorizationViewControllerDelegate> @interface CheckoutViewController () <GetCompletionStatusOperationDelegate, SFSafariViewControllerDelegate, PKPaymentAuthorizationViewControllerDelegate>
...@@ -291,7 +292,7 @@ NSString * const MerchantId = @""; ...@@ -291,7 +292,7 @@ NSString * const MerchantId = @"";
{ {
PKPaymentRequest *paymentRequest = [[PKPaymentRequest alloc] init]; PKPaymentRequest *paymentRequest = [[PKPaymentRequest alloc] init];
[paymentRequest setMerchantIdentifier:MerchantId]; [paymentRequest setMerchantIdentifier:MERCHANT_ID];
[paymentRequest setRequiredBillingAddressFields:PKAddressFieldAll]; [paymentRequest setRequiredBillingAddressFields:PKAddressFieldAll];
[paymentRequest setRequiredShippingAddressFields:self.checkout.requiresShipping ? PKAddressFieldAll : PKAddressFieldEmail|PKAddressFieldPhone]; [paymentRequest setRequiredShippingAddressFields:self.checkout.requiresShipping ? PKAddressFieldAll : PKAddressFieldEmail|PKAddressFieldPhone];
[paymentRequest setSupportedNetworks:@[PKPaymentNetworkVisa, PKPaymentNetworkMasterCard]]; [paymentRequest setSupportedNetworks:@[PKPaymentNetworkVisa, PKPaymentNetworkMasterCard]];
......
...@@ -27,6 +27,10 @@ ...@@ -27,6 +27,10 @@
@import UIKit; @import UIKit;
@import Buy; @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 @interface ProductListViewController : UITableViewController
- (instancetype)initWithClient:(BUYClient *)client collection:(BUYCollection*)collection; - (instancetype)initWithClient:(BUYClient *)client collection:(BUYCollection*)collection;
......
...@@ -30,10 +30,6 @@ ...@@ -30,10 +30,6 @@
#import "ProductViewControllerThemeStyleTableViewCell.h" #import "ProductViewControllerThemeStyleTableViewCell.h"
#import "ProductViewControllerThemeTintColorTableViewCell.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> @interface ProductListViewController () <UIViewControllerPreviewingDelegate>
@property (nonatomic, strong) BUYClient *client; @property (nonatomic, strong) BUYClient *client;
......
...@@ -90,8 +90,6 @@ static NSString *const kBUYClientPathCollectionPublications = @"collection_publi ...@@ -90,8 +90,6 @@ static NSString *const kBUYClientPathCollectionPublications = @"collection_publi
self.queue = dispatch_get_main_queue(); self.queue = dispatch_get_main_queue();
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration]; NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
// ensure requests are sent serially
config.HTTPMaximumConnectionsPerHost = 1;
config.HTTPAdditionalHeaders = @{@"User-Agent": [NSString stringWithFormat:@"Mobile Buy SDK iOS/%@", BUYVersionString]}; config.HTTPAdditionalHeaders = @{@"User-Agent": [NSString stringWithFormat:@"Mobile Buy SDK iOS/%@", BUYVersionString]};
self.session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil]; self.session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
......
...@@ -89,12 +89,6 @@ NSString * BUYURLKey = @"url"; ...@@ -89,12 +89,6 @@ NSString * BUYURLKey = @"url";
if (error == nil) { if (error == nil) {
self.shop = shop; self.shop = shop;
} }
else {
if ([self.delegate respondsToSelector:@selector(controllerFailedToStartApplePayProcess:)]) {
[self.delegate controllerFailedToStartApplePayProcess:self];
}
}
self.isLoadingShop = NO; self.isLoadingShop = NO;
...@@ -145,22 +139,65 @@ NSString * BUYURLKey = @"url"; ...@@ -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 // Default to the failure state, since cancelling a payment would not update the state and thus appear as a success
self.paymentAuthorizationStatus = PKPaymentAuthorizationStatusFailure; self.paymentAuthorizationStatus = PKPaymentAuthorizationStatusFailure;
if (self.shop == nil && self.isLoadingShop == NO) { /**
// since requests are sent serially, this will return before the checkout is created * To perform an Apple Pay checkout, we need both the BUYShop object, and a BUYCheckout
[self loadShopWithCallback:nil]; * 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 handleCheckout:checkout completion:^(BUYCheckout *checkout, NSError *error) {
if (error) {
checkoutError = error;
if ([self.delegate respondsToSelector:@selector(controller:failedToCreateCheckout:)]) {
[self.delegate controller:self failedToCreateCheckout:error];
}
}
else {
self.checkout = checkout; 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 (error == nil) {
if ([self.delegate respondsToSelector:@selector(controllerWillCheckoutViaApplePay:)]) { if ([self.delegate respondsToSelector:@selector(controllerWillCheckoutViaApplePay:)]) {
[self.delegate controllerWillCheckoutViaApplePay:self]; [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 - (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