Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ApplePay support in STPPaymentHandler, STPPaymentContext #1264

Merged
merged 10 commits into from
Jul 31, 2019
27 changes: 17 additions & 10 deletions Example/Custom Integration/ApplePayExampleViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -134,24 +134,31 @@ - (void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController
}

- (void)_createAndConfirmPaymentIntentWithPaymentMethod:(STPPaymentMethod *)paymentMethod completion:(void (^)(PKPaymentAuthorizationStatus))completion {
void (^finishWithStatus)(PKPaymentAuthorizationStatus) = ^(PKPaymentAuthorizationStatus status) {
if (self.applePayVC) {
completion(status);
} else {
[self _finish];
}
};
void (^reconfirmPaymentIntent)(STPPaymentIntent *) = ^(STPPaymentIntent *paymentIntent) {
[self.delegate confirmPaymentIntent:paymentIntent completion:^(STPBackendResult status, NSString *clientSecret, NSError *error) {
if (status == STPBackendResultFailure || error) {
self.applePayError = error;
self.applePayVC ? completion(PKPaymentAuthorizationStatusFailure) : [self _finish];
finishWithStatus(PKPaymentAuthorizationStatusFailure);
return;
}
[[STPAPIClient sharedClient] retrievePaymentIntentWithClientSecret:clientSecret completion:^(STPPaymentIntent *finalPaymentIntent, NSError *finalError) {
if (finalError) {
self.applePayError = finalError;
self.applePayVC ? completion(PKPaymentAuthorizationStatusFailure) : [self _finish];
finishWithStatus(PKPaymentAuthorizationStatusFailure);
return;
}
if (finalPaymentIntent.status == STPPaymentIntentStatusSucceeded || finalPaymentIntent.status == STPPaymentIntentStatusRequiresCapture) {
self.applePaySucceeded = YES;
self.applePayVC ? completion(PKPaymentAuthorizationStatusSuccess) : [self _finish];
finishWithStatus(PKPaymentAuthorizationStatusSuccess);
} else {
self.applePayVC ? completion(PKPaymentAuthorizationStatusFailure) : [self _finish];
finishWithStatus(PKPaymentAuthorizationStatusFailure);
}
}];
}];
Expand All @@ -160,19 +167,19 @@ - (void)_createAndConfirmPaymentIntentWithPaymentMethod:(STPPaymentMethod *)paym
switch (handlerStatus) {
case STPPaymentHandlerActionStatusFailed:
self.applePayError = handlerError;
self.applePayVC ? completion(PKPaymentAuthorizationStatusFailure) : [self _finish];
finishWithStatus(PKPaymentAuthorizationStatusFailure);
break;
case STPPaymentHandlerActionStatusCanceled:
self.applePayError = [NSError errorWithDomain:StripeDomain code:123 userInfo:@{NSLocalizedDescriptionKey: @"User cancelled"}];
self.applePayVC ? completion(PKPaymentAuthorizationStatusFailure) : [self _finish];
finishWithStatus(PKPaymentAuthorizationStatusFailure);
break;
case STPPaymentHandlerActionStatusSucceeded:
if (paymentIntent.status == STPPaymentIntentStatusRequiresConfirmation) {
// Manually confirm the PaymentIntent on the backend again to complete the payment.
reconfirmPaymentIntent(paymentIntent);
break;
} else {
self.applePayVC ? completion(PKPaymentAuthorizationStatusSuccess) : [self _finish];
finishWithStatus(PKPaymentAuthorizationStatusSuccess);
}
}
};
Expand Down Expand Up @@ -220,14 +227,14 @@ - (UIViewController *)authenticationPresentingViewController {
return self;
}

- (void)authenticationWillPresent:(STPVoidBlock)continueBlock {
- (void)prepareAuthenticationContextForPresentation:(STPVoidBlock)completion {
if (self.applePayVC.presentingViewController != nil) {
[self dismissViewControllerAnimated:YES completion:^{
self.applePayVC = nil;
continueBlock();
completion();
}];
} else {
continueBlock();
completion();
}
}

Expand Down
16 changes: 8 additions & 8 deletions Stripe/Payments/STPPaymentHandler.m
Original file line number Diff line number Diff line change
Expand Up @@ -488,8 +488,8 @@ - (void)_handleAuthenticationForCurrentAction {
[self->_currentAction completeWithStatus:STPPaymentHandlerActionStatusFailed error:[self _errorForCode:STPPaymentHandlerStripe3DS2ErrorCode userInfo:@{@"exception": exception}]];
}
};
if ([self->_currentAction.authenticationContext respondsToSelector:@selector(authenticationWillPresent:)]) {
[self->_currentAction.authenticationContext authenticationWillPresent:doChallenge];
if ([self->_currentAction.authenticationContext respondsToSelector:@selector(prepareAuthenticationContextForPresentation:)]) {
[self->_currentAction.authenticationContext prepareAuthenticationContextForPresentation:doChallenge];
} else {
doChallenge();
}
Expand Down Expand Up @@ -576,8 +576,8 @@ - (void)_handleRedirectToURL:(NSURL *)url withReturnURL:(nullable NSURL *)return
safariViewController.delegate = self;
[presentingViewController presentViewController:safariViewController animated:YES completion:nil];
};
if ([self->_currentAction.authenticationContext respondsToSelector:@selector(authenticationWillPresent:)]) {
[self->_currentAction.authenticationContext authenticationWillPresent:doChallenge];
if ([self->_currentAction.authenticationContext respondsToSelector:@selector(prepareAuthenticationContextForPresentation:)]) {
[self->_currentAction.authenticationContext prepareAuthenticationContextForPresentation:doChallenge];
} else {
doChallenge();
}
Expand Down Expand Up @@ -611,16 +611,16 @@ - (BOOL)_canPresentWithAuthenticationContext:(id<STPAuthenticationContext>)authe

// Is it the Apple Pay VC?
if ([presentingViewController isKindOfClass:[PKPaymentAuthorizationViewController class]]) {
// We can't present over Apple Pay, user must implement authenticationWillPresent to dismiss it.
return [authenticationContext respondsToSelector:@selector(authenticationWillPresent:)];
// We can't present over Apple Pay, user must implement prepareAuthenticationContextForPresentation: to dismiss it.
return [authenticationContext respondsToSelector:@selector(prepareAuthenticationContextForPresentation:)];
}

// Is it already presenting something?
if (presentingViewController.presentedViewController == nil) {
return YES;
} else {
// Hopefully the user implemented authenticationWillPresent: to dismiss it.
return [authenticationContext respondsToSelector:@selector(authenticationWillPresent:)];
// Hopefully the user implemented prepareAuthenticationContextForPresentation: to dismiss it.
return [authenticationContext respondsToSelector:@selector(prepareAuthenticationContextForPresentation:)];
}
}

Expand Down
2 changes: 1 addition & 1 deletion Stripe/PublicHeaders/STPAuthenticationContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ NS_ASSUME_NONNULL_BEGIN

yuki-stripe marked this conversation as resolved.
Show resolved Hide resolved
Note that `paymentAuthorizationViewControllerDidFinish` is not called after `PKPaymentAuthorizationViewController` is dismissed.
*/
- (void)authenticationWillPresent:(STPVoidBlock)continueBlock;
- (void)prepareAuthenticationContextForPresentation:(STPVoidBlock)completion;

@end

Expand Down
4 changes: 2 additions & 2 deletions Stripe/PublicHeaders/STPPaymentHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ typedef NS_ENUM(NSInteger, STPPaymentHandlerErrorCode) {

/**
Payment requires a valid `STPAuthenticationContext`. Make sure your presentingViewController isn't already presenting.
If you're using Apple Pay, you must implement `STPAuthenticationContext authenticationWillPresent`
If you're using Apple Pay, you must implement `STPAuthenticationContext prepareAuthenticationContextForPresentation:`
*/
STPPaymentHandlerRequiresAuthenticationContextErrorCode,
};
Expand All @@ -104,7 +104,7 @@ typedef void (^STPPaymentHandlerActionSetupIntentCompletionBlock)(STPPaymentHand
`STPPaymentHandler` is a utility class that can confirm PaymentIntents and handle
any additional required actions for 3DS(2) authentication. It can present authentication UI on top of your app or redirect users out of your app (to e.g. their banking app).

@note If you're using Apple Pay, you must implement `STPAuthenticationContext authenticationWillPresent`. See that method's docstring for more details.
@note If you're using Apple Pay, you must implement `STPAuthenticationContext prepareAuthenticationContextForPresentation:`. See that method's docstring for more details.

@see https://stripe.com/docs/mobile/ios/authentication
*/
Expand Down
6 changes: 3 additions & 3 deletions Stripe/STPPaymentContext.m
Original file line number Diff line number Diff line change
Expand Up @@ -752,14 +752,14 @@ - (UIViewController *)authenticationPresentingViewController {
return self.hostViewController;
}

- (void)authenticationWillPresent:(STPVoidBlock)continueBlock {
- (void)prepareAuthenticationContextForPresentation:(STPVoidBlock)completion {
if (self.applePayVC && self.applePayVC.presentingViewController != nil) {
[self.hostViewController dismissViewControllerAnimated:[self transitionAnimationsEnabled]
completion:^{
continueBlock();
completion();
}];
} else {
continueBlock();
completion();
}
}

Expand Down