From ba4f0d2b6eb61b5011ed4b9e196edea4f7cc75e5 Mon Sep 17 00:00:00 2001 From: Cameron <36750494+csabol-stripe@users.noreply.github.com> Date: Thu, 10 Feb 2022 16:03:46 -0800 Subject: [PATCH] Uses card CVC to complete payment/setup for new or updated Link card (#747) * Pass new card cvc to Link intent completion * Include cvc updates --- Stripe/ConsumerSession.swift | 14 ++++++++------ ...nkViewController-NewPaymentViewController.swift | 4 ++++ ...iewController-UpdatePaymentViewController.swift | 7 +++++++ Stripe/PaymentDetails.swift | 12 ++++++++++++ Stripe/STPAPIClient+Link.swift | 14 ++++++++++++-- 5 files changed, 43 insertions(+), 8 deletions(-) diff --git a/Stripe/ConsumerSession.swift b/Stripe/ConsumerSession.swift index bd59bcb3b50..c97006230b8 100644 --- a/Stripe/ConsumerSession.swift +++ b/Stripe/ConsumerSession.swift @@ -274,16 +274,17 @@ extension ConsumerSession { updateParams: updateParams, completion: completion) } - + func completePayment(with apiClient: STPAPIClient = STPAPIClient.shared, for paymentIntent: STPPaymentIntent, paymentDetails: ConsumerPaymentDetails, completion: @escaping STPPaymentIntentCompletionBlock) { apiClient.completePayment(for: paymentIntent.stripeId, - paymentIntentClientSecret: paymentIntent.clientSecret, - consumerSessionClientSecret: clientSecret, - paymentDetailsID: paymentDetails.stripeID, - completion: completion) + paymentIntentClientSecret: paymentIntent.clientSecret, + consumerSessionClientSecret: clientSecret, + paymentDetailsID: paymentDetails.stripeID, + cvc: paymentDetails.cvc, + completion: completion) } func completeSetup(with apiClient: STPAPIClient = STPAPIClient.shared, @@ -294,9 +295,10 @@ extension ConsumerSession { setupIntentClientSecret: setupIntent.clientSecret, consumerSessionClientSecret: clientSecret, paymentDetailsID: paymentDetails.stripeID, + cvc: paymentDetails.cvc, completion: completion) } - + func logout( with apiClient: STPAPIClient = STPAPIClient.shared, diff --git a/Stripe/PayWithLinkViewController-NewPaymentViewController.swift b/Stripe/PayWithLinkViewController-NewPaymentViewController.swift index 37dc5dcade9..e882ea64976 100644 --- a/Stripe/PayWithLinkViewController-NewPaymentViewController.swift +++ b/Stripe/PayWithLinkViewController-NewPaymentViewController.swift @@ -175,6 +175,10 @@ extension PayWithLinkViewController { } if let paymentDetails = paymentDetails { + if case .card(let card) = paymentDetails.details { + card.cvc = confirmParams.paymentMethodParams.card?.cvc + } + self.coordinator?.confirm(with: self.linkAccount, paymentDetails: paymentDetails, completion: { [weak self] result in diff --git a/Stripe/PayWithLinkViewController-UpdatePaymentViewController.swift b/Stripe/PayWithLinkViewController-UpdatePaymentViewController.swift index a83cf3bf567..f2ba77ff8ab 100644 --- a/Stripe/PayWithLinkViewController-UpdatePaymentViewController.swift +++ b/Stripe/PayWithLinkViewController-UpdatePaymentViewController.swift @@ -198,6 +198,13 @@ extension PayWithLinkViewController { switch result { case .success(let updatedPaymentDetails): + // Updates to CVC only get applied when the intent is confirmed so we manually add them here + // instead of including in the /update API call + if let cvc = paymentMethodParams.card?.cvc, + case .card(let card) = updatedPaymentDetails.details { + card.cvc = cvc + } + self?.updateButton.update(state: .succeeded, style: nil, callToAction: nil, animated: true) { self?.delegate?.didUpdate(paymentMethod: updatedPaymentDetails) self?.navigationController?.popViewController(animated: true) diff --git a/Stripe/PaymentDetails.swift b/Stripe/PaymentDetails.swift index 189fdc8eec9..0b9bdd6be1c 100644 --- a/Stripe/PaymentDetails.swift +++ b/Stripe/PaymentDetails.swift @@ -120,6 +120,9 @@ extension ConsumerPaymentDetails.Details { let allResponseFields: [AnyHashable : Any] + /// A frontend convenience property, i.e. not part of the API Object + var cvc: String? = nil + required init(expiryYear: Int, expiryMonth: Int, brand: String, @@ -244,6 +247,15 @@ extension ConsumerPaymentDetails { return nil } } + + var cvc: String? { + switch details { + case .card(let card): + return card.cvc + case .bankAccount: + return nil + } + } var accessibilityDescription: String { switch details { diff --git a/Stripe/STPAPIClient+Link.swift b/Stripe/STPAPIClient+Link.swift index e734f468d3f..ffa1ad48d3b 100644 --- a/Stripe/STPAPIClient+Link.swift +++ b/Stripe/STPAPIClient+Link.swift @@ -321,15 +321,20 @@ extension STPAPIClient { paymentIntentClientSecret: String, consumerSessionClientSecret: String, paymentDetailsID: String, + cvc: String?, completion: @escaping STPPaymentIntentCompletionBlock) { let endpoint: String = "consumers/payment_intents/\(paymentIntentID)/complete" - let parameters: [String: Any] = [ + var parameters: [String: Any] = [ "credentials": ["consumer_session_client_secret": consumerSessionClientSecret], "client_secret": paymentIntentClientSecret, "payment_details_id": paymentDetailsID ] + if let cvc = cvc { + parameters["payment_method_options"] = ["card": ["cvc": cvc]] + } + APIRequest.post( with: self, endpoint: endpoint, @@ -343,15 +348,20 @@ extension STPAPIClient { setupIntentClientSecret: String, consumerSessionClientSecret: String, paymentDetailsID: String, + cvc: String?, completion: @escaping STPSetupIntentCompletionBlock) { let endpoint: String = "consumers/setup_intents/\(setupIntentID)/complete" - let parameters: [String: Any] = [ + var parameters: [String: Any] = [ "credentials": ["consumer_session_client_secret": consumerSessionClientSecret], "client_secret": setupIntentClientSecret, "payment_details_id": paymentDetailsID ] + if let cvc = cvc { + parameters["payment_method_options"] = ["card": ["cvc": cvc]] + } + APIRequest.post( with: self, endpoint: endpoint,