Issue #356
StripeHandler.swift
From Stripe 16.0.0 https://github.com/stripe/stripe-ios/blob/master/CHANGELOG.md#1600-2019-07-18
Migrates STPPaymentCardTextField.cardParams property type from STPCardParams to STPPaymentMethodCardParams
final class StripeHandler {
func createPaymentMethod(
textField: STPPaymentCardTextField,
completion: @escaping (Result<STPPaymentMethod, Error>) -> Void) {
let paymentMethodParams = STPPaymentMethodParams(
card: textField.cardParams,
billingDetails: nil,
metadata: nil
)
STPAPIClient.shared().createPaymentMethod(
with: paymentMethodParams,
completion: { paymentMethod, error in
DispatchQueue.main.async {
if let paymentMethod = paymentMethod {
completion(.success(paymentMethod))
} else {
completion(.failure(error ?? AppError.request))
}
}
})
STPAPIClient.shared().createPaymentMethod(
with: paymentMethodParams,
completion: { paymentMethod, error in
DispatchQueue.main.async {
if let paymentMethod = paymentMethod {
completion(.success(paymentMethod))
} else {
completion(.failure(error ?? AppError.request))
}
}
})
}
func confirmSetupIntents(
clientSecret: String,
paymentMethodId: String,
context: STPAuthenticationContext?,
completion: @escaping (Result<STPSetupIntent, Error>) -> Void) {
guard let context = context else {
completion(.failure(AppError.invalid))
return
}
let setupIntentParams = STPSetupIntentConfirmParams(clientSecret: clientSecret)
setupIntentParams.paymentMethodID = paymentMethodId
let paymentHandler = STPPaymentHandler.shared()
paymentHandler.confirmSetupIntent(
setupIntentParams,
with: context,
completion: { status, intent, error in
DispatchQueue.main.async {
if case .succeeded = status, let intent = intent {
completion(.success(intent))
} else {
completion(.failure(error ?? AppError.invalid))
}
}
})
}
func confirmPaymentIntents(
clientSecret: String,
context: STPAuthenticationContext?,
completion: @escaping (Result<STPPaymentIntent, Error>) -> Void) {
guard let context = context else {
completion(.failure(AppError.invalid))
return
}
STPPaymentHandler.shared().handleNextAction(
forPayment: clientSecret,
with: context,
returnURL: nil,
completion: { status, paymentIntent, error in
if case .succeeded = status, let paymentIntent = paymentIntent {
completion(.success(paymentIntent))
} else {
completion(.failure(error ?? AppError.invalid))
}
})
}
func toCard(paymentMethod: STPPaymentMethod) -> MyCard? {
guard
let card = paymentMethod.card,
let last4 = card.last4
else {
return nil
}
return withValue(MyCard()) {
$0.expiryYear = UInt32(card.expYear)
$0.expiryMonth = UInt32(card.expMonth)
$0.lastFourDigits = last4
$0.brand = STPCard.string(from: card.brand)
}
}
}
Payment intents
https://stripe.com/docs/payments/payment-intents/creating-payment-intents
When using automatic confirmation, create the PaymentIntent at the beginning of the checkout process. When using manual confirmation, create the PaymentIntent after collecting payment information from the customer using Elements or our iOS and Android SDKs. For a detailed comparison on the automatic and manual confirmation flows, see accepting one-time payments.
Step 3: Authenticate the payment if necessary
Pass the confirmed Payment Intent client secret from the previous step to STPPaymentHandler handleNextActionForPayment. If the customer must perform 3D Secure authentication to complete the payment, STPPaymentHandler presents view controllers using the STPAuthenticationContext passed in and walks them through that process. See Supporting 3D Secure Authentication on iOS to learn more.
MyAPIClient.createAndConfirmPaymentIntent(paymentMethodId: paymentMethodId) { result in
guard case .success(let paymentIntentClientSecret) = result else {
// Handle error
return
}
STPPaymentHandler.shared().handleNextAction(forPayment: paymentIntentClientSecret, with: self, returnURL: nil) { (status, paymentIntent, error) in
switch (status) {
case .succeeded:
// ...Continued in Step 4
case .canceled:
// Handle cancel
case .failed:
// Handle error
}
}
}
Setup intents
There is Setup intents https://stripe.com/docs/payments/cards/reusing-cards#saving-cards-without-payment for saving cards
Use the Setup Intents API to authenticate a customer’s card without making an initial payment. This flow works best for businesses that want to onboard customers without charging them right away:
Step 4: Submit the card details to Stripe from the client
Pass the STPSetupIntentParams object to the confirmSetupIntent method on a STPPaymentHandler sharedManager. If the customer must perform additional steps to complete the payment, such as authentication, STPPaymentHandler presents view controllers using the STPAuthenticationContext passed in and walks them through that process. See Supporting 3D Secure Authentication on iOS to learn more.
let setupIntentParams = STPSetupIntentParams(clientSecret: clientSecret)
setupIntentParams.paymentMethodId = paymentMethodId
let paymentManager = STPPaymentHandler.shared()
paymentManager.confirmSetupIntent(setupIntentParams, authenticationContext: self, completion { (status, setupIntent, error) in
switch (status) {
case .succeeded:
// Setup succeeded
case .canceled:
// Handle cancel
case .failed:
// Handle error
}
})
Authentication context
In STPPaymentHandler.m
- (BOOL)_canPresentWithAuthenticationContext:(id<STPAuthenticationContext>)authenticationContext {
UIViewController *presentingViewController = authenticationContext.authenticationPresentingViewController;
// Is presentingViewController non-nil and in the window?
if (presentingViewController == nil || presentingViewController.view.window == nil) {
return NO;
}
// Is it the Apple Pay VC?
if ([presentingViewController isKindOfClass:[PKPaymentAuthorizationViewController class]]) {
// 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 prepareAuthenticationContextForPresentation: to dismiss it.
return [authenticationContext respondsToSelector:@selector(prepareAuthenticationContextForPresentation:)];
}
}
Use stripe SDK
STPSetupIntentConfirmParams.useStripeSDK
A boolean number to indicate whether you intend to use the Stripe SDK’s functionality to handle any SetupIntent next actions. If set to false, STPSetupIntent.nextAction will only ever contain a redirect url that can be opened in a webview or mobile browser. When set to true, the nextAction may contain information that the Stripe SDK can use to perform native authentication within your app.
let setupIntentParams = STPSetupIntentConfirmParams(clientSecret: clientSecret)
setupIntentParams.useStripeSDK = NSNumber(booleanLiteral: true)