Commit a57f4973 by Dima Bart

Merge pull request #174 from Shopify/feature/convenience-api

Add convenience auto-polling API.
parents 90d5caa7 62319c1e
......@@ -47,7 +47,7 @@ NSString * const BUYFakeCustomerToken = @"dsfasdgafdg";
@implementation BUYClient_Test
- (void)startTask:(BUYRequestOperation *)task
- (void)startOperation:(BUYOperation *)operation
{
// Do nothing
}
......@@ -128,7 +128,7 @@ NSString * const BUYFakeCustomerToken = @"dsfasdgafdg";
{
BUYCheckout *checkout = [[BUYCheckout alloc] initWithModelManager:self.client.modelManager JSONDictionary:@{@"token": @"abcdef", @"payment_due": @0}];
BUYRequestOperation *task = [self.client completeCheckout:checkout paymentToken:nil completion:^(BUYCheckout *checkout, NSError *error) {}];
BUYOperation *task = [self.client completeCheckout:checkout paymentToken:nil completion:^(BUYCheckout *checkout, NSError *error) {}];
XCTAssertNotNil(task);
}
......@@ -184,19 +184,6 @@ NSString * const BUYFakeCustomerToken = @"dsfasdgafdg";
XCTAssertEqual(BUYStatusComplete, status);
}
- (void)testCheckoutWithApplePayToken
{
id<BUYPaymentToken> token = [[BUYApplePayToken alloc] initWithPaymentToken:[BUYApplePayTestToken validToken]];
XCTAssertThrows(
[self.client completeCheckout:[BUYCheckout new] paymentToken:token completion:^(BUYCheckout *checkout, NSError *error) {}]
);
BUYCheckout *checkout = [[BUYCheckout alloc] initWithModelManager:self.client.modelManager JSONDictionary:@{@"token": @"abcdef", @"payment_due": @0}];
XCTAssertNoThrow(
[self.client completeCheckout:checkout paymentToken:nil completion:^(BUYCheckout *checkout, NSError *error) {}]
);
}
- (void)testQueryItemsConversion
{
NSDictionary *dictionary = @{@"collection_id" : @"1", @"limit" : @"25", @"page" : @"1", @"sort_by" : @"collection-default"};
......
......@@ -31,6 +31,7 @@
#import "BUYTestConstants.h"
#import "BUYCheckout.h"
#import "BUYClientTestBase.h"
#import "BUYClient+Routing.h"
#import <OHHTTPStubs/OHHTTPStubs.h>
#import "OHHTTPStubsResponse+Helpers.h"
......@@ -167,33 +168,20 @@
} withStubResponse:^OHHTTPStubsResponse * _Nonnull(NSURLRequest * _Nonnull request) {
return [OHHTTPStubsResponse responseWithKey:@"testCheckoutFlowUsingCreditCard_2"];
}];
__block BUYStatus shippingStatus = BUYStatusUnknown;
do {
NSLog(@"Fetching shipping rates...");
XCTestExpectation *expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)];
[self.client getShippingRatesForCheckout:_checkout completion:^(NSArray *returnedShippingRates, BUYStatus status, NSError *error) {
XCTAssertNil(error);
shippingStatus = status;
if (shippingStatus == BUYStatusComplete) {
XCTAssertNotNil(returnedShippingRates);
_shippingRates = returnedShippingRates;
}
[expectation fulfill];
}];
XCTestExpectation *expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)];
[self.client getShippingRatesForCheckout:_checkout completion:^(NSArray *shippingRates, BUYStatus status, NSError *error) {
XCTAssertNil(error);
XCTAssertEqual(status, BUYStatusComplete);
[self waitForExpectationsWithTimeout:10 handler:^(NSError *error) {
XCTAssertNil(error);
}];
XCTAssertNotNil(shippingRates);
XCTAssertTrue([shippingRates count] > 0);
if (shippingStatus == BUYStatusProcessing) {
[NSThread sleepForTimeInterval:0.5f];
}
} while (shippingStatus == BUYStatusProcessing);
_shippingRates = shippingRates;
[expectation fulfill];
}];
XCTAssertTrue([_shippingRates count] > 0);
XCTAssertEqual(shippingStatus, BUYStatusComplete);
[self waitForExpectationsWithTimeout:10.0 handler:nil];
}
- (void)updateCheckout
......@@ -232,12 +220,10 @@
__block id<BUYPaymentToken> token = nil;
XCTestExpectation *expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)];
[self.client storeCreditCard:creditCard checkout:_checkout completion:^(BUYCheckout *returnedCheckout, id<BUYPaymentToken> paymentToken, NSError *error) {
[self.client storeCreditCard:creditCard checkout:_checkout completion:^(id<BUYPaymentToken> paymentToken, NSError *error) {
XCTAssertNil(error);
XCTAssertNotNil(paymentToken);
XCTAssertNotNil(returnedCheckout);
_checkout = returnedCheckout;
token = paymentToken;
[expectation fulfill];
}];
......@@ -265,75 +251,30 @@
[OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest * _Nonnull request) {
return [self shouldUseMocks];
} withStubResponse:^OHHTTPStubsResponse * _Nonnull(NSURLRequest * _Nonnull request) {
return [OHHTTPStubsResponse responseWithKey:@"testCheckoutFlowUsingCreditCard_5"];
NSString *path = request.URL.absoluteString.lastPathComponent;
if ([path isEqualToString:[self.client urlForCheckoutsCompletionWithToken:_checkout.token].lastPathComponent]) {
return [OHHTTPStubsResponse responseWithKey:@"testCheckoutFlowUsingCreditCard_5"];
} else if ([path isEqualToString:[self.client urlForCheckoutsProcessingWithToken:_checkout.token].lastPathComponent]) {
return [OHHTTPStubsResponse responseWithKey:@"testCheckoutFlowUsingCreditCard_14"];
} else if ([path isEqualToString:[self.client urlForCheckoutsWithToken:_checkout.token].lastPathComponent]) {
return [OHHTTPStubsResponse responseWithKey:@"testCheckoutFlowUsingCreditCard_15"];
}
return [OHHTTPStubsResponse responseWithData:[NSData new] statusCode:500 headers:nil];;
}];
XCTestExpectation *expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)];
[self.client completeCheckout:_checkout paymentToken:paymentToken completion:^(BUYCheckout *returnedCheckout, NSError *error) {
XCTAssertNil(error);
XCTAssertNotNil(returnedCheckout);
XCTAssertNotNil(returnedCheckout.order);
XCTAssertNotNil(returnedCheckout.order.identifier);
XCTAssertNotNil(returnedCheckout.order.statusURL);
XCTAssertNotNil(returnedCheckout.order.name);
_checkout = returnedCheckout;
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:10 handler:^(NSError *error) {
XCTAssertNil(error);
}];
}
- (void)pollUntilCheckoutIsComplete
{
[OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest * _Nonnull request) {
return [self shouldUseMocks];
} withStubResponse:^OHHTTPStubsResponse * _Nonnull(NSURLRequest * _Nonnull request) {
return [OHHTTPStubsResponse responseWithKey:@"testCheckoutFlowUsingCreditCard_14"];
}];
__block BUYStatus checkoutStatus = BUYStatusUnknown;
__block NSError *checkoutError = nil;
while (_checkout.token && checkoutStatus != BUYStatusFailed && checkoutStatus != BUYStatusComplete) {
NSLog(@"Checking completion status...");
XCTestExpectation *expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)];
[self.client getCompletionStatusOfCheckout:_checkout completion:^(BUYStatus status, NSError *error) {
XCTAssertNil(error);
checkoutError = error;
checkoutStatus = status;
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:10 handler:^(NSError *error) {
XCTAssertNil(error);
}];
if (checkoutStatus != BUYStatusComplete) {
[NSThread sleepForTimeInterval:0.5f];
}
}
XCTAssertNil(checkoutError);
XCTAssertEqual(checkoutStatus, BUYStatusComplete);
}
- (void)verifyCompletedCheckout
{
XCTAssertNil(_checkout.order.identifier);
[OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest * _Nonnull request) {
return [self shouldUseMocks];
} withStubResponse:^OHHTTPStubsResponse * _Nonnull(NSURLRequest * _Nonnull request) {
return [OHHTTPStubsResponse responseWithKey:@"testCheckoutFlowUsingCreditCard_15"];
}];
XCTestExpectation *expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)];
[self.client getCheckout:_checkout completion:^(BUYCheckout *returnedCheckout, NSError *error) {
XCTAssertNil(error);
XCTAssertNotNil(returnedCheckout);
_checkout = returnedCheckout;
XCTAssertNotNil(_checkout.order.identifier);
XCTAssertNotNil(_checkout.order.statusURL);
XCTAssertNotNil(_checkout.order.name);
[expectation fulfill];
[self confirmCreditCard];
......@@ -994,9 +935,6 @@
[self updateCheckout];
[self completeCheckoutWithToken:[self addCreditCardToCheckout]];
[self pollUntilCheckoutIsComplete];
[self verifyCompletedCheckout];
}
- (void)testCheckoutWithAPartialAddress
......@@ -1042,9 +980,6 @@
//We use a credit card here because we're not generating apple pay tokens in the tests
id<BUYPaymentToken> token = [self addCreditCardToCheckout];
[self completeCheckoutWithToken:token];
[self pollUntilCheckoutIsComplete];
[self verifyCompletedCheckout];
}
- (void)testCheckoutCreationWithApplicableDiscount
......
......@@ -49,11 +49,11 @@
XCTAssertTrue(operation.isReady);
[operation start];
XCTAssertTrue(operation.isExecuting);
XCTAssertTrue(operation.executing);
[operation finishExecution];
XCTAssertTrue(operation.isFinished);
XCTAssertFalse(operation.isCancelled);
XCTAssertFalse(operation.cancelled);
}
- (void)testCancelledExecutionFlow
......@@ -62,10 +62,10 @@
XCTAssertTrue(operation.isReady);
[operation start];
XCTAssertTrue(operation.isExecuting);
XCTAssertTrue(operation.executing);
[operation cancel];
XCTAssertTrue(operation.isCancelled);
XCTAssertTrue(operation.cancelled);
XCTAssertFalse(operation.isFinished);
[operation cancelExecution];
......
......@@ -2,8 +2,26 @@
// BUYOrderTests.m
// Mobile Buy SDK
//
// Created by Gabriel O'Flaherty-Chan on 2016-02-17.
// Copyright © 2016 Shopify Inc. All rights reserved.
// Created by Shopify.
// Copyright (c) 2015 Shopify Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#import <XCTest/XCTest.h>
......
......@@ -2,8 +2,26 @@
// BUYPaymentProviderTests.m
// Mobile Buy SDK
//
// Created by David Muzi on 2015-12-15.
// Copyright © 2015 Shopify Inc. All rights reserved.
// Created by Shopify.
// Copyright (c) 2015 Shopify Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
@import XCTest;
......
......@@ -2,8 +2,26 @@
// TestModel.h
// Mobile Buy SDK
//
// Created by Brent Gulanowski on 2016-04-27.
// Copyright © 2016 Shopify Inc. All rights reserved.
// Created by Shopify.
// Copyright (c) 2015 Shopify Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#import <Foundation/Foundation.h>
......
......@@ -2,8 +2,26 @@
// TestModel.m
// Mobile Buy SDK
//
// Created by Brent Gulanowski on 2016-04-27.
// Copyright © 2016 Shopify Inc. All rights reserved.
// Created by Shopify.
// Copyright (c) 2015 Shopify Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#import "TestModel.h"
......
......@@ -359,6 +359,10 @@
9A0B0C831CEB981C0037D68F /* BUYClient+RoutingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A0B0C821CEB981C0037D68F /* BUYClient+RoutingTests.m */; };
9A0B0CA21CECC1BC0037D68F /* BUYClient+Checkout.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A0B0C711CEB52B90037D68F /* BUYClient+Checkout.m */; };
9A0B0CA31CECC1BD0037D68F /* BUYClient+Checkout.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A0B0C711CEB52B90037D68F /* BUYClient+Checkout.m */; };
9A0B0CA71CED0A860037D68F /* BUYCheckoutOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A0B0CA51CED0A860037D68F /* BUYCheckoutOperation.h */; };
9A0B0CA81CED0A860037D68F /* BUYCheckoutOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A0B0CA51CED0A860037D68F /* BUYCheckoutOperation.h */; };
9A0B0CA91CED0A860037D68F /* BUYCheckoutOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A0B0CA61CED0A860037D68F /* BUYCheckoutOperation.m */; };
9A0B0CAA1CED0A860037D68F /* BUYCheckoutOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A0B0CA61CED0A860037D68F /* BUYCheckoutOperation.m */; };
9A102D1B1CDD1F960026CC43 /* BUYErrorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A102D1A1CDD1F960026CC43 /* BUYErrorTests.m */; };
9A102D1E1CDD25980026CC43 /* BUYOptionValueTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A102D1D1CDD25980026CC43 /* BUYOptionValueTests.m */; };
9A47CEFD1CE39F6000A6D5BA /* BUYCreditCardToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A47CEFB1CE39F5B00A6D5BA /* BUYCreditCardToken.m */; };
......@@ -652,6 +656,8 @@
9A0B0C761CEB5BBD0037D68F /* BUYAuthenticatedResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BUYAuthenticatedResponse.h; sourceTree = "<group>"; };
9A0B0C771CEB5BBD0037D68F /* BUYAuthenticatedResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BUYAuthenticatedResponse.m; sourceTree = "<group>"; };
9A0B0C821CEB981C0037D68F /* BUYClient+RoutingTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "BUYClient+RoutingTests.m"; sourceTree = "<group>"; };
9A0B0CA51CED0A860037D68F /* BUYCheckoutOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BUYCheckoutOperation.h; sourceTree = "<group>"; };
9A0B0CA61CED0A860037D68F /* BUYCheckoutOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BUYCheckoutOperation.m; sourceTree = "<group>"; };
9A102D1A1CDD1F960026CC43 /* BUYErrorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BUYErrorTests.m; sourceTree = "<group>"; };
9A102D1D1CDD25980026CC43 /* BUYOptionValueTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BUYOptionValueTests.m; sourceTree = "<group>"; };
9A47CEF81CE39EC200A6D5BA /* BUYPaymentToken.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BUYPaymentToken.h; sourceTree = "<group>"; };
......@@ -1012,6 +1018,15 @@
name = "Operation Tests";
sourceTree = "<group>";
};
9A0B0CAB1CEDDBD80037D68F /* Aggregate */ = {
isa = PBXGroup;
children = (
9A0B0CA51CED0A860037D68F /* BUYCheckoutOperation.h */,
9A0B0CA61CED0A860037D68F /* BUYCheckoutOperation.m */,
);
name = Aggregate;
sourceTree = "<group>";
};
9A102D1C1CDD257D0026CC43 /* Models Tests */ = {
isa = PBXGroup;
children = (
......@@ -1059,6 +1074,7 @@
9A585C051CE6440B001F20F0 /* BUYOperation.m */,
9A585C111CE65811001F20F0 /* BUYRequestOperation.h */,
9A585C121CE65811001F20F0 /* BUYRequestOperation.m */,
9A0B0CAB1CEDDBD80037D68F /* Aggregate */,
);
path = Operations;
sourceTree = "<group>";
......@@ -1208,6 +1224,7 @@
84DD12A91CC63FE600A2442D /* _BUYOrder.h in Headers */,
84DD129F1CC63FE600A2442D /* _BUYCustomer.h in Headers */,
9A0B0C671CEA703E0037D68F /* BUYClient+Routing.h in Headers */,
9A0B0CA81CED0A860037D68F /* BUYCheckoutOperation.h in Headers */,
84DD12991CC63FE600A2442D /* _BUYCart.h in Headers */,
84DD129D1CC63FE600A2442D /* _BUYCollection.h in Headers */,
9A0B0C731CEB52B90037D68F /* BUYClient+Checkout.h in Headers */,
......@@ -1370,6 +1387,7 @@
84D9154B1CC03F1600D334FB /* BUYManagedObject.h in Headers */,
84DD12CD1CC6401400A2442D /* BUYCustomer.h in Headers */,
BE9A645D1B503CE30033E558 /* BUYObject.h in Headers */,
9A0B0CA71CED0A860037D68F /* BUYCheckoutOperation.h in Headers */,
BE9A646E1B503D1E0033E558 /* BUYRuntime.h in Headers */,
BEB74A901B55A3D00005A300 /* BUYCollection.h in Headers */,
84D915431CC0359700D334FB /* BUYObserver.h in Headers */,
......@@ -1607,6 +1625,7 @@
9A0B0CA21CECC1BC0037D68F /* BUYClient+Checkout.m in Sources */,
841ADE0E1CB6C942000004B0 /* NSDecimalNumber+BUYAdditions.m in Sources */,
84DD12AE1CC63FE600A2442D /* _BUYProductVariant.m in Sources */,
9A0B0CAA1CED0A860037D68F /* BUYCheckoutOperation.m in Sources */,
84B0A7211CDD261100253EB0 /* BUYSerializable.m in Sources */,
9A47CEFE1CE39F6100A6D5BA /* BUYCreditCardToken.m in Sources */,
9A0B0C7B1CEB5BBD0037D68F /* BUYAuthenticatedResponse.m in Sources */,
......@@ -1748,6 +1767,7 @@
9A0B0CA31CECC1BD0037D68F /* BUYClient+Checkout.m in Sources */,
841ADE0D1CB6C942000004B0 /* NSDecimalNumber+BUYAdditions.m in Sources */,
84DD12961CC63FE600A2442D /* _BUYProductVariant.m in Sources */,
9A0B0CA91CED0A860037D68F /* BUYCheckoutOperation.m in Sources */,
84B0A7201CDD261100253EB0 /* BUYSerializable.m in Sources */,
9A47CEFD1CE39F6000A6D5BA /* BUYCreditCardToken.m in Sources */,
9A0B0C7A1CEB5BBD0037D68F /* BUYAuthenticatedResponse.m in Sources */,
......
......@@ -41,7 +41,7 @@ NS_ASSUME_NONNULL_BEGIN
* @param paymentToken An opaque payment token type that wraps necessary credentials for payment
* @param error Optional NSError
*/
typedef void (^BUYDataCreditCardBlock)(BUYCheckout * _Nullable checkout, id<BUYPaymentToken> _Nullable paymentToken, NSError * _Nullable error);
typedef void (^BUYDataCreditCardBlock)(id<BUYPaymentToken> _Nullable paymentToken, NSError * _Nullable error);
/**
* Return block containing a BUYCheckout and/or an NSError
......@@ -153,9 +153,9 @@ typedef void (^BUYDataGiftCardBlock)(BUYGiftCard * _Nullable giftCard, NSError *
* @param paymentToken Opaque payment token object. May be nil if the total checkout amount is equal to $0.00
* @param block (^BUYDataCheckoutBlock)(BUYCheckout *checkout, NSError *error);
*
* @return The associated BUYRequestOperation
* @return The associated BUYOperation
*/
- (BUYRequestOperation *)completeCheckout:(BUYCheckout *)checkout paymentToken:(_Nullable id<BUYPaymentToken>)paymentToken completion:(BUYDataCheckoutBlock)block;
- (BUYOperation *)completeCheckout:(BUYCheckout *)checkout paymentToken:(_Nullable id<BUYPaymentToken>)paymentToken completion:(BUYDataCheckoutBlock)block;
/**
* Retrieve the status of a BUYCheckout. This checks the status of the current payment processing job for the provided checkout.
......
......@@ -27,6 +27,8 @@
#import "BUYClient+Checkout.h"
#import "BUYClient+Internal.h"
#import "BUYClient+Routing.h"
#import "BUYRequestOperation.h"
#import "BUYCheckoutOperation.h"
#import "BUYAddress.h"
#import "BUYCheckout.h"
#import "BUYGiftCard.h"
......@@ -41,6 +43,8 @@
@implementation BUYClient (Checkout)
#pragma mark - API -
- (void)handleCheckoutResponse:(NSDictionary *)json error:(NSError *)error block:(BUYDataCheckoutBlock)block
{
BUYCheckout *checkout = nil;
......@@ -137,10 +141,15 @@
- (BUYRequestOperation *)getCheckout:(BUYCheckout *)checkout completion:(BUYDataCheckoutBlock)block
{
return [self getCheckout:checkout start:YES completion:block];
}
- (BUYRequestOperation *)getCheckout:(BUYCheckout *)checkout start:(BOOL)start completion:(BUYDataCheckoutBlock)block
{
BUYAssertCheckout(checkout);
NSURL *route = [self urlForCheckoutsWithToken:checkout.token];
return [self getRequestForURL:route completionHandler:^(NSDictionary *json, NSURLResponse *response, NSError *error) {
return [self getRequestForURL:route start:start completionHandler:^(NSDictionary *json, NSURLResponse *response, NSError *error) {
[self handleCheckoutResponse:json error:error block:block];
}];
}
......@@ -155,7 +164,13 @@
}];
}
- (BUYRequestOperation*)completeCheckout:(BUYCheckout *)checkout paymentToken:(id<BUYPaymentToken>)paymentToken completion:(BUYDataCheckoutBlock)block
- (BUYOperation *)completeCheckout:(BUYCheckout *)checkout paymentToken:(id<BUYPaymentToken>)paymentToken completion:(BUYDataCheckoutBlock)block {
BUYCheckoutOperation *operation = [[BUYCheckoutOperation alloc] initWithClient:self checkout:checkout token:paymentToken completion:block];
[self startOperation:operation];
return operation;
}
- (BUYRequestOperation *)beginCheckout:(BUYCheckout *)checkout paymentToken:(id<BUYPaymentToken>)paymentToken completion:(BUYDataCheckoutBlock)block
{
BUYAssertCheckout(checkout);
......@@ -164,7 +179,7 @@
BUYAssert(paymentToken || isFree, @"Failed to complete checkout. Checkout must have a payment token or have a payment value equal to $0.00");
NSURL *route = [self urlForCheckoutsCompletionWithToken:checkout.token];
return [self postRequestForURL:route object:[paymentToken JSONDictionary] completionHandler:^(NSDictionary *json, NSURLResponse *response, NSError *error) {
return [self postRequestForURL:route object:[paymentToken JSONDictionary] start:NO completionHandler:^(NSDictionary *json, NSURLResponse *response, NSError *error) {
[self handleCheckoutResponse:json error:error block:block];
}];
}
......@@ -173,7 +188,7 @@
{
BUYAssertCheckout(checkout);
return [self getCompletionStatusOfCheckoutToken:checkout.token completion:block];
return [self getCompletionStatusOfCheckoutToken:checkout.token start:YES completion:block];
}
- (BUYRequestOperation *)getCompletionStatusOfCheckoutURL:(NSURL *)url completion:(BUYDataStatusBlock)block
......@@ -190,19 +205,19 @@
BUYAssert(token, @"Failed to get completion status of checkout. Checkout URL must have a valid token associated with it.");
return [self getCompletionStatusOfCheckoutToken:token completion:block];
return [self getCompletionStatusOfCheckoutToken:token start:YES completion:block];
}
- (BUYRequestOperation *)getCompletionStatusOfCheckoutToken:(NSString *)token completion:(BUYDataStatusBlock)block
- (BUYRequestOperation *)getCompletionStatusOfCheckoutToken:(NSString *)token start:(BOOL)start completion:(BUYDataStatusBlock)block
{
NSURL *route = [self urlForCheckoutsProcessingWithToken:token];
return [self getRequestForURL:route completionHandler:^(NSDictionary *json, NSURLResponse *response, NSError *error) {
return [self getRequestForURL:route start:start completionHandler:^(NSDictionary *json, NSURLResponse *response, NSError *error) {
NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode];
block([self statusForStatusCode:statusCode error:error], error);
}];
}
#pragma mark - Shipping Rates
#pragma mark - Shipping Rates -
- (BUYRequestOperation *)getShippingRatesForCheckout:(BUYCheckout *)checkout completion:(BUYDataShippingRatesBlock)block
{
......@@ -212,7 +227,7 @@
@"checkout" : @"",
}];
return [self getRequestForURL:route completionHandler:^(NSDictionary *json, NSURLResponse *response, NSError *error) {
BUYRequestOperation *operation = [self getRequestForURL:route start:NO completionHandler:^(NSDictionary *json, NSURLResponse *response, NSError *error) {
NSArray *shippingRates = nil;
if (json && !error) {
shippingRates = [self.modelManager insertShippingRatesWithJSONArray:json[@"shipping_rates"]];
......@@ -221,9 +236,16 @@
NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode];
block(shippingRates, [self statusForStatusCode:statusCode error:error], error);
}];
operation.pollingHandler = ^BOOL(NSDictionary *json, NSHTTPURLResponse *response, NSError *error) {
return response.statusCode == BUYStatusProcessing;
};
[self startOperation:operation];
return operation;
}
#pragma mark - Payments
#pragma mark - Payments -
- (BUYRequestOperation *)storeCreditCard:(BUYCreditCard *)creditCard checkout:(BUYCheckout *)checkout completion:(BUYDataCreditCardBlock)completion
{
......@@ -242,7 +264,7 @@
if (!error) {
token = [[BUYCreditCardToken alloc] initWithPaymentSessionID:json[@"id"]];
}
completion(checkout, token, error);
completion(token, error);
}];
}
......
......@@ -25,22 +25,46 @@
//
#import "BUYClient.h"
#import "BUYClient+Checkout.h"
#import "BUYSerializable.h"
static NSString * const BUYShopifyErrorDomain = @"shopify";
static NSString * const BUYClientVersionString = @"1.3";
static NSString * const BUYClientCustomerAccessToken = @"X-Shopify-Customer-Access-Token";
typedef void (^BUYClientRequestJSONCompletion)(NSDictionary *json, NSURLResponse *response, NSError *error);
@interface BUYClient (Internal)
- (BUYRequestOperation *)getRequestForURL:(NSURL *)url completionHandler:(void (^)(NSDictionary *json, NSURLResponse *response, NSError *error))completionHandler;
- (BUYRequestOperation *)deleteRequestForURL:(NSURL *)url completionHandler:(void (^)(NSDictionary *json, NSURLResponse *response, NSError *error))completionHandler;
- (BUYRequestOperation *)getRequestForURL:(NSURL *)url completionHandler:(BUYClientRequestJSONCompletion)completionHandler;
- (BUYRequestOperation *)deleteRequestForURL:(NSURL *)url completionHandler:(BUYClientRequestJSONCompletion)completionHandler;
- (BUYRequestOperation *)postRequestForURL:(NSURL *)url object:(id <BUYSerializable>)object completionHandler:(BUYClientRequestJSONCompletion)completionHandler;
- (BUYRequestOperation *)putRequestForURL:(NSURL *)url object:(id<BUYSerializable>)object completionHandler:(BUYClientRequestJSONCompletion)completionHandler;
- (BUYRequestOperation *)patchRequestForURL:(NSURL *)url object:(id <BUYSerializable>)object completionHandler:(BUYClientRequestJSONCompletion)completionHandler;
- (BUYRequestOperation *)getRequestForURL:(NSURL *)url start:(BOOL)start completionHandler:(BUYClientRequestJSONCompletion)completionHandler;
- (BUYRequestOperation *)deleteRequestForURL:(NSURL *)url start:(BOOL)start completionHandler:(BUYClientRequestJSONCompletion)completionHandler;
- (BUYRequestOperation *)postRequestForURL:(NSURL *)url object:(id <BUYSerializable>)object completionHandler:(void (^)(NSDictionary *json, NSURLResponse *response, NSError *error))completionHandler;
- (BUYRequestOperation *)putRequestForURL:(NSURL *)url object:(id<BUYSerializable>)object completionHandler:(void (^)(NSDictionary *json, NSURLResponse *response, NSError *error))completionHandler;
- (BUYRequestOperation *)patchRequestForURL:(NSURL *)url object:(id <BUYSerializable>)object completionHandler:(void (^)(NSDictionary *json, NSURLResponse *response, NSError *error))completionHandler;
- (BUYRequestOperation *)postRequestForURL:(NSURL *)url object:(id <BUYSerializable>)object start:(BOOL)start completionHandler:(BUYClientRequestJSONCompletion)completionHandler;
- (BUYRequestOperation *)putRequestForURL:(NSURL *)url object:(id<BUYSerializable>)object start:(BOOL)start completionHandler:(BUYClientRequestJSONCompletion)completionHandler;
- (BUYRequestOperation *)patchRequestForURL:(NSURL *)url object:(id <BUYSerializable>)object start:(BOOL)start completionHandler:(BUYClientRequestJSONCompletion)completionHandler;
- (BUYStatus)statusForStatusCode:(NSUInteger)statusCode error:(NSError *)error;
- (NSError *)errorFromJSON:(NSDictionary *)json response:(NSURLResponse *)response;
- (void)startOperation:(BUYOperation *)operation;
@end
@class BUYCheckout;
@protocol BUYPaymentToken;
@interface BUYClient (PrivateCheckout)
- (BUYRequestOperation *)beginCheckout:(BUYCheckout *)checkout paymentToken:(id<BUYPaymentToken>)paymentToken completion:(BUYDataCheckoutBlock)block;
- (BUYRequestOperation *)getCompletionStatusOfCheckoutToken:(NSString *)token start:(BOOL)start completion:(BUYDataStatusBlock)block;
- (BUYRequestOperation *)getCheckout:(BUYCheckout *)checkout start:(BOOL)start completion:(BUYDataCheckoutBlock)block;
@end
......@@ -27,6 +27,7 @@
@import Foundation;
@class BUYModelManager;
@class BUYOperation;
@class BUYRequestOperation;
/**
......
......@@ -117,38 +117,65 @@ static NSString * const BUYClientJSONMimeType = @"application/json";
return nil;
}
#pragma mark - Auto Starting Convenience Requests
- (BUYRequestOperation *)getRequestForURL:(NSURL *)url completionHandler:(BUYClientRequestJSONCompletion)completionHandler
{
return [self getRequestForURL:url start:YES completionHandler:completionHandler];
}
- (BUYRequestOperation *)postRequestForURL:(NSURL *)url object:(id <BUYSerializable>)object completionHandler:(BUYClientRequestJSONCompletion)completionHandler
{
return [self postRequestForURL:url object:object start:YES completionHandler:completionHandler];
}
- (BUYRequestOperation *)putRequestForURL:(NSURL *)url object:(id<BUYSerializable>)object completionHandler:(BUYClientRequestJSONCompletion)completionHandler
{
return [self putRequestForURL:url object:object start:YES completionHandler:completionHandler];
}
- (BUYRequestOperation *)patchRequestForURL:(NSURL *)url object:(id <BUYSerializable>)object completionHandler:(BUYClientRequestJSONCompletion)completionHandler
{
return [self patchRequestForURL:url object:object start:YES completionHandler:completionHandler];
}
- (BUYRequestOperation *)deleteRequestForURL:(NSURL *)url completionHandler:(BUYClientRequestJSONCompletion)completionHandler
{
return [self deleteRequestForURL:url start:YES completionHandler:completionHandler];
}
#pragma mark - Convenience Requests
- (BUYRequestOperation *)getRequestForURL:(NSURL *)url completionHandler:(void (^)(NSDictionary *json, NSURLResponse *response, NSError *error))completionHandler
- (BUYRequestOperation *)getRequestForURL:(NSURL *)url start:(BOOL)start completionHandler:(BUYClientRequestJSONCompletion)completionHandler
{
return [self requestForURL:url method:@"GET" object:nil completionHandler:completionHandler];
return [self requestForURL:url method:@"GET" object:nil start:start completionHandler:completionHandler];
}
- (BUYRequestOperation *)postRequestForURL:(NSURL *)url object:(id <BUYSerializable>)object completionHandler:(void (^)(NSDictionary *json, NSURLResponse *response, NSError *error))completionHandler
- (BUYRequestOperation *)postRequestForURL:(NSURL *)url object:(id <BUYSerializable>)object start:(BOOL)start completionHandler:(BUYClientRequestJSONCompletion)completionHandler
{
return [self requestForURL:url method:@"POST" object:object completionHandler:completionHandler];
return [self requestForURL:url method:@"POST" object:object start:start completionHandler:completionHandler];
}
- (BUYRequestOperation *)putRequestForURL:(NSURL *)url object:(id<BUYSerializable>)object completionHandler:(void (^)(NSDictionary *json, NSURLResponse *response, NSError *error))completionHandler
- (BUYRequestOperation *)putRequestForURL:(NSURL *)url object:(id<BUYSerializable>)object start:(BOOL)start completionHandler:(BUYClientRequestJSONCompletion)completionHandler
{
return [self requestForURL:url method:@"PUT" object:object completionHandler:completionHandler];
return [self requestForURL:url method:@"PUT" object:object start:start completionHandler:completionHandler];
}
- (BUYRequestOperation *)patchRequestForURL:(NSURL *)url object:(id <BUYSerializable>)object completionHandler:(void (^)(NSDictionary *json, NSURLResponse *response, NSError *error))completionHandler
- (BUYRequestOperation *)patchRequestForURL:(NSURL *)url object:(id <BUYSerializable>)object start:(BOOL)start completionHandler:(BUYClientRequestJSONCompletion)completionHandler
{
return [self requestForURL:url method:@"PATCH" object:object completionHandler:completionHandler];
return [self requestForURL:url method:@"PATCH" object:object start:start completionHandler:completionHandler];
}
- (BUYRequestOperation *)deleteRequestForURL:(NSURL *)url completionHandler:(void (^)(NSDictionary *json, NSURLResponse *response, NSError *error))completionHandler
- (BUYRequestOperation *)deleteRequestForURL:(NSURL *)url start:(BOOL)start completionHandler:(BUYClientRequestJSONCompletion)completionHandler
{
return [self requestForURL:url method:@"DELETE" object:nil completionHandler:completionHandler];
return [self requestForURL:url method:@"DELETE" object:nil start:start completionHandler:completionHandler];
}
#pragma mark - Generic Requests
- (void)startTask:(BUYRequestOperation *)task
- (void)startOperation:(BUYOperation *)operation
{
[self.requestQueue addOperation:task];
[self.requestQueue addOperation:operation];
}
- (NSString *)authorizationHeader
......@@ -157,7 +184,12 @@ static NSString * const BUYClientJSONMimeType = @"application/json";
return [NSString stringWithFormat:@"%@ %@", @"Basic", [data base64EncodedStringWithOptions:0]];
}
- (BUYRequestOperation *)requestForURL:(NSURL *)url method:(NSString *)method object:(id <BUYSerializable>)object completionHandler:(void (^)(NSDictionary *json, NSURLResponse *response, NSError *error))completionHandler
- (BUYRequestOperation *)requestForURL:(NSURL *)url method:(NSString *)method object:(id <BUYSerializable>)object completionHandler:(BUYClientRequestJSONCompletion)completionHandler
{
return [self requestForURL:url method:method object:object start:YES completionHandler:completionHandler];
}
- (BUYRequestOperation *)requestForURL:(NSURL *)url method:(NSString *)method object:(id <BUYSerializable>)object start:(BOOL)start completionHandler:(BUYClientRequestJSONCompletion)completionHandler
{
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
if (object) {
......@@ -180,7 +212,9 @@ static NSString * const BUYClientJSONMimeType = @"application/json";
}];
}];
[self startTask:operation];
if (start) {
[self startOperation:operation];
}
return operation;
}
......
//
// BUYRouter.m
// Mobile Buy SDK
//
// Created by Shopify.
// Copyright (c) 2015 Shopify Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#import "BUYRouter.h"
#pragma mark - Route -
@interface BUYRoute ()
@property (strong, nonatomic) NSURLComponents *components;
@end
@implementation BUYRoute
+ (instancetype)routeWithFormat:(NSString *)format, ...
{
va_list list;
va_start(list, format);
NSString *URLString = [[NSString alloc] initWithFormat:format arguments:list];
va_end(list);
return [[[self class] alloc] initWithURLString:URLString];
}
- (instancetype)initWithURLString:(NSString *)URLString
{
self = [super init];
if (self) {
NSURL *url = [NSURL URLWithString:URLString];
_components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO];
}
return self;
}
- (void)setQueryItems:(NSDictionary *)queryItems
{
_queryItems = queryItems;
NSMutableArray *items = [NSMutableArray new];
[queryItems enumerateKeysAndObjectsUsingBlock:^(NSString *key, id value, BOOL *stop) {
[items addObject:[NSURLQueryItem queryItemWithName:key value:[NSString stringWithFormat:@"%@", value]]];
}];
[_components setQueryItems:items];
}
#pragma mark - Accessors -
- (NSURL *)URL
{
/* ---------------------------------
* All API requests should end with
* a .json suffix.
*/
return [_components.URL URLByAppendingPathExtension:@"json"];
}
#pragma mark - Mutation -
- (BUYRoute *)appendFormat:(NSString *)format, ...
{
va_list list;
va_start(list, format);
NSString *formattedString = [[NSString alloc] initWithFormat:format arguments:list];
va_end(list);
if (formattedString.length > 0) {
_components.path = [_components.path stringByAppendingPathComponent:formattedString];
}
return self;
}
- (BUYRoute *)appendPath:(NSString *)path
{
return [self appendFormat:path];
}
- (BUYRoute *)appendIdentifier:(NSNumber *)identifier
{
return [self appendFormat:@"%@", identifier];
}
@end
#pragma mark - Router -
@interface BUYRouter ()
@property (strong, nonatomic) NSString *shopDomain;
@property (strong, nonatomic) NSString *appID;
@end
@implementation BUYRouter
#pragma mark - Init -
- (instancetype)initWithShopDomain:(NSString *)shopDomain appID:(NSString *)appID
{
self = [super init];
if (self) {
_shopDomain = shopDomain;
_appID = appID;
}
return self;
}
#pragma mark - API -
- (BUYRoute *)routeForAPI
{
return [BUYRoute routeWithFormat:@"https://%@/api/", self.shopDomain];
}
- (BUYRoute *)routeForApps
{
return [[self routeForAPI] appendFormat:@"/apps/%@", self.appID];
}
#pragma mark - Storefront -
- (BUYRoute *)routeForShop
{
return [[self routeForAPI] appendPath:@"/meta"];
}
- (BUYRoute *)routeForProductListings
{
return [[self routeForApps] appendPath:@"/product_listings"];
}
- (BUYRoute *)routeForCollectionListings
{
return [[self routeForApps] appendPath:@"/collection_listings"];
}
#pragma mark - Checkout -
- (BUYRoute *)routeForCheckouts
{
return [[self routeForAPI] appendPath:@"/checkouts"];
}
- (BUYRoute *)routeForCheckoutsWithToken:(NSString *)token
{
return [self routeForCheckoutsAction:@"" withToken:token];
}
- (BUYRoute *)routeForCheckoutsProcessingWithToken:(NSString *)token
{
return [self routeForCheckoutsAction:@"/processing" withToken:token];
}
- (BUYRoute *)routeForCheckoutsCompletionWithToken:(NSString *)token
{
return [self routeForCheckoutsAction:@"/complete" withToken:token];
}
- (BUYRoute *)routeForCheckoutsShippingRatesWithToken:(NSString *)token
{
return [self routeForCheckoutsAction:@"/shipping_rates" withToken:token];
}
- (BUYRoute *)routeForCheckoutsUsingGiftCard
{
return [[self routeForCheckouts] appendPath:@"/gift_cards"];
}
- (BUYRoute *)routeForCheckoutsUsingGiftCardWithToken:(NSString *)token
{
return [[[self routeForCheckouts] appendPath:@"/gift_cards"] appendPath:token];
}
- (BUYRoute *)routeForCheckoutsUsingGiftCard:(NSNumber *)giftCardID token:(NSString *)token
{
return [[[self routeForCheckoutsUsingGiftCard] appendIdentifier:giftCardID] appendPath:token];
}
#pragma mark - Customers -
- (BUYRoute *)routeForCustomers
{
return [[self routeForAPI] appendPath:@"/customers"];
}
- (BUYRoute *)routeForCustomersOrders
{
return [[self routeForCustomers] appendPath:@"/orders"];
}
- (BUYRoute *)routeForCustomersWithID:(NSString *)identifier
{
return [[self routeForCustomers] appendPath:identifier];
}
- (BUYRoute *)routeForCustomersActivationWithID:(NSString *)identifier
{
return [[self routeForCustomersWithID:identifier] appendPath:@"/activate"];
}
- (BUYRoute *)routeForCustomersToken
{
return [[self routeForCustomers] appendPath:@"/customer_token"];
}
- (BUYRoute *)routeForCustomersTokenRenewalWithID:(NSString *)customerID
{
return [[self routeForCustomersWithID:customerID] appendPath:@"/customer_token/renew"];
}
- (BUYRoute *)routeForCustomersPasswordRecovery
{
return [[self routeForCustomers] appendPath:@"/recover"];
}
- (BUYRoute *)routeForCustomersPasswordResetWithID:(NSString *)identifier
{
return [[self routeForCustomersWithID:identifier] appendPath:@"/reset"];
}
#pragma mark - Utilities -
- (BUYRoute *)routeForCheckoutsAction:(NSString *)action withToken:(NSString *)token
{
return [[[self routeForCheckouts] appendPath:action] appendPath:token];
}
@end
//
// BUYCheckoutOperation.h
// Mobile Buy SDK
//
// Created by Shopify.
// Copyright (c) 2015 Shopify Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#import "BUYOperation.h"
NS_ASSUME_NONNULL_BEGIN
@class BUYClient;
@class BUYCheckout;
@protocol BUYPaymentToken;
typedef void (^BUYCheckoutOperationCompletion)(BUYCheckout * _Nullable checkout, NSError * _Nullable error);
@interface BUYCheckoutOperation : BUYOperation
@property (strong, nonatomic, readonly, nonnull) BUYClient *client;
+ (instancetype)operationWithClient:(BUYClient *)client checkout:(BUYCheckout *)checkout token:(id<BUYPaymentToken>)token completion:(BUYCheckoutOperationCompletion)completion;
- (instancetype)initWithClient:(BUYClient *)client checkout:(BUYCheckout *)checkout token:(id<BUYPaymentToken>)token completion:(BUYCheckoutOperationCompletion)completion;
@end
NS_ASSUME_NONNULL_END
//
// BUYCheckoutOperation.m
// Mobile Buy SDK
//
// Created by Shopify.
// Copyright (c) 2015 Shopify Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#import "BUYCheckoutOperation.h"
#import "BUYClient+Checkout.h"
#import "BUYClient+Internal.h"
#import "BUYPaymentToken.h"
#import "BUYCheckout.h"
#import "BUYRequestOperation.h"
@interface BUYCheckoutOperation ()
@property (strong, nonatomic, readonly) BUYCheckout *checkout;
@property (strong, nonatomic, readonly) id<BUYPaymentToken> token;
@property (strong, nonatomic, readonly) BUYCheckoutOperationCompletion completion;
@property (strong, atomic) NSArray *operations;
@end
@implementation BUYCheckoutOperation
#pragma mark - Init -
+ (instancetype)operationWithClient:(BUYClient *)client checkout:(BUYCheckout *)checkout token:(id<BUYPaymentToken>)token completion:(BUYCheckoutOperationCompletion)completion
{
return [[[self class] alloc] initWithClient:client checkout:checkout token:token completion:completion];
}
- (instancetype)initWithClient:(BUYClient *)client checkout:(BUYCheckout *)checkout token:(id<BUYPaymentToken>)token completion:(BUYCheckoutOperationCompletion)completion
{
self = [super init];
if (self) {
_client = client;
_checkout = checkout;
_token = token;
_completion = completion;
}
return self;
}
#pragma mark - Finishing -
- (void)finishWithCheckout:(BUYCheckout *)checkout
{
if (self.cancelled) {
return;
}
[self finishExecution];
self.completion(checkout, nil);
}
- (void)finishWithError:(NSError *)error
{
if (self.cancelled) {
return;
}
[self cancelAllOperations];
[self finishExecution];
self.completion(nil, error);
}
#pragma mark - Execution -
- (void)startExecution
{
if (self.cancelled) {
return;
}
[super startExecution];
BUYRequestOperation *beginOperation = [self createBeginOperation];
BUYRequestOperation *pollOperation = [self createPollOperation];
BUYRequestOperation *getOperation = [self createGetOperation];
[pollOperation addDependency:beginOperation];
[getOperation addDependency:pollOperation];
self.operations = @[
beginOperation,
pollOperation,
getOperation,
];
[self startAllOperations];
}
- (void)cancelExecution
{
[super cancelExecution];
[self cancelAllOperations];
}
#pragma mark - Start / Stop -
- (void)startAllOperations
{
for (BUYRequestOperation *operation in self.operations) {
[self.client startOperation:operation];
}
}
- (void)cancelAllOperations
{
for (BUYRequestOperation *operation in self.operations) {
[operation cancel];
}
}
#pragma mark - Operations -
- (BUYRequestOperation *)createBeginOperation
{
return [self.client beginCheckout:self.checkout paymentToken:self.token completion:^(BUYCheckout *checkout, NSError *error) {
if (!checkout) {
[self finishWithError:error];
}
}];
}
- (BUYRequestOperation *)createPollOperation
{
BUYRequestOperation *operation =[self.client getCompletionStatusOfCheckoutToken:self.checkout.token start:NO completion:^(BUYStatus status, NSError *error) {
if (status != BUYStatusComplete) {
[self finishWithError:error];
}
}];
operation.pollingHandler = ^BOOL (NSDictionary *json, NSHTTPURLResponse *response, NSError *error) {
return response.statusCode == BUYStatusProcessing;
};
return operation;
}
- (BUYRequestOperation *)createGetOperation
{
return [self.client getCheckout:self.checkout start:NO completion:^(BUYCheckout *checkout, NSError *error) {
if (checkout) {
[self finishWithCheckout:checkout];
} else {
[self finishWithError:error];
}
}];
}
@end
......@@ -34,6 +34,8 @@ typedef BOOL (^BUYRequestOperationPollingHandler)(NSDictionary * _Nullable json,
@interface BUYRequestOperation : BUYOperation
@property (assign, atomic) NSTimeInterval pollingInterval;
@property (strong, nonatomic, readonly, nonnull) NSURLSession *session;
@property (strong, nonatomic, readonly, nonnull) NSURLRequest *originalRequest;
......
......@@ -32,6 +32,7 @@ NSString * const kShopifyError = @"shopify";
typedef void (^BUYRequestJSONCompletion)(NSDictionary *json, NSHTTPURLResponse *response, NSError *error);
#pragma mark - NSURLResponse -
@interface NSHTTPURLResponse (Convenience)
@property (assign, nonatomic, readonly) BOOL successful;
......@@ -40,19 +41,13 @@ typedef void (^BUYRequestJSONCompletion)(NSDictionary *json, NSHTTPURLResponse *
@implementation NSHTTPURLResponse (Convenience)
- (BOOL)successful {
- (BOOL)successful
{
return ((NSUInteger)(self.statusCode / 100)) == 2;
}
@end
#pragma mark - BUYOperation Private -
@interface BUYOperation (Private)
- (void)setExecuting:(BOOL)executing;
- (void)setFinished:(BOOL)finished;
@end
#pragma mark - BUYRequestOperation -
@interface BUYRequestOperation ()
......@@ -65,7 +60,9 @@ typedef void (^BUYRequestJSONCompletion)(NSDictionary *json, NSHTTPURLResponse *
@implementation BUYRequestOperation
#pragma mark - Init -
+ (instancetype)operationWithSession:(NSURLSession *)session request:(NSURLRequest *)request payload:(id<BUYSerializable>)payload completion:(BUYRequestOperationCompletion)completion {
+ (instancetype)operationWithSession:(NSURLSession *)session request:(NSURLRequest *)request payload:(id<BUYSerializable>)payload completion:(BUYRequestOperationCompletion)completion
{
return [[[self class] alloc] initWithSession:session request:request payload:payload completion:completion];
}
......@@ -73,6 +70,8 @@ typedef void (^BUYRequestJSONCompletion)(NSDictionary *json, NSHTTPURLResponse *
{
self = [super init];
if (self) {
self.pollingInterval = 0.3;
_session = session;
_originalRequest = request;
_completion = completion;
......@@ -98,7 +97,7 @@ typedef void (^BUYRequestJSONCompletion)(NSDictionary *json, NSHTTPURLResponse *
- (void)startExecution
{
if (self.isCancelled) {
if (self.cancelled) {
return;
}
......@@ -127,13 +126,13 @@ typedef void (^BUYRequestJSONCompletion)(NSDictionary *json, NSHTTPURLResponse *
- (NSURLSessionDataTask *)requestUsingPollingIfNeeded:(NSURLRequest *)request completion:(BUYRequestJSONCompletion)completion
{
if (self.isCancelled) {
if (self.cancelled) {
return nil;
}
return [self request:request completion:^(NSDictionary *json, NSHTTPURLResponse *response, NSError *error) {
if (self.isCancelled) {
if (self.cancelled) {
return;
}
......@@ -144,9 +143,16 @@ typedef void (^BUYRequestJSONCompletion)(NSDictionary *json, NSHTTPURLResponse *
* the polling process.
*/
if (self.pollingHandler && self.pollingHandler(json, response, error)) {
NSURLSessionDataTask *task = [self requestUsingPollingIfNeeded:request completion:completion];
self.runningTask = task;
[task resume];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.pollingInterval * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if (self.cancelled) {
return;
}
NSURLSessionDataTask *task = [self requestUsingPollingIfNeeded:request completion:completion];
self.runningTask = task;
[task resume];
});
} else {
completion(json, response, error);
......
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