Commit 53102c3a by Dima Bart

Merge pull request #153 from Shopify/task/payment-providers

Add payment providers
parents 15e8ade1 2b1264cd
......@@ -30,7 +30,6 @@
extern NSString * const BUYShopDomain_Placeholder;
extern NSString * const BUYAPIKey_Placeholder;
extern NSString * const BUYAppId_Placeholder;
extern NSString * const BUYChannelId_Placeholder;
extern NSString * const BUYFakeCustomerToken;
@interface BUYClientTestBase : XCTestCase
......
//
// BUYPaymentProviderTests.m
// Mobile Buy SDK
//
// Created by David Muzi on 2015-12-15.
// Copyright © 2015 Shopify Inc. All rights reserved.
//
@import XCTest;
#import <Buy/Buy.h>
#import "BUYApplePayPaymentProvider.h"
#import "BUYWebCheckoutPaymentProvider.h"
#import "BUYClientTestBase.h"
#import "BUYPaymentController.h"
#import <OHHTTPStubs/OHHTTPStubs.h>
@interface BUYPaymentController ()
- (id <BUYPaymentProvider>)providerForType:(NSString *)type;
@end
@interface BUYPaymentProviderTests : XCTestCase <BUYPaymentProviderDelegate>
@property (nonatomic) NSMutableDictionary <NSString *, XCTestExpectation *> *expectations;
@property (nonatomic) BUYModelManager *modelManager;
@end
@implementation BUYPaymentProviderTests
- (void)setUp
{
[super setUp];
self.modelManager = [BUYModelManager modelManager];
self.expectations = [@{} mutableCopy];
}
- (void)tearDown
{
[super tearDown];
[OHHTTPStubs removeAllStubs];
}
- (BUYClient *)client
{
return [[BUYClient alloc] initWithShopDomain:BUYShopDomain_Placeholder apiKey:BUYAPIKey_Placeholder appId:BUYAppId_Placeholder];
}
- (BUYCheckout *)checkout
{
return [self.modelManager insertCheckoutWithJSONDictionary:nil];
}
- (void)mockRequests
{
// This mocks a getShop, and createCheckout request
[OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest * _Nonnull request) {
return YES;
} withStubResponse:^OHHTTPStubsResponse * _Nonnull(NSURLRequest * _Nonnull request) {
return [BUYPaymentProviderTests responseForRequest:request];
}];
}
+ (OHHTTPStubsResponse *)responseForRequest:(NSURLRequest *)request
{
NSURLComponents *components = [NSURLComponents componentsWithURL:request.URL resolvingAgainstBaseURL:NO];
if ([components.path isEqualToString:@"/meta.json"]) {
return [OHHTTPStubsResponse responseWithJSONObject:@{@"id": @"123", @"country": @"US", @"currency": @"USD"} statusCode:200 headers:nil];
}
else if ([components.path isEqualToString:@"/api/checkouts.json"]) {
return [OHHTTPStubsResponse responseWithJSONObject:@{@"checkout":@{@"payment_due": @(99), @"web_checkout_url": @"https://example.com"}} statusCode:200 headers:nil];
}
return nil;
}
#pragma mark - Apple Pay
- (void)testAppleAvailability
{
BUYApplePayPaymentProvider *applePay = [[BUYApplePayPaymentProvider alloc] initWithClient:self.client merchantID:@"merchant.id.1"];
XCTAssertTrue(applePay.isAvailable);
BUYApplePayPaymentProvider *applePay2 = [[BUYApplePayPaymentProvider alloc] initWithClient:self.client merchantID:@""];
XCTAssertFalse(applePay2.isAvailable);
}
- (void)testApplePayPresentationCallbacks
{
[self mockRequests];
BUYApplePayPaymentProvider *applePay = [[BUYApplePayPaymentProvider alloc] initWithClient:self.client merchantID:@"merchant.id.1"];
applePay.delegate = self;
self.expectations[@"presentController"] = [self expectationWithDescription:NSStringFromSelector(_cmd)];
[applePay startCheckout:self.checkout];
[self waitForExpectationsWithTimeout:1 handler:^(NSError *error) {
XCTAssertNil(error);
}];
}
- (void)testApplePayProvider
{
BUYApplePayPaymentProvider *applePay1 = [[BUYApplePayPaymentProvider alloc] initWithClient:self.client merchantID:@"merchant.id.1"];
XCTAssertEqualObjects(applePay1.merchantID, @"merchant.id.1");
// 4 default networks should be configured
XCTAssertEqual(applePay1.supportedNetworks.count, 4);
applePay1.supportedNetworks = @[PKPaymentNetworkMasterCard];
XCTAssertEqual(applePay1.supportedNetworks.count, 1);
XCTAssertEqualObjects(applePay1.supportedNetworks[0], PKPaymentNetworkMasterCard);
}
- (void)testCanShowApplePaySetup
{
BUYApplePayPaymentProvider *applePay = [[BUYApplePayPaymentProvider alloc] initWithClient:self.client merchantID:@"merchant.id.1"];
XCTAssertTrue(applePay.canShowApplePaySetup);
BUYApplePayPaymentProvider *applePay2 = [[BUYApplePayPaymentProvider alloc] initWithClient:self.client merchantID:@""];
XCTAssertFalse(applePay2.canShowApplePaySetup);
}
- (void)testFailedApplePayCallbacks
{
[OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest * _Nonnull request) {
return YES;
} withStubResponse:^OHHTTPStubsResponse * _Nonnull(NSURLRequest * _Nonnull request) {
return [OHHTTPStubsResponse responseWithJSONObject:@{} statusCode:400 headers:nil];
}];
BUYApplePayPaymentProvider *applePay = [[BUYApplePayPaymentProvider alloc] initWithClient:self.client merchantID:@"merchant.id.1"];
applePay.delegate = self;
self.expectations[@"failedCheckout"] = [self expectationWithDescription:NSStringFromSelector(_cmd)];
self.expectations[@"failedShop"] = [self expectationWithDescription:NSStringFromSelector(_cmd)];
[applePay startCheckout:self.checkout];
[self waitForExpectationsWithTimeout:1 handler:^(NSError *error) {
XCTAssertNil(error);
}];
}
#pragma mark - Web
- (void)testWebAvailability
{
BUYWebCheckoutPaymentProvider *webProvider = [[BUYWebCheckoutPaymentProvider alloc] initWithClient:self.client];
XCTAssertTrue(webProvider.isAvailable);
}
- (void)testWebPresentationCallbacks
{
[self mockRequests];
BUYWebCheckoutPaymentProvider *webProvider = [[BUYWebCheckoutPaymentProvider alloc] initWithClient:self.client];
webProvider.delegate = self;
self.expectations[@"presentController"] = [self expectationWithDescription:NSStringFromSelector(_cmd)];
[webProvider startCheckout:self.checkout];
[self waitForExpectationsWithTimeout:1 handler:^(NSError *error) {
XCTAssertNil(error);
}];
}
#pragma mark - Payment Controller
- (void)testPaymentController
{
BUYPaymentController *controller = [[BUYPaymentController alloc] init];
BUYApplePayPaymentProvider *applePay1 = [[BUYApplePayPaymentProvider alloc] initWithClient:self.client merchantID:@"merchant.id.1"];
[controller addPaymentProvider:applePay1];
XCTAssertEqual(controller.providers.count, 1);
id <BUYPaymentProvider> provider = [controller providerForType:BUYApplePayPaymentProviderId];
XCTAssertEqualObjects(provider, applePay1);
BUYWebCheckoutPaymentProvider *webProvider = [[BUYWebCheckoutPaymentProvider alloc] initWithClient:self.client];
[controller addPaymentProvider:webProvider];
XCTAssertEqual(controller.providers.count, 2);
provider = [controller providerForType:BUYWebPaymentProviderId];
XCTAssertEqualObjects(provider, webProvider);
// Attempt to add an alternate Apple Pay provider
BUYApplePayPaymentProvider *applePay2 = [[BUYApplePayPaymentProvider alloc] initWithClient:self.client merchantID:@"merchant.id.2"];
[controller addPaymentProvider:applePay2];
XCTAssertEqual(controller.providers.count, 2);
}
- (void)testStartingPaymentWithPaymentController
{
[self mockRequests];
BUYPaymentController *controller = [[BUYPaymentController alloc] init];
BUYWebCheckoutPaymentProvider *webProvider = [[BUYWebCheckoutPaymentProvider alloc] initWithClient:self.client];
webProvider.delegate = self;
[controller addPaymentProvider:webProvider];
self.expectations[@"presentController"] = [self expectationWithDescription:NSStringFromSelector(_cmd)];
[controller startCheckout:self.checkout withProviderType:BUYWebPaymentProviderId];
[self waitForExpectationsWithTimeout:1 handler:^(NSError *error) {
XCTAssertNil(error);
}];
}
#pragma mark - Payment Provider delegate
- (void)paymentProvider:(id <BUYPaymentProvider>)provider wantsControllerPresented:(UIViewController *)controller
{
[self.expectations[@"presentController"] fulfill];
}
- (void)paymentProviderWantsControllerDismissed:(id <BUYPaymentProvider>)provider
{
}
- (void)paymentProviderWillStartCheckout:(id <BUYPaymentProvider>)provider
{
}
- (void)paymentProviderDidDismissCheckout:(id <BUYPaymentProvider>)provider
{
}
- (void)paymentProvider:(id <BUYPaymentProvider>)provider didFailToUpdateCheckoutWithError:(NSError *)error
{
}
- (void)paymentProvider:(id <BUYPaymentProvider>)provider didFailCheckoutWithError:(NSError *)error;
{
if (self.expectations[@"failedCheckout"]) {
[self.expectations[@"failedCheckout"] fulfill];
[self.expectations removeObjectForKey:@"failedCheckout"];
}
if (self.expectations[@"failedShop"]) {
[self.expectations[@"failedShop"] fulfill];
[self.expectations removeObjectForKey:@"failedShop"];
}
}
- (void)paymentProvider:(id <BUYPaymentProvider>)provider didCompleteCheckout:(BUYCheckout *)checkout withStatus:(BUYStatus)status
{
}
@end
......@@ -5,8 +5,8 @@
"channel_id": "",
"app_id": "",
"merchant_id": "",
"customer_email": "",
"customer_password": "",
"customer_email": "asd@asd.com",
"customer_password": "asdasd",
"product_ids": [
"",
""
......
......@@ -57,8 +57,14 @@ FOUNDATION_EXPORT const unsigned char BuyVersionString[];
#import <Buy/BUYApplePayAdditions.h>
#import <Buy/BUYApplePayHelpers.h>
#import <Buy/BUYApplePayPaymentProvider.h>
#import <Buy/BUYPaymentController.h>
#import <Buy/BUYPaymentProvider.h>
#import <Buy/BUYWebCheckoutPaymentProvider.h>
#import <Buy/BUYClient.h>
#import <Buy/BUYClient+Customers.h>
#import <Buy/BUYClient+Checkout.h>
#import <Buy/BUYError.h>
#import <Buy/BUYError+BUYAdditions.h>
#import <Buy/BUYManagedObject.h>
......@@ -76,6 +82,7 @@ FOUNDATION_EXPORT const unsigned char BuyVersionString[];
#import <Buy/BUYViewController.h>
#import <Buy/NSArray+BUYAdditions.h>
#import <Buy/NSDate+BUYAdditions.h>
#import <Buy/NSDateFormatter+BUYAdditions.h>
#import <Buy/NSDecimalNumber+BUYAdditions.h>
#import <Buy/NSDictionary+BUYAdditions.h>
......
//
// BUYApplePayPaymentProvider.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 UIKit;
#import "BUYPaymentProvider.h"
NS_ASSUME_NONNULL_BEGIN
extern NSString *const BUYApplePayPaymentProviderId;
@class BUYClient;
@interface BUYApplePayPaymentProvider : NSObject <BUYPaymentProvider>
/**
* Initializer for Apple Pay payment provider
*
* @param client a `BUYClient`
* @param merchantID the merchant ID for Apple Pay
*
* @return an instance of `BUYApplePayPaymentProvider`
*/
- (instancetype)initWithClient:(BUYClient *)client merchantID:(NSString *)merchantID NS_DESIGNATED_INITIALIZER;
- (instancetype)init NS_UNAVAILABLE;
/**
* The supported credit card payment networks. Default values:
* iOS 8.3: PKPaymentNetworkAmex, PKPaymentNetworkMasterCard, PKPaymentNetworkVisa.
* iOS 9.0: PKPaymentNetworkAmex, PKPaymentNetworkDiscover, PKPaymentNetworkMasterCard, PKPaymentNetworkVisa.
*/
@property (nonatomic, copy) NSArray * supportedNetworks;
/**
* The merchant ID required for Apple Pay
*/
@property (nonatomic, copy, readonly) NSString * merchantID;
/**
* If the merchantId is set and the device support Apple Pay but no credit card is present this allows the user to add a payment pass to the Wallet.
* The user is given the option to add a payment pass or continue with web checkout. Default is set to true. The Set Up Apple Pay button will, however
* still only show if [PKAddPaymentPassViewController canAddPaymentPass] returns true, merchantId is set and the app is running iOS 9.0 and above.
*/
@property (nonatomic, assign) BOOL allowApplePaySetup;
/**
* Whether the device is setup to show the Apple Pay setup sheet.
* `allowApplePaySetup` must be set to YES, and the `merchantId` must also be set in addition to the
* device settings for this method to return YES.
*
* @return YES if the Setup Apple Pay button should be shown
*/
- (BOOL)canShowApplePaySetup;
@end
NS_ASSUME_NONNULL_END
//
// BUYClient+Checkout.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 <Buy/Buy.h>
NS_ASSUME_NONNULL_BEGIN
@interface BUYClient (Checkout)
- (NSURLSessionDataTask *)handleCheckout:(BUYCheckout *)checkout completion:(BUYDataCheckoutBlock)completion;
@end
NS_ASSUME_NONNULL_END
//
// BUYClient+Checkout.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 "BUYClient+Checkout.h"
@implementation BUYClient (Checkout)
- (NSURLSessionDataTask *)handleCheckout:(BUYCheckout *)checkout completion:(BUYDataCheckoutBlock)completion
{
if ([checkout hasToken]) {
return [self updateCheckout:checkout completion:completion];
} else {
return [self createCheckout:checkout completion:completion];
}
}
@end
//
// BUYPaymentController.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 Foundation;
#import "BUYPaymentProvider.h"
NS_ASSUME_NONNULL_BEGIN
@interface BUYPaymentController : NSObject
/**
* The registered payment providers
*/
@property (nonatomic, strong, readonly) NSOrderedSet <id <BUYPaymentProvider>> *providers;
/**
* Register a payment provider
*
* @param paymentProvider a payment provider
*
* @note can only add 1 provider per type
*/
- (void)addPaymentProvider:(id <BUYPaymentProvider>)paymentProvider;
/**
* Convenience method to retrieve a BUYPaymentProvider by the identifier
*
* @param type The identifier for the payment provider
*
* @return The payment provider matching the given identifier
*/
- (id <BUYPaymentProvider> _Nullable)providerForType:(NSString *)type;
/**
* Start a checkout
*
* @param checkout the `BUYCheckout` to start
* @param type the type of payment provider to use
*/
- (void)startCheckout:(BUYCheckout *)checkout withProviderType:(NSString *)typeIdentifier;
@end
NS_ASSUME_NONNULL_END
//
// BUYPaymentController.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 "BUYPaymentController.h"
NSString *const BUYPaymentProviderWillStartCheckoutNotificationKey = @"BUYPaymentProviderWillStartCheckoutNotificationKey";
NSString *const BUYPaymentProviderDidDismissCheckoutNotificationKey = @"BUYPaymentProviderDidDismissCheckoutNotificationKey";
NSString *const BUYPaymentProviderDidFailToUpdateCheckoutNotificationKey = @"BUYPaymentProviderDidFailToUpdateCheckoutNotificationKey";
NSString *const BUYPaymentProviderDidFailCheckoutNotificationKey = @"BUYPaymentProviderDidFailCheckoutNotificationKey";
NSString *const BUYPaymentProviderDidCompleteCheckoutNotificationKey = @"BUYPaymentProviderDidCompleteCheckoutNotificationKey";
@interface BUYPaymentController ()
@property (nonatomic, strong) NSMutableOrderedSet <id <BUYPaymentProvider>> *mutableProviders;
@end
@implementation BUYPaymentController
- (void)startCheckout:(BUYCheckout *)checkout withProviderType:(NSString *)typeIdentifier;
{
id <BUYPaymentProvider> provider = [self providerForType:typeIdentifier];
[provider startCheckout:checkout];
}
- (void)addPaymentProvider:(id <BUYPaymentProvider>)paymentProvider
{
if ([self.mutableProviders containsObject:paymentProvider]) {
NSLog(@"Payment provider %@ has already been added", paymentProvider.identifier);
}
[self.mutableProviders addObject:paymentProvider];
}
- (NSSet <id <BUYPaymentProvider>> *)providers
{
return [self.mutableProviders copy];
}
- (NSMutableOrderedSet *)mutableProviders
{
if (_mutableProviders == nil) {
_mutableProviders = [[NSMutableOrderedSet alloc] init];
}
return _mutableProviders;
}
- (id <BUYPaymentProvider>)providerForType:(NSString *)type
{
for (id <BUYPaymentProvider> provider in self.mutableProviders) {
if ([provider.identifier isEqualToString: type]) {
return provider;
}
}
return nil;
}
@end
//
// BUYPaymentProvider.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 UIKit;
#import "BUYClient.h"
NS_ASSUME_NONNULL_BEGIN
@class BUYCheckout;
@protocol BUYPaymentProvider;
extern NSString *const BUYPaymentProviderWillStartCheckoutNotificationKey;
extern NSString *const BUYPaymentProviderDidDismissCheckoutNotificationKey;
extern NSString *const BUYPaymentProviderDidFailToUpdateCheckoutNotificationKey;
extern NSString *const BUYPaymentProviderDidFailCheckoutNotificationKey;
extern NSString *const BUYPaymentProviderDidCompleteCheckoutNotificationKey;
@protocol BUYPaymentProviderDelegate <NSObject>
@required
/**
* Called when a view controller needs to be presented
*
* @param provider the `BUYPaymentProvider`
* @param controller the `UIViewController` to be presented
*/
- (void)paymentProvider:(id <BUYPaymentProvider>)provider wantsControllerPresented:(UIViewController *)controller;
/**
* Called when the view controller
*
* @param provider the `BUYPaymentProvider`
*/
- (void)paymentProviderWantsControllerDismissed:(id <BUYPaymentProvider>)provider;
@optional
/**
* Called when the checkout process has started
*
* @param provider the `BUYPaymentProvider`
*/
- (void)paymentProviderWillStartCheckout:(id <BUYPaymentProvider>)provider;
/**
* Called when the checkout has been dismissed
*
* @param provider the `BUYPaymentProvider`
*/
- (void)paymentProviderDidDismissCheckout:(id <BUYPaymentProvider>)provider;
/**
* Called when a checkout failed to update
*
* @param provider the `BUYPaymentProvider`
* @param error the optional `NSError`
*/
- (void)paymentProvider:(id <BUYPaymentProvider>)provider didFailToUpdateCheckoutWithError:(NSError *)error;
/**
* Called when the checkout failed
*
* @param provider the `BUYPaymentProvider`
* @param error the optional `NSError`
*/
- (void)paymentProvider:(id <BUYPaymentProvider>)provider didFailCheckoutWithError:(NSError * _Nullable)error;
/**
* Called when the checkout has completed
*
* @param provider the `BUYPaymentProvider`
* @param checkout the `BUYCheckout`
* @param status the `BUYStatus` of the checkout
*/
- (void)paymentProvider:(id <BUYPaymentProvider>)provider didCompleteCheckout:(BUYCheckout *)checkout withStatus:(BUYStatus)status;
@end
@protocol BUYPaymentProvider <NSObject>
/**
* Starts the checkout process
*
* @param checkout the `BUYCheckout`
*/
- (void)startCheckout:(BUYCheckout *)checkout;
/**
* Clears the current checkout in progress
*/
- (void)cancelCheckout;
/**
* The payment type identifier
*/
@property (nonatomic, readonly) NSString *identifier;
/**
* The checkout object. If checkout has started, the checkout token will be set.
*/
@property (nonatomic, readonly) BUYCheckout *checkout;
/**
* Whether the payment provider is currently available given the current device and configuration of the `BUYPaymentProvider`
*/
@property (nonatomic, readonly, getter=isAvailable) BOOL available;
/**
* Returns YES if the checkout is currently in progress. `startPaymentForCheckout` should not be called when a checkout is already in progress
*/
@property (nonatomic, readonly, getter=isInProgress) BOOL inProgress;
/**
* Delegate to receive callback messages
*/
@property (nonatomic, weak) id <BUYPaymentProviderDelegate> delegate;
@end
NS_ASSUME_NONNULL_END
//
// BUYWebCheckoutPaymentProvider.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 <Foundation/Foundation.h>
#import "BUYPaymentProvider.h"
NS_ASSUME_NONNULL_BEGIN
extern NSString * BUYSafariCallbackURLNotification;
extern NSString * BUYURLKey;
extern NSString * const BUYWebPaymentProviderId;
@interface BUYWebCheckoutPaymentProvider : NSObject <BUYPaymentProvider>
/**
* Web payment provider
*
* @param client a `BUYClient`
*
* @return an instance of `BUYWebCheckoutPaymentProvider`
*/
- (instancetype)initWithClient:(BUYClient *)client NS_DESIGNATED_INITIALIZER;
- (instancetype)init NS_UNAVAILABLE;
@end
NS_ASSUME_NONNULL_END
//
// BUYWebCheckoutPaymentProvider.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 "BUYWebCheckoutPaymentProvider.h"
#import "BUYCheckout.h"
#import "BUYClient+Checkout.h"
@import SafariServices;
NSString * BUYSafariCallbackURLNotification = @"kBUYSafariCallbackURLNotification";
NSString * BUYURLKey = @"url";
NSString * const BUYWebPaymentProviderId = @"BUYWebPaymentProviderId";
static NSString *const WebCheckoutCustomerAccessToken = @"customer_access_token";
@interface BUYWebCheckoutPaymentProvider () <SFSafariViewControllerDelegate>
@property (nonatomic, strong) BUYCheckout *checkout;
@property (nonatomic, strong) BUYClient *client;
@end
@implementation BUYWebCheckoutPaymentProvider
@synthesize delegate;
- (instancetype)initWithClient:(BUYClient *)client
{
NSParameterAssert(client);
self = [super init];
if (self) {
_client = client;
}
return self;
}
- (BOOL)isInProgress
{
return (self.checkout != nil);
}
- (NSUInteger)hash
{
return self.identifier.hash;
}
- (BOOL)isEqual:(id)object
{
return ([object isKindOfClass:[self class]] && [self.identifier isEqual:[object identifier]]);
}
- (NSString *)identifier
{
return BUYWebPaymentProviderId;
}
- (void)startCheckout:(BUYCheckout *)checkout
{
if (self.isInProgress) {
[[NSException exceptionWithName:NSInternalInconsistencyException reason:[NSString stringWithFormat:@"Asked to start checkout; but checkout has already started in %@", self] userInfo:nil] raise];
}
self.checkout = checkout;
[self.client handleCheckout:checkout completion:^(BUYCheckout *checkout, NSError *error) {
[self postCheckoutCompletion:checkout error:error];
}];
}
- (void)cancelCheckout
{
self.checkout = nil;
}
- (void)cancelCheckoutAndNotify
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:BUYSafariCallbackURLNotification object:nil];
if ([self.delegate respondsToSelector:@selector(paymentProviderDidDismissCheckout:)]) {
[self.delegate paymentProviderDidDismissCheckout:self];
}
[[NSNotificationCenter defaultCenter] postNotificationName:BUYPaymentProviderDidDismissCheckoutNotificationKey object:self];
self.checkout = nil;
}
- (BOOL)isAvailable
{
return YES;
}
- (void)postCheckoutCompletion:(BUYCheckout *)checkout error:(NSError *)error
{
if (self.checkout && error == nil) {
self.checkout = checkout;
if ([self.delegate respondsToSelector:@selector(paymentProviderWillStartCheckout:)]) {
[self.delegate paymentProviderWillStartCheckout:self];
}
[[NSNotificationCenter defaultCenter] postNotificationName:BUYPaymentProviderWillStartCheckoutNotificationKey object:self];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveCallbackURLNotification:) name:BUYSafariCallbackURLNotification object:nil];
[self openWebCheckout:checkout];
}
else {
if ([self.delegate respondsToSelector:@selector(paymentProvider:didFailCheckoutWithError:)]) {
[self.delegate paymentProvider:self didFailCheckoutWithError:error];
}
[[NSNotificationCenter defaultCenter] postNotificationName:BUYPaymentProviderDidFailCheckoutNotificationKey object:self];
self.checkout = nil;
}
}
- (void)openWebCheckout:(BUYCheckout *)checkout
{
NSURL *checkoutURL = [self authenticatedWebCheckoutURL:checkout.webCheckoutURL];
if ([SFSafariViewController class]) {
SFSafariViewController *safariViewController = [[SFSafariViewController alloc] initWithURL:checkoutURL];
safariViewController.delegate = self;
[self.delegate paymentProvider:self wantsControllerPresented:safariViewController];
}
else {
[[UIApplication sharedApplication] openURL:checkoutURL];
}
}
- (NSURL *)authenticatedWebCheckoutURL:(NSURL *)url
{
NSString *customerToken = self.client.customerToken;
if (!customerToken.length) {
return url;
}
NSURLQueryItem *item = [NSURLQueryItem queryItemWithName:WebCheckoutCustomerAccessToken value:customerToken];
NSURLComponents *authenticatedComponents = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:YES];
authenticatedComponents.queryItems = authenticatedComponents.queryItems ? [authenticatedComponents.queryItems arrayByAddingObject:item] : @[item];
return authenticatedComponents.URL;
}
#pragma mark - Web Checkout delegate methods
- (void)safariViewControllerDidFinish:(SFSafariViewController *)controller;
{
[self cancelCheckoutAndNotify];
}
- (void)didReceiveCallbackURLNotification:(NSNotification *)notification
{
NSURL *url = notification.userInfo[BUYURLKey];
[self.client getCompletionStatusOfCheckoutURL:url completion:^(BUYStatus status, NSError *error) {
if ([self.delegate respondsToSelector:@selector(paymentProvider:didCompleteCheckout:withStatus:)]) {
[self.delegate paymentProvider:self didCompleteCheckout:self.checkout withStatus:status];
}
[[NSNotificationCenter defaultCenter] postNotificationName:BUYPaymentProviderDidCompleteCheckoutNotificationKey object:self];
[[NSNotificationCenter defaultCenter] removeObserver:self name:BUYSafariCallbackURLNotification object:nil];
}];
[self.delegate paymentProviderWantsControllerDismissed:self];
self.checkout = nil;
}
@end
......@@ -36,9 +36,6 @@
#import "BUYCartLineItem.h"
#import "BUYCheckout.h"
#import "BUYCheckoutAttribute.h"
#import "BUYClient+Test.h"
#import "BUYClient.h"
#import "BUYClient+Customers.h"
#import "BUYCollection.h"
#import "BUYCreditCard.h"
#import "BUYCustomer.h"
......@@ -56,7 +53,17 @@
#import "BUYShop.h"
#import "BUYTaxLine.h"
#import "BUYApplePayHelpers.h"
#import "BUYApplePayPaymentProvider.h"
#import "BUYPaymentController.h"
#import "BUYPaymentProvider.h"
#import "BUYWebCheckoutPaymentProvider.h"
#import "BUYClient.h"
#import "BUYClient+Customers.h"
#import "BUYClient+Checkout.h"
#import "BUYError.h"
#import "BUYError+BUYAdditions.h"
#import "BUYManagedObject.h"
#import "BUYModelManager.h"
#import "BUYModelManagerProtocol.h"
......
......@@ -35,10 +35,7 @@
#import "BUYApplePayHelpers.h"
#import "BUYDiscount.h"
#import "BUYShop.h"
NSString * BUYSafariCallbackURLNotification = @"kBUYSafariCallbackURLNotification";
NSString * BUYURLKey = @"url";
#import "BUYWebCheckoutPaymentProvider.h"
@interface BUYViewController () <SFSafariViewControllerDelegate>
......
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