Skip to content

Commit

Permalink
[iOS] Add 3DSecure (#74)
Browse files Browse the repository at this point in the history
* Trigger 3DSecure modal

* Add threeDSecure option

* Handle errors

* Handle race condition

* Dismiss native view while waiting for 3DSecure
  • Loading branch information
Minishlink authored and kraffslol committed Dec 19, 2017
1 parent ff92d6c commit 96fd6a0
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 3 deletions.
1 change: 1 addition & 0 deletions index.ios.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ var Braintree = {
title: config.title,
description: config.description,
amount: config.amount,
threeDSecure: config.threeDSecure,
};
return new Promise(function(resolve, reject) {
RCTBraintree.showPaymentViewController(options, function(err, nonce) {
Expand Down
3 changes: 3 additions & 0 deletions ios/RCTBraintree/RCTBraintree.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,19 @@
#import "BraintreePayPal.h"
#import "BraintreeCard.h"
#import "BraintreeUI.h"
#import "Braintree3DSecure.h"
#import "BTDataCollector.h"
#import "PPDataCollector.h"

@interface RCTBraintree : UIViewController <RCTBridgeModule, BTDropInViewControllerDelegate, BTViewControllerPresentingDelegate>

@property (nonatomic, strong) BTAPIClient *braintreeClient;
@property (nonatomic, strong, readwrite) BTThreeDSecureDriver *threeDSecure;
@property (nonatomic, strong) UIViewController *reactRoot;
@property (nonatomic, strong) BTDataCollector *dataCollector;

@property (nonatomic, strong) RCTResponseSenderBlock callback;
@property (nonatomic, strong) NSDictionary *threeDSecureOptions;

+ (instancetype)sharedInstance;
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation;
Expand Down
40 changes: 37 additions & 3 deletions ios/RCTBraintree/RCTBraintree.m
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ - (instancetype)init
RCT_EXPORT_METHOD(showPaymentViewController:(NSDictionary *)options callback:(RCTResponseSenderBlock)callback)
{
dispatch_async(dispatch_get_main_queue(), ^{
self.threeDSecureOptions = options[@"threeDSecure"];
if (self.threeDSecureOptions) {
self.threeDSecure = [[BTThreeDSecureDriver alloc] initWithAPIClient:self.braintreeClient delegate:self];
}

BTDropInViewController *dropInViewController = [[BTDropInViewController alloc] initWithAPIClient:self.braintreeClient];
dropInViewController.delegate = self;

Expand Down Expand Up @@ -228,6 +233,7 @@ - (void)paymentDriver:(id)paymentDriver requestsDismissalOfViewController:(UIVie

- (void)userDidCancelPayment {
[self.reactRoot dismissViewControllerAnimated:YES completion:nil];
runCallback = FALSE;
self.callback(@[@"USER_CANCELLATION", [NSNull null]]);
}

Expand All @@ -238,10 +244,38 @@ - (void)dropInViewControllerWillComplete:(BTDropInViewController *)viewControlle
- (void)dropInViewController:(BTDropInViewController *)viewController didSucceedWithTokenization:(BTPaymentMethodNonce *)paymentMethodNonce {
// when the user pays for the first time with paypal, dropInViewControllerWillComplete is never called, yet the callback should be invoked. the second condition checks for that
if (runCallback || ([paymentMethodNonce.type isEqualToString:@"PayPal"] && [viewController.paymentMethodNonces count] == 1)) {
runCallback = FALSE;
self.callback(@[[NSNull null],paymentMethodNonce.nonce]);
if (self.threeDSecure) {
[self.reactRoot dismissViewControllerAnimated:YES completion:nil];
[self.threeDSecure verifyCardWithNonce:paymentMethodNonce.nonce
amount:self.threeDSecureOptions[@"amount"]
completion:^(BTThreeDSecureCardNonce *card, NSError *error) {
if (runCallback) {
runCallback = FALSE;
if (error) {
self.callback(@[error.localizedDescription, [NSNull null]]);
} else if (card) {
if (!card.liabilityShiftPossible) {
self.callback(@[@"3DSECURE_NOT_ABLE_TO_SHIFT_LIABILITY", [NSNull null]]);
} else if (!card.liabilityShifted) {
self.callback(@[@"3DSECURE_LIABILITY_NOT_SHIFTED", [NSNull null]]);
} else {
self.callback(@[[NSNull null], card.nonce]);
}
} else {
self.callback(@[@"USER_CANCELLATION", [NSNull null]]);
}
}
[self.reactRoot dismissViewControllerAnimated:YES completion:nil];
}];
} else {
runCallback = FALSE;
self.callback(@[[NSNull null], paymentMethodNonce.nonce]);
}
}

if (!self.threeDSecure) {
[self.reactRoot dismissViewControllerAnimated:YES completion:nil];
}
[self.reactRoot dismissViewControllerAnimated:YES completion:nil];
}

- (void)dropInViewControllerDidCancel:(__unused BTDropInViewController *)viewController {
Expand Down

0 comments on commit 96fd6a0

Please sign in to comment.