From 6fadddffcdd64024d927039eb126cb446ac82b38 Mon Sep 17 00:00:00 2001 From: Till Hellmund Date: Fri, 31 Jan 2025 15:03:56 -0500 Subject: [PATCH] Enable bank payments in native Link --- .../Helpers/PaymentSheetLinkAccount.swift | 13 +++++++++- .../API Bindings/Link/ConsumerSession.swift | 4 +++ .../API Bindings/Link/STPAPIClient+Link.swift | 7 ++++- .../PayWithLinkViewController.swift | 6 +++-- .../PaymentSheet/PaymentSheet+API.swift | 26 +++++++++++++++++-- 5 files changed, 50 insertions(+), 6 deletions(-) diff --git a/StripePaymentSheet/StripePaymentSheet/Source/Helpers/PaymentSheetLinkAccount.swift b/StripePaymentSheet/StripePaymentSheet/Source/Helpers/PaymentSheetLinkAccount.swift index 995a09b296d..cf22ce48080 100644 --- a/StripePaymentSheet/StripePaymentSheet/Source/Helpers/PaymentSheetLinkAccount.swift +++ b/StripePaymentSheet/StripePaymentSheet/Source/Helpers/PaymentSheetLinkAccount.swift @@ -298,6 +298,7 @@ class PaymentSheetLinkAccount: PaymentSheetLinkAccountInfoProtocol { } func listPaymentDetails( + elementsSession: STPElementsSession, completion: @escaping (Result<[ConsumerPaymentDetails], Error>) -> Void ) { retryingOnAuthError(completion: completion) { completionRetryingOnAuthErrors in @@ -306,9 +307,12 @@ class PaymentSheetLinkAccount: PaymentSheetLinkAccountInfoProtocol { completion(.failure(PaymentSheetError.unknown(debugDescription: "Paying with Link without valid session"))) return } + + let supportedPaymentMethodTypes = self.supportedPaymentDetailsTypes(for: elementsSession) session.listPaymentDetails( with: self.apiClient, + supportedPaymentMethodTypes: supportedPaymentMethodTypes, consumerAccountPublishableKey: self.publishableKey, completion: completionRetryingOnAuthErrors ) @@ -364,7 +368,13 @@ class PaymentSheetLinkAccount: PaymentSheetLinkAccountInfoProtocol { } } - func sharePaymentDetails(id: String, cvc: String?, allowRedisplay: STPPaymentMethodAllowRedisplay?, completion: @escaping (Result) -> Void) { + func sharePaymentDetails( + id: String, + cvc: String?, + allowRedisplay: STPPaymentMethodAllowRedisplay?, + expectedPaymentMethodType: String?, + completion: @escaping (Result + ) -> Void) { retryingOnAuthError(completion: completion) { [apiClient, publishableKey] completionRetryingOnAuthErrors in guard let session = self.currentSession else { stpAssertionFailure() @@ -380,6 +390,7 @@ class PaymentSheetLinkAccount: PaymentSheetLinkAccountInfoProtocol { id: id, cvc: cvc, allowRedisplay: allowRedisplay, + expectedPaymentMethodType: expectedPaymentMethodType, consumerAccountPublishableKey: publishableKey, completion: completionRetryingOnAuthErrors ) diff --git a/StripePaymentSheet/StripePaymentSheet/Source/Internal/API Bindings/Link/ConsumerSession.swift b/StripePaymentSheet/StripePaymentSheet/Source/Internal/API Bindings/Link/ConsumerSession.swift index a06d8243df9..c99f4fda881 100644 --- a/StripePaymentSheet/StripePaymentSheet/Source/Internal/API Bindings/Link/ConsumerSession.swift +++ b/StripePaymentSheet/StripePaymentSheet/Source/Internal/API Bindings/Link/ConsumerSession.swift @@ -198,11 +198,13 @@ extension ConsumerSession { func listPaymentDetails( with apiClient: STPAPIClient = STPAPIClient.shared, + supportedPaymentMethodTypes: Set, consumerAccountPublishableKey: String?, completion: @escaping (Result<[ConsumerPaymentDetails], Error>) -> Void ) { apiClient.listPaymentDetails( for: clientSecret, + supportedPaymentMethodTypes: supportedPaymentMethodTypes, consumerAccountPublishableKey: consumerAccountPublishableKey, completion: completion) } @@ -239,6 +241,7 @@ extension ConsumerSession { id: String, cvc: String?, allowRedisplay: STPPaymentMethodAllowRedisplay?, + expectedPaymentMethodType: String?, consumerAccountPublishableKey: String?, completion: @escaping (Result) -> Void ) { @@ -248,6 +251,7 @@ extension ConsumerSession { consumerAccountPublishableKey: consumerAccountPublishableKey, allowRedisplay: allowRedisplay, cvc: cvc, + expectedPaymentMethodType: expectedPaymentMethodType, completion: completion) } diff --git a/StripePaymentSheet/StripePaymentSheet/Source/Internal/API Bindings/Link/STPAPIClient+Link.swift b/StripePaymentSheet/StripePaymentSheet/Source/Internal/API Bindings/Link/STPAPIClient+Link.swift index 98da371542f..2e954c5272c 100644 --- a/StripePaymentSheet/StripePaymentSheet/Source/Internal/API Bindings/Link/STPAPIClient+Link.swift +++ b/StripePaymentSheet/StripePaymentSheet/Source/Internal/API Bindings/Link/STPAPIClient+Link.swift @@ -288,6 +288,7 @@ extension STPAPIClient { consumerAccountPublishableKey: String?, allowRedisplay: STPPaymentMethodAllowRedisplay?, cvc: String?, + expectedPaymentMethodType: String?, completion: @escaping (Result) -> Void ) { let endpoint: String = "consumers/payment_details/share" @@ -305,6 +306,9 @@ extension STPAPIClient { if let allowRedisplay { parameters["allow_redisplay"] = allowRedisplay.stringValue } + if let expectedPaymentMethodType { + parameters["expected_payment_method_type"] = expectedPaymentMethodType + } APIRequest.post( with: self, @@ -322,6 +326,7 @@ extension STPAPIClient { func listPaymentDetails( for consumerSessionClientSecret: String, + supportedPaymentMethodTypes: Set, consumerAccountPublishableKey: String?, completion: @escaping (Result<[ConsumerPaymentDetails], Error>) -> Void ) { @@ -330,7 +335,7 @@ extension STPAPIClient { let parameters: [String: Any] = [ "credentials": ["consumer_session_client_secret": consumerSessionClientSecret], "request_surface": "ios_payment_element", - "types": ["card"], + "types": supportedPaymentMethodTypes.map(\.rawValue), ] post( diff --git a/StripePaymentSheet/StripePaymentSheet/Source/Internal/Link/Controllers/PayWithLinkViewController.swift b/StripePaymentSheet/StripePaymentSheet/Source/Internal/Link/Controllers/PayWithLinkViewController.swift index 592fedc0760..b79d32f7180 100644 --- a/StripePaymentSheet/StripePaymentSheet/Source/Internal/Link/Controllers/PayWithLinkViewController.swift +++ b/StripePaymentSheet/StripePaymentSheet/Source/Internal/Link/Controllers/PayWithLinkViewController.swift @@ -256,8 +256,10 @@ private extension PayWithLinkViewController { stpAssertionFailure(LinkAccountError.noLinkAccount.localizedDescription) return } - - linkAccount.listPaymentDetails { result in + + linkAccount.listPaymentDetails( + elementsSession: context.elementsSession + ) { result in switch result { case .success(let paymentDetails): if paymentDetails.isEmpty { diff --git a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentSheet+API.swift b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentSheet+API.swift index e32b4ffcd2b..9e7aeb2b91e 100644 --- a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentSheet+API.swift +++ b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentSheet+API.swift @@ -456,7 +456,12 @@ extension PaymentSheet { case .success(let paymentDetails): if elementsSession.linkPassthroughModeEnabled { // If passthrough mode, share payment details - linkAccount.sharePaymentDetails(id: paymentDetails.stripeID, cvc: paymentMethodParams.card?.cvc, allowRedisplay: paymentMethodParams.allowRedisplay) { result in + linkAccount.sharePaymentDetails( + id: paymentDetails.stripeID, + cvc: paymentMethodParams.card?.cvc, + allowRedisplay: paymentMethodParams.allowRedisplay, + expectedPaymentMethodType: paymentDetails.expectedPaymentMethodTypeForPassthroughMode + ) { result in switch result { case .success(let paymentDetailsShareResponse): confirmWithPaymentMethod(paymentDetailsShareResponse.paymentMethod, linkAccount, shouldSave) @@ -514,7 +519,12 @@ extension PaymentSheet { if elementsSession.linkPassthroughModeEnabled { // allowRedisplay is nil since we are not saving a payment method. - linkAccount.sharePaymentDetails(id: paymentDetails.stripeID, cvc: paymentDetails.cvc, allowRedisplay: nil) { result in + linkAccount.sharePaymentDetails( + id: paymentDetails.stripeID, + cvc: paymentDetails.cvc, + allowRedisplay: nil, + expectedPaymentMethodType: paymentDetails.expectedPaymentMethodTypeForPassthroughMode + ) { result in switch result { case .success(let paymentDetailsShareResponse): confirmWithPaymentMethod(paymentDetailsShareResponse.paymentMethod, linkAccount, shouldSave) @@ -733,3 +743,15 @@ private func isEqual(_ lhs: STPPaymentIntentShippingDetails?, _ rhs: STPPaymentI return rhs == lhsConverted } + +private extension ConsumerPaymentDetails { + + var expectedPaymentMethodTypeForPassthroughMode: String? { + switch type { + case .card, .unparsable: + return nil + case .bankAccount: + return "card" + } + } +}