From cdb29195e00e0304c46a54ca66a9970ea9c98a54 Mon Sep 17 00:00:00 2001 From: Mat Schmid Date: Tue, 27 Aug 2024 13:52:48 -0400 Subject: [PATCH 1/5] [POC] Link Brand Card --- .../PaymentSheetTestPlaygroundSettings.swift | 9 +++++++ .../PlaygroundController.swift | 1 + .../project.pbxproj | 4 +++ .../Helpers/PaymentSheetLinkAccount.swift | 8 +++++- .../ConsumerSession+PaymentMethodType.swift | 15 +++++++++++ .../API Bindings/Link/ConsumerSession.swift | 2 ++ .../API Bindings/Link/PaymentDetails.swift | 12 ++++++++- .../API Bindings/Link/STPAPIClient+Link.swift | 5 ++++ .../Link/Extensions/Intent+Link.swift | 4 +++ .../PaymentSheet/PaymentMethodType.swift | 25 ++++++++++++++++--- .../PaymentSheet/PaymentOption+Images.swift | 2 ++ .../PaymentSheet/PaymentSheet+API.swift | 22 +++++++++++++++- .../PaymentSheetFormFactory.swift | 2 ++ .../StripePayments.xcodeproj/project.pbxproj | 4 +++ .../STPPaymentMethodEnums.swift | 6 +++++ .../STPPaymentMethodParams.swift | 7 +++++- .../STPPaymentMethodLinkCardBrandParams.swift | 22 ++++++++++++++++ .../Models/Shared/LinkSettings.swift | 11 ++++++++ .../Enums+CustomStringConvertible.swift | 2 ++ .../PaymentHandler/STPPaymentHandler.swift | 1 + 20 files changed, 156 insertions(+), 8 deletions(-) create mode 100644 StripePaymentSheet/StripePaymentSheet/Source/Internal/API Bindings/Link/ConsumerSession+PaymentMethodType.swift create mode 100644 StripePayments/StripePayments/Source/API Bindings/Models/PaymentMethods/Types/STPPaymentMethodLinkCardBrandParams.swift diff --git a/Example/PaymentSheet Example/PaymentSheet Example/PaymentSheetTestPlaygroundSettings.swift b/Example/PaymentSheet Example/PaymentSheet Example/PaymentSheetTestPlaygroundSettings.swift index 71dac6bee4b..cc5694d381a 100644 --- a/Example/PaymentSheet Example/PaymentSheet Example/PaymentSheetTestPlaygroundSettings.swift +++ b/Example/PaymentSheet Example/PaymentSheet Example/PaymentSheetTestPlaygroundSettings.swift @@ -242,6 +242,15 @@ struct PaymentSheetTestPlaygroundSettings: Codable, Equatable { case link_pm = "Link PM" case passthrough + case link_card_brand = "Link Card" + + var value: String { + switch self { + case .link_pm: "LINK_PAYMENT_METHOD" + case .passthrough: "PASSTHROUGH" + case .link_card_brand: "LINK_CARD_BRAND" + } + } } enum UserOverrideCountry: String, PickerEnum { diff --git a/Example/PaymentSheet Example/PaymentSheet Example/PlaygroundController.swift b/Example/PaymentSheet Example/PaymentSheet Example/PlaygroundController.swift index 8e40ecc1f0c..7acc188e973 100644 --- a/Example/PaymentSheet Example/PaymentSheet Example/PlaygroundController.swift +++ b/Example/PaymentSheet Example/PaymentSheet Example/PlaygroundController.swift @@ -458,6 +458,7 @@ extension PlaygroundController { "mode": settings.mode.rawValue, "automatic_payment_methods": settings.apmsEnabled == .on, "use_link": settings.linkMode == .link_pm, + "link_mode": settings.linkMode.value, "use_manual_confirmation": settings.integrationType == .deferred_mc, "require_cvc_recollection": settings.requireCVCRecollection == .on, "customer_session_component_name": "mobile_payment_element", diff --git a/StripePaymentSheet/StripePaymentSheet.xcodeproj/project.pbxproj b/StripePaymentSheet/StripePaymentSheet.xcodeproj/project.pbxproj index 854cb94fbc3..42ee5533b02 100644 --- a/StripePaymentSheet/StripePaymentSheet.xcodeproj/project.pbxproj +++ b/StripePaymentSheet/StripePaymentSheet.xcodeproj/project.pbxproj @@ -85,6 +85,7 @@ 47AD56A9889DF5EFBBA9CEFB /* PollingViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ADE49E72DD5EDA448D12D88 /* PollingViewTests.swift */; }; 47B19F96CCEA290541E3B988 /* CardSectionElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D03000A6807B09BFD8E6CB1 /* CardSectionElement.swift */; }; 48DA2EFE0944E737B0F197B0 /* OHHTTPStubs in Frameworks */ = {isa = PBXBuildFile; productRef = B2AFFAD776D5F21DF837F1BD /* OHHTTPStubs */; }; + 493CA9F12C7E14F90089058D /* ConsumerSession+PaymentMethodType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 493CA9F02C7E14F90089058D /* ConsumerSession+PaymentMethodType.swift */; }; 49803444CD948F1ED28FF021 /* PaymentSheetFormFactory+FormSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD7A1EFF100C589FDFF4D516 /* PaymentSheetFormFactory+FormSpec.swift */; }; 49F62EDF394F18E5BB201D53 /* StripePaymentSheet.h in Headers */ = {isa = PBXBuildFile; fileRef = 7AA6166F234C3A2129CBD573 /* StripePaymentSheet.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4A1A0A542B824C830A200BE0 /* StubbedBackend.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBF8498CCD12A5190F9267CD /* StubbedBackend.swift */; }; @@ -426,6 +427,7 @@ 45B6DC9BD9183495E5649369 /* LinkAccountService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkAccountService.swift; sourceTree = ""; }; 47C5DB8C01BA7137369C8B4D /* TextFieldElement+Card.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TextFieldElement+Card.swift"; sourceTree = ""; }; 492B254E43F3BB9F9CEAEA06 /* PaymentSheetLoaderStubbedTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentSheetLoaderStubbedTest.swift; sourceTree = ""; }; + 493CA9F02C7E14F90089058D /* ConsumerSession+PaymentMethodType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ConsumerSession+PaymentMethodType.swift"; sourceTree = ""; }; 4BEFE8C0CFEAE73F9FD736D3 /* STPStringUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = STPStringUtils.swift; sourceTree = ""; }; 4C6AA41454A6757B3E26AE67 /* StripePaymentSheetTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = StripePaymentSheetTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 4D595AA033BC84CB4E1C277F /* PaymentSheetFormFactorySnapshotTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentSheetFormFactorySnapshotTest.swift; sourceTree = ""; }; @@ -941,6 +943,7 @@ F1E614E8481658A027599A92 /* STPAPIClient+Link.swift */, B662953D2C63F6C2007B6B14 /* PaymentDetailsShareResponse.swift */, 441C3414745D483C9A47ED0B /* VerificationSession.swift */, + 493CA9F02C7E14F90089058D /* ConsumerSession+PaymentMethodType.swift */, ); path = Link; sourceTree = ""; @@ -1788,6 +1791,7 @@ 229A4A578609A3711F02682E /* STPCardBrandChoice.swift in Sources */, 3EDFACA133567159875143C5 /* STPElementsSession.swift in Sources */, 1BFC617EED154D32BFCADAE7 /* SeparatorLabel.swift in Sources */, + 493CA9F12C7E14F90089058D /* ConsumerSession+PaymentMethodType.swift in Sources */, 01D46644D87983FC4387B92C /* InstantDebitsOnlyFinancialConnectionsAuthManager.swift in Sources */, 367BB57FA826A82EEF074A70 /* PayWithLinkWebController.swift in Sources */, F3A34AD1CC2CBB899738C9D7 /* LinkInlineSignupElement.swift in Sources */, diff --git a/StripePaymentSheet/StripePaymentSheet/Source/Helpers/PaymentSheetLinkAccount.swift b/StripePaymentSheet/StripePaymentSheet/Source/Helpers/PaymentSheetLinkAccount.swift index 8fb136865da..2b897066a04 100644 --- a/StripePaymentSheet/StripePaymentSheet/Source/Helpers/PaymentSheetLinkAccount.swift +++ b/StripePaymentSheet/StripePaymentSheet/Source/Helpers/PaymentSheetLinkAccount.swift @@ -166,7 +166,12 @@ class PaymentSheetLinkAccount: PaymentSheetLinkAccountInfoProtocol { } } - func sharePaymentDetails(id: String, cvc: String?, completion: @escaping (Result) -> Void) { + func sharePaymentDetails( + id: String, + cvc: String?, + paymentMethodType: ConsumerSession.PaymentMethodType?, + completion: @escaping (Result) -> Void + ) { guard let session = currentSession else { assertionFailure() return completion( @@ -182,6 +187,7 @@ class PaymentSheetLinkAccount: PaymentSheetLinkAccountInfoProtocol { id: id, cvc: cvc, consumerAccountPublishableKey: publishableKey, + paymentMethodType: paymentMethodType, completion: completionWrapper ) } diff --git a/StripePaymentSheet/StripePaymentSheet/Source/Internal/API Bindings/Link/ConsumerSession+PaymentMethodType.swift b/StripePaymentSheet/StripePaymentSheet/Source/Internal/API Bindings/Link/ConsumerSession+PaymentMethodType.swift new file mode 100644 index 00000000000..74dcfdf02b7 --- /dev/null +++ b/StripePaymentSheet/StripePaymentSheet/Source/Internal/API Bindings/Link/ConsumerSession+PaymentMethodType.swift @@ -0,0 +1,15 @@ +// +// ConsumerSession+PaymentMethodType.swift +// StripePaymentSheet +// +// Created by Mat Schmid on 2024-08-27. +// + +import Foundation + +extension ConsumerSession { + enum PaymentMethodType: String { + case card = "CARD" + case bankAccount = "BANK_ACCOUNT" + } +} diff --git a/StripePaymentSheet/StripePaymentSheet/Source/Internal/API Bindings/Link/ConsumerSession.swift b/StripePaymentSheet/StripePaymentSheet/Source/Internal/API Bindings/Link/ConsumerSession.swift index ee6fb7f5a3d..e6bab3a78c0 100644 --- a/StripePaymentSheet/StripePaymentSheet/Source/Internal/API Bindings/Link/ConsumerSession.swift +++ b/StripePaymentSheet/StripePaymentSheet/Source/Internal/API Bindings/Link/ConsumerSession.swift @@ -129,6 +129,7 @@ extension ConsumerSession { id: String, cvc: String?, consumerAccountPublishableKey: String?, + paymentMethodType: PaymentMethodType?, completion: @escaping (Result) -> Void ) { apiClient.sharePaymentDetails( @@ -136,6 +137,7 @@ extension ConsumerSession { id: id, consumerAccountPublishableKey: consumerAccountPublishableKey, cvc: cvc, + paymentMethodType: paymentMethodType?.rawValue, completion: completion) } diff --git a/StripePaymentSheet/StripePaymentSheet/Source/Internal/API Bindings/Link/PaymentDetails.swift b/StripePaymentSheet/StripePaymentSheet/Source/Internal/API Bindings/Link/PaymentDetails.swift index 20897763728..a88f8dd2252 100644 --- a/StripePaymentSheet/StripePaymentSheet/Source/Internal/API Bindings/Link/PaymentDetails.swift +++ b/StripePaymentSheet/StripePaymentSheet/Source/Internal/API Bindings/Link/PaymentDetails.swift @@ -19,18 +19,28 @@ typealias ConsumerSessionWithPaymentDetails = (session: ConsumerSession, payment For internal SDK use only */ final class ConsumerPaymentDetails: Decodable { + enum PaymentDetailsType: String, Decodable { + case card = "CARD" + case bankAccount = "BANK_ACCOUNT" + case invalid = "PAYMENT_DETAILS_TYPE_INVALID" + } + let stripeID: String + let paymentDetailsType: PaymentDetailsType - init(stripeID: String) { + init(stripeID: String, paymentDetailsType: PaymentDetailsType) { self.stripeID = stripeID + self.paymentDetailsType = paymentDetailsType } private enum CodingKeys: String, CodingKey { case stripeID = "id" + case paymentDetailsType = "type" } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.stripeID = try container.decode(String.self, forKey: .stripeID) + self.paymentDetailsType = try container.decode(PaymentDetailsType.self, forKey: .paymentDetailsType) } } 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 3aa434462ab..0e12b2f694a 100644 --- a/StripePaymentSheet/StripePaymentSheet/Source/Internal/API Bindings/Link/STPAPIClient+Link.swift +++ b/StripePaymentSheet/StripePaymentSheet/Source/Internal/API Bindings/Link/STPAPIClient+Link.swift @@ -200,6 +200,7 @@ extension STPAPIClient { id: String, consumerAccountPublishableKey: String?, cvc: String?, + paymentMethodType: String?, completion: @escaping (Result) -> Void ) { let endpoint: String = "consumers/payment_details/share" @@ -215,6 +216,10 @@ extension STPAPIClient { parameters["payment_method_options"] = ["card": ["cvc": cvc]] } + if let paymentMethodType { + parameters["expected_payment_method_type"] = paymentMethodType + } + APIRequest.post( with: self, endpoint: endpoint, diff --git a/StripePaymentSheet/StripePaymentSheet/Source/Internal/Link/Extensions/Intent+Link.swift b/StripePaymentSheet/StripePaymentSheet/Source/Internal/Link/Extensions/Intent+Link.swift index d3bbf88d194..85909fc3a63 100644 --- a/StripePaymentSheet/StripePaymentSheet/Source/Internal/Link/Extensions/Intent+Link.swift +++ b/StripePaymentSheet/StripePaymentSheet/Source/Internal/Link/Extensions/Intent+Link.swift @@ -26,6 +26,10 @@ extension STPElementsSession { linkSettings?.fundingSources } + var linkMode: LinkSettings.LinkMode? { + linkSettings?.linkMode + } + var disableLinkSignup: Bool { linkSettings?.disableSignup ?? false } diff --git a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentMethodType.swift b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentMethodType.swift index 5944eff8699..b02dd6dd2c9 100644 --- a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentMethodType.swift +++ b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentMethodType.swift @@ -175,6 +175,21 @@ extension PaymentSheet { } } + if + elementsSession.linkFundingSources?.contains(.bankAccount) == true, + !elementsSession.orderedPaymentMethodTypes.contains(.USBankAccount), + elementsSession.linkSettings?.linkMode == .linkCardBrand + { + let availabilityStatus = configurationSatisfiesRequirements( + requirements: [.financialConnectionsSDK], + configuration: configuration, + intent: intent + ) + if availabilityStatus == .supported { + recommendedPaymentMethodTypes.append(.stripe(.linkCardBrand)) + } + } + if let merchantPaymentMethodOrder = configuration.paymentMethodOrder?.map({ $0.lowercased() }) { // Order the payment methods according to the merchant's `paymentMethodOrder` configuration: var reorderedPaymentMethodTypes = [PaymentMethodType]() @@ -196,8 +211,10 @@ extension PaymentSheet { } // 3. Append the remaining PMs in recommendedPaymentMethodTypes reorderedPaymentMethodTypes.append(contentsOf: recommendedPaymentMethodTypes) + print("**** reorderedPaymentMethodTypes", reorderedPaymentMethodTypes) return reorderedPaymentMethodTypes } else { + print("**** recommendedPaymentMethodTypes", recommendedPaymentMethodTypes) return recommendedPaymentMethodTypes } } @@ -237,9 +254,9 @@ extension PaymentSheet { case .bacsDebit: return [.returnURL, .userSupportsDelayedPaymentMethods] case .cardPresent, .blik, .weChatPay, .grabPay, .FPX, .giropay, .przelewy24, .EPS, - .netBanking, .OXXO, .afterpayClearpay, .UPI, .link, .affirm, .paynow, .zip, .alma, - .mobilePay, .unknown, .alipay, .konbini, .promptPay, .swish, .twint, .multibanco, - .sunbit, .billie, .satispay: + .netBanking, .OXXO, .afterpayClearpay, .UPI, .link, .linkCardBrand, .affirm, .paynow, + .zip, .alma, .mobilePay, .unknown, .alipay, .konbini, .promptPay, .swish, .twint, + .multibanco, .sunbit, .billie, .satispay: return [.unsupportedForSetup] @unknown default: return [.unsupportedForSetup] @@ -254,7 +271,7 @@ extension PaymentSheet { .bancontact, .iDEAL, .cashApp, .affirm, .zip, .revolutPay, .amazonPay, .alma, .mobilePay, .swish, .twint, .sunbit, .billie, .satispay: return [.returnURL] - case .USBankAccount: + case .USBankAccount, .linkCardBrand: return [ .userSupportsDelayedPaymentMethods, .financialConnectionsSDK, .validUSBankVerificationMethod, diff --git a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentOption+Images.swift b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentOption+Images.swift index f33cac64f86..1cd60b2dcbd 100644 --- a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentOption+Images.swift +++ b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentOption+Images.swift @@ -186,6 +186,8 @@ extension STPPaymentMethodType { return .pm_type_aubecsdebit case .USBankAccount: return .pm_type_us_bank + case .linkCardBrand: + return .pm_type_us_bank case .UPI: return .pm_type_upi case .cashApp: diff --git a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentSheet+API.swift b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentSheet+API.swift index a1db37b5304..2df558fda2d 100644 --- a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentSheet+API.swift +++ b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentSheet+API.swift @@ -410,8 +410,16 @@ extension PaymentSheet { switch result { case .success(let paymentDetails): if elementsSession.linkPassthroughModeEnabled { + let paymentMethodType = Self.getExpectedPaymentMethodType( + paymentMethodType: paymentDetails.paymentDetailsType, + linkMode: elementsSession.linkMode + ) // If passthrough mode, share payment details - linkAccount.sharePaymentDetails(id: paymentDetails.stripeID, cvc: paymentMethodParams.card?.cvc) { result in + linkAccount.sharePaymentDetails( + id: paymentDetails.stripeID, + cvc: paymentMethodParams.card?.cvc, + paymentMethodType: paymentMethodType + ) { result in switch result { case .success(let paymentDetailsShareResponse): confirmWithPaymentMethod(paymentDetailsShareResponse.paymentMethod, linkAccount, shouldSave) @@ -633,6 +641,18 @@ extension PaymentSheet { params.returnURL = configuration.returnURL return params } + + static func getExpectedPaymentMethodType( + paymentMethodType: ConsumerPaymentDetails.PaymentDetailsType?, + linkMode: LinkSettings.LinkMode? + ) -> ConsumerSession.PaymentMethodType? { + switch paymentMethodType { + case .card: .card + case .bankAccount: linkMode == .linkCardBrand ? .card : .bankAccount + case .invalid: nil + case .none: nil + } + } } /// A helper method to compare shipping details diff --git a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentSheetFormFactory/PaymentSheetFormFactory.swift b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentSheetFormFactory/PaymentSheetFormFactory.swift index 3bac4088ed0..0b2c3ab5dd0 100644 --- a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentSheetFormFactory/PaymentSheetFormFactory.swift +++ b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentSheetFormFactory/PaymentSheetFormFactory.swift @@ -177,6 +177,8 @@ class PaymentSheetFormFactory { return makeBoleto() } else if paymentMethod == .swish { return makeSwish() + } else if paymentMethod == .linkCardBrand { + return makeInstantDebits() } guard let spec = FormSpecProvider.shared.formSpec(for: paymentMethod.identifier) else { diff --git a/StripePayments/StripePayments.xcodeproj/project.pbxproj b/StripePayments/StripePayments.xcodeproj/project.pbxproj index 54893f5cc3f..ed139db1c4d 100644 --- a/StripePayments/StripePayments.xcodeproj/project.pbxproj +++ b/StripePayments/StripePayments.xcodeproj/project.pbxproj @@ -99,6 +99,7 @@ 457AB5EDA3C13FF9FE9FBD9A /* STPBlocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01AD8DAB423F22BE6FC2FFCB /* STPBlocks.swift */; }; 466FD050408B0678FE5B60FC /* STPPaymentIntentEnums.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BD1E8C2A561AF7E6CDFCD97 /* STPPaymentIntentEnums.swift */; }; 47677A8DE9E3D36E3392F7DE /* STPPaymentMethodAlipay.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBF5E4108B8BF1C30BECC326 /* STPPaymentMethodAlipay.swift */; }; + 493CA9F32C7E23B70089058D /* STPPaymentMethodLinkCardBrandParams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 493CA9F22C7E23B70089058D /* STPPaymentMethodLinkCardBrandParams.swift */; }; 4A93430C40F5C73BC76447B2 /* StripePaymentsBundleLocator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 468FAF1D4D81DB5F5854287B /* StripePaymentsBundleLocator.swift */; }; 4C7486A126100F8F067F248A /* STPPaymentMethodGiropay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 444BEF2E8A83B5B489062595 /* STPPaymentMethodGiropay.swift */; }; 4C9A78FBE900C635CC3AA400 /* STPThreeDSUICustomization.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07382674619AEF657397B21 /* STPThreeDSUICustomization.swift */; }; @@ -478,6 +479,7 @@ 48A73DA27ABEC6115513F315 /* STPPaymentMethodNetBanking.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = STPPaymentMethodNetBanking.swift; sourceTree = ""; }; 48EE0DEDAB9A8BFF0523FF9F /* STPPaymentMethodBLIKParams.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = STPPaymentMethodBLIKParams.swift; sourceTree = ""; }; 48F636D8CADF68EB74B88313 /* STPFixtures+Swift.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "STPFixtures+Swift.swift"; sourceTree = ""; }; + 493CA9F22C7E23B70089058D /* STPPaymentMethodLinkCardBrandParams.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = STPPaymentMethodLinkCardBrandParams.swift; sourceTree = ""; }; 4C3628526271ABA16C3994EE /* STPConnectAccountAddress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = STPConnectAccountAddress.swift; sourceTree = ""; }; 4D99D2C6C436F6FAFB3960EE /* STPPaymentMethodBancontactParams.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = STPPaymentMethodBancontactParams.swift; sourceTree = ""; }; 4FED7EC426D937D69C4B2D3B /* STPCollectBankAccountParams.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = STPCollectBankAccountParams.swift; sourceTree = ""; }; @@ -1267,6 +1269,7 @@ B9F83C7EDA9903D041E4C001 /* STPPaymentMethodKlarnaParams.swift */, E99B164CDD13E8F5C9CAD349 /* STPPaymentMethodLink.swift */, 3E397BCD2C450E4770A278DA /* STPPaymentMethodLinkParams.swift */, + 493CA9F22C7E23B70089058D /* STPPaymentMethodLinkCardBrandParams.swift */, 6BA4B9152BF3E0C900D1F21D /* STPPaymentMethodMobilePay.swift */, 92A9741EA9B8FD4301DEB5FD /* STPPaymentMethodMobilePayParams.swift */, 48A73DA27ABEC6115513F315 /* STPPaymentMethodNetBanking.swift */, @@ -1773,6 +1776,7 @@ 15867BBB56A73E740FE5CC4E /* STPCustomer.swift in Sources */, EA3D613104589DA964C35073 /* STPFPXBankBrand.swift in Sources */, 141AA8A68D0A8C385704CD7A /* STPFile.swift in Sources */, + 493CA9F32C7E23B70089058D /* STPPaymentMethodLinkCardBrandParams.swift in Sources */, 61152B4B2B865F58003B69A0 /* STPPaymentMethodAmazonPayParams.swift in Sources */, B0FB058E602F6F63F2E00030 /* STPFormEncodable.swift in Sources */, 59F5403292D00A4B9C335485 /* STPIssuingCardPin.swift in Sources */, diff --git a/StripePayments/StripePayments/Source/API Bindings/Models/PaymentMethods/STPPaymentMethodEnums.swift b/StripePayments/StripePayments/Source/API Bindings/Models/PaymentMethods/STPPaymentMethodEnums.swift index 63723a32a20..81dd5829df2 100644 --- a/StripePayments/StripePayments/Source/API Bindings/Models/PaymentMethods/STPPaymentMethodEnums.swift +++ b/StripePayments/StripePayments/Source/API Bindings/Models/PaymentMethods/STPPaymentMethodEnums.swift @@ -58,6 +58,8 @@ import Foundation case boleto /// A Link payment method case link + /// A Link-branded card payment method. + case linkCardBrand /// A Klarna payment method. case klarna /// An Affirm payment method @@ -145,6 +147,8 @@ import Foundation return STPLocalizedString("Boleto", "Payment Method type brand name") case .link: return STPLocalizedString("Link", "Link Payment Method type brand name") + case .linkCardBrand: + return STPLocalizedString("Bank", "Link Card Bank Payment Method type brand name") case .klarna: return STPLocalizedString("Klarna", "Payment Method type brand name") case .affirm: @@ -243,6 +247,8 @@ import Foundation return "boleto" case .link: return "link" + case .linkCardBrand: + return "link_card_brand" case .klarna: return "klarna" case .affirm: diff --git a/StripePayments/StripePayments/Source/API Bindings/Models/PaymentMethods/STPPaymentMethodParams.swift b/StripePayments/StripePayments/Source/API Bindings/Models/PaymentMethods/STPPaymentMethodParams.swift index a64ba13b12f..22911be7186 100644 --- a/StripePayments/StripePayments/Source/API Bindings/Models/PaymentMethods/STPPaymentMethodParams.swift +++ b/StripePayments/StripePayments/Source/API Bindings/Models/PaymentMethods/STPPaymentMethodParams.swift @@ -82,6 +82,8 @@ public class STPPaymentMethodParams: NSObject, STPFormEncodable { @objc public var boleto: STPPaymentMethodBoletoParams? /// If this is a Link PaymentMethod, this contains additional details @objc public var link: STPPaymentMethodLinkParams? + /// If this is a Link Card Brand PaymentMethod, this contains additional details + @objc public var linkCardBrand: STPPaymentMethodLinkCardBrandParams? /// If this is an Klarna PaymentMethod, this contains additional details. @objc public var klarna: STPPaymentMethodKlarnaParams? /// If this is an Affirm PaymentMethod, this contains additional details. @@ -807,6 +809,7 @@ public class STPPaymentMethodParams: NSObject, STPFormEncodable { .blik, .weChatPay, .link, + .linkCardBrand, .USBankAccount, .cashApp, .revolutPay, @@ -1309,6 +1312,8 @@ extension STPPaymentMethodParams { boleto = STPPaymentMethodBoletoParams() case .link: link = STPPaymentMethodLinkParams() + case .linkCardBrand: + linkCardBrand = STPPaymentMethodLinkCardBrandParams() case .klarna: klarna = STPPaymentMethodKlarnaParams() case .affirm: @@ -1363,7 +1368,7 @@ extension STPPaymentMethodParams { } else { return "FPX" } - case .paynow, .zip, .amazonPay, .alma, .mobilePay, .konbini, .promptPay, .swish, .sunbit, .billie, .satispay, .iDEAL, .SEPADebit, .bacsDebit, .AUBECSDebit, .giropay, .przelewy24, .EPS, .bancontact, .netBanking, .OXXO, .sofort, .UPI, .grabPay, .payPal, .afterpayClearpay, .blik, .weChatPay, .boleto, .link, .klarna, .affirm, .USBankAccount, .cashApp, .revolutPay, .twint, .multibanco, .alipay, .cardPresent, .unknown: + case .paynow, .zip, .amazonPay, .alma, .mobilePay, .konbini, .promptPay, .swish, .sunbit, .billie, .satispay, .iDEAL, .SEPADebit, .bacsDebit, .AUBECSDebit, .giropay, .przelewy24, .EPS, .bancontact, .netBanking, .OXXO, .sofort, .UPI, .grabPay, .payPal, .afterpayClearpay, .blik, .weChatPay, .boleto, .link, .linkCardBrand, .klarna, .affirm, .USBankAccount, .cashApp, .revolutPay, .twint, .multibanco, .alipay, .cardPresent, .unknown: // Use the label already defined in STPPaymentMethodType; the params object for these types don't contain additional information that affect the display label (like cards do) return type.displayName @unknown default: diff --git a/StripePayments/StripePayments/Source/API Bindings/Models/PaymentMethods/Types/STPPaymentMethodLinkCardBrandParams.swift b/StripePayments/StripePayments/Source/API Bindings/Models/PaymentMethods/Types/STPPaymentMethodLinkCardBrandParams.swift new file mode 100644 index 00000000000..aeebff4c324 --- /dev/null +++ b/StripePayments/StripePayments/Source/API Bindings/Models/PaymentMethods/Types/STPPaymentMethodLinkCardBrandParams.swift @@ -0,0 +1,22 @@ +// +// STPPaymentMethodLinkCardBrandParams.swift +// StripePayments +// +// Created by Mat Schmid on 2024-08-27. +// + +import Foundation + +public class STPPaymentMethodLinkCardBrandParams: NSObject, STPFormEncodable { + public var additionalAPIParameters: [AnyHashable: Any] = [:] + + // MARK: - STPFormEncodable + @objc + public class func rootObjectName() -> String? { + "link_card_brand" + } + + public static func propertyNamesToFormFieldNamesMapping() -> [String: String] { + [:] + } +} diff --git a/StripePayments/StripePayments/Source/API Bindings/Models/Shared/LinkSettings.swift b/StripePayments/StripePayments/Source/API Bindings/Models/Shared/LinkSettings.swift index d9aa199eda6..9f98bd845ad 100644 --- a/StripePayments/StripePayments/Source/API Bindings/Models/Shared/LinkSettings.swift +++ b/StripePayments/StripePayments/Source/API Bindings/Models/Shared/LinkSettings.swift @@ -21,10 +21,17 @@ import Foundation case ephemeral } + @_spi(STP) @frozen public enum LinkMode: String { + case linkPaymentMethod = "LINK_PAYMENT_METHOD" + case passthrough = "PASSTHROUGH" + case linkCardBrand = "LINK_CARD_BRAND" + } + @_spi(STP) public let fundingSources: Set @_spi(STP) public let popupWebviewOption: PopupWebviewOption? @_spi(STP) public let passthroughModeEnabled: Bool? @_spi(STP) public let disableSignup: Bool? + @_spi(STP) public let linkMode: LinkMode? @_spi(STP) public let linkFlags: [String: Bool]? @_spi(STP) public let allResponseFields: [AnyHashable: Any] @@ -34,6 +41,7 @@ import Foundation popupWebviewOption: PopupWebviewOption?, passthroughModeEnabled: Bool?, disableSignup: Bool?, + linkMode: LinkMode?, linkFlags: [String: Bool]?, allResponseFields: [AnyHashable: Any] ) { @@ -41,6 +49,7 @@ import Foundation self.popupWebviewOption = popupWebviewOption self.passthroughModeEnabled = passthroughModeEnabled self.disableSignup = disableSignup + self.linkMode = linkMode self.linkFlags = linkFlags self.allResponseFields = allResponseFields } @@ -61,6 +70,7 @@ import Foundation let webviewOption = PopupWebviewOption(rawValue: response["link_popup_webview_option"] as? String ?? "") let passthroughModeEnabled = response["link_passthrough_mode_enabled"] as? Bool ?? false let disableSignup = response["link_mobile_disable_signup"] as? Bool ?? false + let linkMode = (response["link_mode"] as? String).flatMap { LinkMode(rawValue: $0) } // Collect the flags for the URL generator let linkFlags = response.reduce(into: [String: Bool]()) { partialResult, element in @@ -74,6 +84,7 @@ import Foundation popupWebviewOption: webviewOption, passthroughModeEnabled: passthroughModeEnabled, disableSignup: disableSignup, + linkMode: linkMode, linkFlags: linkFlags, allResponseFields: response ) as? Self diff --git a/StripePayments/StripePayments/Source/Categories/Enums+CustomStringConvertible.swift b/StripePayments/StripePayments/Source/Categories/Enums+CustomStringConvertible.swift index 53dad7c9065..a8744489141 100644 --- a/StripePayments/StripePayments/Source/Categories/Enums+CustomStringConvertible.swift +++ b/StripePayments/StripePayments/Source/Categories/Enums+CustomStringConvertible.swift @@ -505,6 +505,8 @@ extension STPPaymentMethodType: CustomStringConvertible { return "klarna" case .link: return "link" + case .linkCardBrand: + return "linkCardBrand" case .netBanking: return "netBanking" case .payPal: diff --git a/StripePayments/StripePayments/Source/PaymentHandler/STPPaymentHandler.swift b/StripePayments/StripePayments/Source/PaymentHandler/STPPaymentHandler.swift index 1ad153ef875..37030c4ccb6 100644 --- a/StripePayments/StripePayments/Source/PaymentHandler/STPPaymentHandler.swift +++ b/StripePayments/StripePayments/Source/PaymentHandler/STPPaymentHandler.swift @@ -723,6 +723,7 @@ public class STPPaymentHandler: NSObject { .weChatPay, .boleto, .link, + .linkCardBrand, .klarna, .affirm, .cashApp, From 47ee6f657445d77c7769ad85f4bde27151ffe945 Mon Sep 17 00:00:00 2001 From: Mat Schmid Date: Wed, 28 Aug 2024 15:23:07 -0400 Subject: [PATCH 2/5] Switch .linkCardBrand from STPPaymentMethod to PaymentMethodType --- .../PaymentSheet/IntentConfirmParams.swift | 3 +++ .../PaymentSheet/PaymentMethodType.swift | 17 +++++++++----- .../PaymentSheet/PaymentOption+Images.swift | 2 -- .../PaymentSheetFormFactory.swift | 4 +--- .../PaymentSheetVerticalViewController.swift | 2 +- .../StripePayments.xcodeproj/project.pbxproj | 4 ---- .../STPPaymentMethodEnums.swift | 6 ----- .../STPPaymentMethodParams.swift | 7 +----- .../STPPaymentMethodLinkCardBrandParams.swift | 22 ------------------- .../Enums+CustomStringConvertible.swift | 2 -- .../PaymentHandler/STPPaymentHandler.swift | 1 - 11 files changed, 17 insertions(+), 53 deletions(-) delete mode 100644 StripePayments/StripePayments/Source/API Bindings/Models/PaymentMethods/Types/STPPaymentMethodLinkCardBrandParams.swift diff --git a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/IntentConfirmParams.swift b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/IntentConfirmParams.swift index 4cc9578d2f8..02363f27af7 100644 --- a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/IntentConfirmParams.swift +++ b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/IntentConfirmParams.swift @@ -69,6 +69,9 @@ class IntentConfirmParams { case .instantDebits: let params = STPPaymentMethodParams(type: .link) self.init(params: params, type: type) + case .linkCardBrand: + let params = STPPaymentMethodParams(type: .card) + self.init(params: params, type: type) } } diff --git a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentMethodType.swift b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentMethodType.swift index b02dd6dd2c9..d536c712d88 100644 --- a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentMethodType.swift +++ b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentMethodType.swift @@ -16,7 +16,10 @@ extension PaymentSheet { enum PaymentMethodType: Equatable, Hashable { case stripe(STPPaymentMethodType) case external(ExternalPaymentMethod) + + // Synthetic payment methods: case instantDebits + case linkCardBrand static var analyticLogForIcon: Set = [] static let analyticLogForIconSemaphore = DispatchSemaphore(value: 1) @@ -27,7 +30,7 @@ extension PaymentSheet { return paymentMethodType.displayName case .external(let externalPaymentMethod): return externalPaymentMethod.label - case .instantDebits: + case .instantDebits, .linkCardBrand: return String.Localized.bank } } @@ -42,6 +45,8 @@ extension PaymentSheet { return externalPaymentMethod.type case .instantDebits: return "instant_debits" + case .linkCardBrand: + return "link_card_bank" } } @@ -103,7 +108,7 @@ extension PaymentSheet { } return DownloadManager.sharedManager.imagePlaceHolder() } - case .instantDebits: + case .instantDebits, .linkCardBrand: return Image.pm_type_us_bank.makeImage(overrideUserInterfaceStyle: forDarkBackground ? .dark : .light) } } @@ -114,7 +119,7 @@ extension PaymentSheet { return stpPaymentMethodType.iconRequiresTinting case .external: return false - case .instantDebits: + case .instantDebits, .linkCardBrand: return true } } @@ -186,7 +191,7 @@ extension PaymentSheet { intent: intent ) if availabilityStatus == .supported { - recommendedPaymentMethodTypes.append(.stripe(.linkCardBrand)) + recommendedPaymentMethodTypes.append(.linkCardBrand) } } @@ -254,7 +259,7 @@ extension PaymentSheet { case .bacsDebit: return [.returnURL, .userSupportsDelayedPaymentMethods] case .cardPresent, .blik, .weChatPay, .grabPay, .FPX, .giropay, .przelewy24, .EPS, - .netBanking, .OXXO, .afterpayClearpay, .UPI, .link, .linkCardBrand, .affirm, .paynow, + .netBanking, .OXXO, .afterpayClearpay, .UPI, .link, .affirm, .paynow, .zip, .alma, .mobilePay, .unknown, .alipay, .konbini, .promptPay, .swish, .twint, .multibanco, .sunbit, .billie, .satispay: return [.unsupportedForSetup] @@ -271,7 +276,7 @@ extension PaymentSheet { .bancontact, .iDEAL, .cashApp, .affirm, .zip, .revolutPay, .amazonPay, .alma, .mobilePay, .swish, .twint, .sunbit, .billie, .satispay: return [.returnURL] - case .USBankAccount, .linkCardBrand: + case .USBankAccount: return [ .userSupportsDelayedPaymentMethods, .financialConnectionsSDK, .validUSBankVerificationMethod, diff --git a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentOption+Images.swift b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentOption+Images.swift index 1cd60b2dcbd..f33cac64f86 100644 --- a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentOption+Images.swift +++ b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentOption+Images.swift @@ -186,8 +186,6 @@ extension STPPaymentMethodType { return .pm_type_aubecsdebit case .USBankAccount: return .pm_type_us_bank - case .linkCardBrand: - return .pm_type_us_bank case .UPI: return .pm_type_upi case .cashApp: diff --git a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentSheetFormFactory/PaymentSheetFormFactory.swift b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentSheetFormFactory/PaymentSheetFormFactory.swift index 0b2c3ab5dd0..806a591d815 100644 --- a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentSheetFormFactory/PaymentSheetFormFactory.swift +++ b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentSheetFormFactory/PaymentSheetFormFactory.swift @@ -133,7 +133,7 @@ class PaymentSheetFormFactory { func make() -> PaymentMethodElement { switch paymentMethod { - case .instantDebits: + case .instantDebits, .linkCardBrand: return makeInstantDebits() case .external: return makeExternalPaymentMethodForm() @@ -177,8 +177,6 @@ class PaymentSheetFormFactory { return makeBoleto() } else if paymentMethod == .swish { return makeSwish() - } else if paymentMethod == .linkCardBrand { - return makeInstantDebits() } guard let spec = FormSpecProvider.shared.formSpec(for: paymentMethod.identifier) else { diff --git a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/ViewControllers/PaymentSheetVerticalViewController.swift b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/ViewControllers/PaymentSheetVerticalViewController.swift index 9bee755eeb8..effbd35b398 100644 --- a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/ViewControllers/PaymentSheetVerticalViewController.swift +++ b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/ViewControllers/PaymentSheetVerticalViewController.swift @@ -37,7 +37,7 @@ class PaymentSheetVerticalViewController: UIViewController, FlowControllerViewCo return .new(confirmParams: params) case .external(let type): return .external(paymentMethod: type, billingDetails: params.paymentMethodParams.nonnil_billingDetails) - case .instantDebits: + case .instantDebits, .linkCardBrand: return .new(confirmParams: params) } case .saved(paymentMethod: let paymentMethod): diff --git a/StripePayments/StripePayments.xcodeproj/project.pbxproj b/StripePayments/StripePayments.xcodeproj/project.pbxproj index ed139db1c4d..54893f5cc3f 100644 --- a/StripePayments/StripePayments.xcodeproj/project.pbxproj +++ b/StripePayments/StripePayments.xcodeproj/project.pbxproj @@ -99,7 +99,6 @@ 457AB5EDA3C13FF9FE9FBD9A /* STPBlocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01AD8DAB423F22BE6FC2FFCB /* STPBlocks.swift */; }; 466FD050408B0678FE5B60FC /* STPPaymentIntentEnums.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BD1E8C2A561AF7E6CDFCD97 /* STPPaymentIntentEnums.swift */; }; 47677A8DE9E3D36E3392F7DE /* STPPaymentMethodAlipay.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBF5E4108B8BF1C30BECC326 /* STPPaymentMethodAlipay.swift */; }; - 493CA9F32C7E23B70089058D /* STPPaymentMethodLinkCardBrandParams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 493CA9F22C7E23B70089058D /* STPPaymentMethodLinkCardBrandParams.swift */; }; 4A93430C40F5C73BC76447B2 /* StripePaymentsBundleLocator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 468FAF1D4D81DB5F5854287B /* StripePaymentsBundleLocator.swift */; }; 4C7486A126100F8F067F248A /* STPPaymentMethodGiropay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 444BEF2E8A83B5B489062595 /* STPPaymentMethodGiropay.swift */; }; 4C9A78FBE900C635CC3AA400 /* STPThreeDSUICustomization.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07382674619AEF657397B21 /* STPThreeDSUICustomization.swift */; }; @@ -479,7 +478,6 @@ 48A73DA27ABEC6115513F315 /* STPPaymentMethodNetBanking.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = STPPaymentMethodNetBanking.swift; sourceTree = ""; }; 48EE0DEDAB9A8BFF0523FF9F /* STPPaymentMethodBLIKParams.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = STPPaymentMethodBLIKParams.swift; sourceTree = ""; }; 48F636D8CADF68EB74B88313 /* STPFixtures+Swift.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "STPFixtures+Swift.swift"; sourceTree = ""; }; - 493CA9F22C7E23B70089058D /* STPPaymentMethodLinkCardBrandParams.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = STPPaymentMethodLinkCardBrandParams.swift; sourceTree = ""; }; 4C3628526271ABA16C3994EE /* STPConnectAccountAddress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = STPConnectAccountAddress.swift; sourceTree = ""; }; 4D99D2C6C436F6FAFB3960EE /* STPPaymentMethodBancontactParams.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = STPPaymentMethodBancontactParams.swift; sourceTree = ""; }; 4FED7EC426D937D69C4B2D3B /* STPCollectBankAccountParams.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = STPCollectBankAccountParams.swift; sourceTree = ""; }; @@ -1269,7 +1267,6 @@ B9F83C7EDA9903D041E4C001 /* STPPaymentMethodKlarnaParams.swift */, E99B164CDD13E8F5C9CAD349 /* STPPaymentMethodLink.swift */, 3E397BCD2C450E4770A278DA /* STPPaymentMethodLinkParams.swift */, - 493CA9F22C7E23B70089058D /* STPPaymentMethodLinkCardBrandParams.swift */, 6BA4B9152BF3E0C900D1F21D /* STPPaymentMethodMobilePay.swift */, 92A9741EA9B8FD4301DEB5FD /* STPPaymentMethodMobilePayParams.swift */, 48A73DA27ABEC6115513F315 /* STPPaymentMethodNetBanking.swift */, @@ -1776,7 +1773,6 @@ 15867BBB56A73E740FE5CC4E /* STPCustomer.swift in Sources */, EA3D613104589DA964C35073 /* STPFPXBankBrand.swift in Sources */, 141AA8A68D0A8C385704CD7A /* STPFile.swift in Sources */, - 493CA9F32C7E23B70089058D /* STPPaymentMethodLinkCardBrandParams.swift in Sources */, 61152B4B2B865F58003B69A0 /* STPPaymentMethodAmazonPayParams.swift in Sources */, B0FB058E602F6F63F2E00030 /* STPFormEncodable.swift in Sources */, 59F5403292D00A4B9C335485 /* STPIssuingCardPin.swift in Sources */, diff --git a/StripePayments/StripePayments/Source/API Bindings/Models/PaymentMethods/STPPaymentMethodEnums.swift b/StripePayments/StripePayments/Source/API Bindings/Models/PaymentMethods/STPPaymentMethodEnums.swift index 81dd5829df2..63723a32a20 100644 --- a/StripePayments/StripePayments/Source/API Bindings/Models/PaymentMethods/STPPaymentMethodEnums.swift +++ b/StripePayments/StripePayments/Source/API Bindings/Models/PaymentMethods/STPPaymentMethodEnums.swift @@ -58,8 +58,6 @@ import Foundation case boleto /// A Link payment method case link - /// A Link-branded card payment method. - case linkCardBrand /// A Klarna payment method. case klarna /// An Affirm payment method @@ -147,8 +145,6 @@ import Foundation return STPLocalizedString("Boleto", "Payment Method type brand name") case .link: return STPLocalizedString("Link", "Link Payment Method type brand name") - case .linkCardBrand: - return STPLocalizedString("Bank", "Link Card Bank Payment Method type brand name") case .klarna: return STPLocalizedString("Klarna", "Payment Method type brand name") case .affirm: @@ -247,8 +243,6 @@ import Foundation return "boleto" case .link: return "link" - case .linkCardBrand: - return "link_card_brand" case .klarna: return "klarna" case .affirm: diff --git a/StripePayments/StripePayments/Source/API Bindings/Models/PaymentMethods/STPPaymentMethodParams.swift b/StripePayments/StripePayments/Source/API Bindings/Models/PaymentMethods/STPPaymentMethodParams.swift index 22911be7186..a64ba13b12f 100644 --- a/StripePayments/StripePayments/Source/API Bindings/Models/PaymentMethods/STPPaymentMethodParams.swift +++ b/StripePayments/StripePayments/Source/API Bindings/Models/PaymentMethods/STPPaymentMethodParams.swift @@ -82,8 +82,6 @@ public class STPPaymentMethodParams: NSObject, STPFormEncodable { @objc public var boleto: STPPaymentMethodBoletoParams? /// If this is a Link PaymentMethod, this contains additional details @objc public var link: STPPaymentMethodLinkParams? - /// If this is a Link Card Brand PaymentMethod, this contains additional details - @objc public var linkCardBrand: STPPaymentMethodLinkCardBrandParams? /// If this is an Klarna PaymentMethod, this contains additional details. @objc public var klarna: STPPaymentMethodKlarnaParams? /// If this is an Affirm PaymentMethod, this contains additional details. @@ -809,7 +807,6 @@ public class STPPaymentMethodParams: NSObject, STPFormEncodable { .blik, .weChatPay, .link, - .linkCardBrand, .USBankAccount, .cashApp, .revolutPay, @@ -1312,8 +1309,6 @@ extension STPPaymentMethodParams { boleto = STPPaymentMethodBoletoParams() case .link: link = STPPaymentMethodLinkParams() - case .linkCardBrand: - linkCardBrand = STPPaymentMethodLinkCardBrandParams() case .klarna: klarna = STPPaymentMethodKlarnaParams() case .affirm: @@ -1368,7 +1363,7 @@ extension STPPaymentMethodParams { } else { return "FPX" } - case .paynow, .zip, .amazonPay, .alma, .mobilePay, .konbini, .promptPay, .swish, .sunbit, .billie, .satispay, .iDEAL, .SEPADebit, .bacsDebit, .AUBECSDebit, .giropay, .przelewy24, .EPS, .bancontact, .netBanking, .OXXO, .sofort, .UPI, .grabPay, .payPal, .afterpayClearpay, .blik, .weChatPay, .boleto, .link, .linkCardBrand, .klarna, .affirm, .USBankAccount, .cashApp, .revolutPay, .twint, .multibanco, .alipay, .cardPresent, .unknown: + case .paynow, .zip, .amazonPay, .alma, .mobilePay, .konbini, .promptPay, .swish, .sunbit, .billie, .satispay, .iDEAL, .SEPADebit, .bacsDebit, .AUBECSDebit, .giropay, .przelewy24, .EPS, .bancontact, .netBanking, .OXXO, .sofort, .UPI, .grabPay, .payPal, .afterpayClearpay, .blik, .weChatPay, .boleto, .link, .klarna, .affirm, .USBankAccount, .cashApp, .revolutPay, .twint, .multibanco, .alipay, .cardPresent, .unknown: // Use the label already defined in STPPaymentMethodType; the params object for these types don't contain additional information that affect the display label (like cards do) return type.displayName @unknown default: diff --git a/StripePayments/StripePayments/Source/API Bindings/Models/PaymentMethods/Types/STPPaymentMethodLinkCardBrandParams.swift b/StripePayments/StripePayments/Source/API Bindings/Models/PaymentMethods/Types/STPPaymentMethodLinkCardBrandParams.swift deleted file mode 100644 index aeebff4c324..00000000000 --- a/StripePayments/StripePayments/Source/API Bindings/Models/PaymentMethods/Types/STPPaymentMethodLinkCardBrandParams.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// STPPaymentMethodLinkCardBrandParams.swift -// StripePayments -// -// Created by Mat Schmid on 2024-08-27. -// - -import Foundation - -public class STPPaymentMethodLinkCardBrandParams: NSObject, STPFormEncodable { - public var additionalAPIParameters: [AnyHashable: Any] = [:] - - // MARK: - STPFormEncodable - @objc - public class func rootObjectName() -> String? { - "link_card_brand" - } - - public static func propertyNamesToFormFieldNamesMapping() -> [String: String] { - [:] - } -} diff --git a/StripePayments/StripePayments/Source/Categories/Enums+CustomStringConvertible.swift b/StripePayments/StripePayments/Source/Categories/Enums+CustomStringConvertible.swift index a8744489141..53dad7c9065 100644 --- a/StripePayments/StripePayments/Source/Categories/Enums+CustomStringConvertible.swift +++ b/StripePayments/StripePayments/Source/Categories/Enums+CustomStringConvertible.swift @@ -505,8 +505,6 @@ extension STPPaymentMethodType: CustomStringConvertible { return "klarna" case .link: return "link" - case .linkCardBrand: - return "linkCardBrand" case .netBanking: return "netBanking" case .payPal: diff --git a/StripePayments/StripePayments/Source/PaymentHandler/STPPaymentHandler.swift b/StripePayments/StripePayments/Source/PaymentHandler/STPPaymentHandler.swift index 37030c4ccb6..1ad153ef875 100644 --- a/StripePayments/StripePayments/Source/PaymentHandler/STPPaymentHandler.swift +++ b/StripePayments/StripePayments/Source/PaymentHandler/STPPaymentHandler.swift @@ -723,7 +723,6 @@ public class STPPaymentHandler: NSObject { .weChatPay, .boleto, .link, - .linkCardBrand, .klarna, .affirm, .cashApp, From 444172d43b6e3a683bde0c6e2ed43cacd24c2343 Mon Sep 17 00:00:00 2001 From: Mat Schmid Date: Tue, 3 Sep 2024 15:37:07 -0400 Subject: [PATCH 3/5] Dedupe bank payment methods --- .../Source/PaymentSheet/PaymentMethodType.swift | 4 +--- .../PaymentSheetFormFactory.swift | 9 ++++++--- .../InstantDebitsPaymentMethodElement.swift | 15 ++++++++++----- .../API Bindings/Models/Shared/LinkSettings.swift | 14 ++++++++------ 4 files changed, 25 insertions(+), 17 deletions(-) diff --git a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentMethodType.swift b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentMethodType.swift index d536c712d88..36404737474 100644 --- a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentMethodType.swift +++ b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentMethodType.swift @@ -178,9 +178,7 @@ extension PaymentSheet { if availabilityStatus == .supported { recommendedPaymentMethodTypes.append(.instantDebits) } - } - - if + } else if elementsSession.linkFundingSources?.contains(.bankAccount) == true, !elementsSession.orderedPaymentMethodTypes.contains(.USBankAccount), elementsSession.linkSettings?.linkMode == .linkCardBrand diff --git a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentSheetFormFactory/PaymentSheetFormFactory.swift b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentSheetFormFactory/PaymentSheetFormFactory.swift index 806a591d815..02abc8b87cd 100644 --- a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentSheetFormFactory/PaymentSheetFormFactory.swift +++ b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentSheetFormFactory/PaymentSheetFormFactory.swift @@ -133,8 +133,10 @@ class PaymentSheetFormFactory { func make() -> PaymentMethodElement { switch paymentMethod { - case .instantDebits, .linkCardBrand: - return makeInstantDebits() + case .instantDebits: + return makeInstantDebits(requireLinkedBank: true) + case .linkCardBrand: + return makeInstantDebits(requireLinkedBank: false) case .external: return makeExternalPaymentMethodForm() case .stripe(let paymentMethod): @@ -642,7 +644,7 @@ extension PaymentSheetFormFactory { return StaticElement(view: label) } - func makeInstantDebits() -> PaymentMethodElement { + func makeInstantDebits(requireLinkedBank: Bool) -> PaymentMethodElement { return InstantDebitsPaymentMethodElement( configuration: configuration, titleElement: { @@ -656,6 +658,7 @@ extension PaymentSheetFormFactory { } }(), emailElement: makeEmail(), + requireLinkedBank: requireLinkedBank, theme: theme ) } diff --git a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/USBankAccount/InstantDebitsPaymentMethodElement.swift b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/USBankAccount/InstantDebitsPaymentMethodElement.swift index b36f0fe81cc..db5fb953c16 100644 --- a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/USBankAccount/InstantDebitsPaymentMethodElement.swift +++ b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/USBankAccount/InstantDebitsPaymentMethodElement.swift @@ -26,6 +26,7 @@ final class InstantDebitsPaymentMethodElement: ContainerElement { private let emailElement: TextFieldElement private let linkedBankInfoView: BankAccountInfoView private var linkedBank: InstantDebitsLinkedBank? + private let requireLinkedBank: Bool private let theme: ElementsUITheme var presentingViewControllerDelegate: PresentingViewControllerDelegate? @@ -75,6 +76,7 @@ final class InstantDebitsPaymentMethodElement: ContainerElement { configuration: PaymentSheetFormFactoryConfig, titleElement: StaticElement?, emailElement: PaymentMethodElementWrapper, + requireLinkedBank: Bool, theme: ElementsUITheme = .default ) { self.configuration = configuration @@ -87,6 +89,7 @@ final class InstantDebitsPaymentMethodElement: ContainerElement { self.emailElement = emailElement.element self.linkedBank = nil self.linkedBankInfoSectionElement.view.isHidden = true + self.requireLinkedBank = requireLinkedBank self.theme = theme let allElements: [Element?] = [ @@ -181,14 +184,16 @@ extension InstantDebitsPaymentMethodElement: PaymentMethodElement { // after a bank is linked, this gets hit to update func updateParams(params: IntentConfirmParams) -> IntentConfirmParams? { - if - let updatedParams = formElement.updateParams(params: params), - let linkedBank - { + guard let updatedParams = formElement.updateParams(params: params) else { + return nil + } + + if requireLinkedBank, let linkedBank { updatedParams.instantDebitsLinkedBank = linkedBank return updatedParams + } else { + return updatedParams } - return nil } } diff --git a/StripePayments/StripePayments/Source/API Bindings/Models/Shared/LinkSettings.swift b/StripePayments/StripePayments/Source/API Bindings/Models/Shared/LinkSettings.swift index 9f98bd845ad..a63b506b2be 100644 --- a/StripePayments/StripePayments/Source/API Bindings/Models/Shared/LinkSettings.swift +++ b/StripePayments/StripePayments/Source/API Bindings/Models/Shared/LinkSettings.swift @@ -58,19 +58,19 @@ import Foundation fromAPIResponse response: [AnyHashable: Any]? ) -> Self? { guard - let response = response, - let fundingSourcesStrings = response["link_funding_sources"] as? [String] + let response = response +// let fundingSourcesStrings = response["link_funding_sources"] as? [String] else { return nil } // Server may send down funding sources we haven't implemented yet, so we'll just ignore any unknown sources - let validFundingSources = Set(fundingSourcesStrings.compactMap(FundingSource.init)) +// let validFundingSources = Set(fundingSourcesStrings.compactMap(FundingSource.init)) let webviewOption = PopupWebviewOption(rawValue: response["link_popup_webview_option"] as? String ?? "") let passthroughModeEnabled = response["link_passthrough_mode_enabled"] as? Bool ?? false let disableSignup = response["link_mobile_disable_signup"] as? Bool ?? false - let linkMode = (response["link_mode"] as? String).flatMap { LinkMode(rawValue: $0) } +// let linkMode = (response["link_mode"] as? String).flatMap { LinkMode(rawValue: $0) } // Collect the flags for the URL generator let linkFlags = response.reduce(into: [String: Bool]()) { partialResult, element in @@ -80,11 +80,13 @@ import Foundation } return LinkSettings( - fundingSources: validFundingSources, + fundingSources: [.card, .bankAccount], +// fundingSources: validFundingSources, popupWebviewOption: webviewOption, passthroughModeEnabled: passthroughModeEnabled, disableSignup: disableSignup, - linkMode: linkMode, + linkMode: .linkCardBrand, +// linkMode: linkMode, linkFlags: linkFlags, allResponseFields: response ) as? Self From f9549596a9f4f0b42f2563e47ed018d39f6f203d Mon Sep 17 00:00:00 2001 From: Mat Schmid Date: Thu, 5 Sep 2024 13:45:53 -0400 Subject: [PATCH 4/5] Revert small changes --- .../PaymentSheetFormFactory.swift | 9 +++------ .../InstantDebitsPaymentMethodElement.swift | 12 +++--------- .../PaymentMethodFormViewController.swift | 15 +++++++++------ .../API Bindings/Models/Shared/LinkSettings.swift | 14 ++++++-------- 4 files changed, 21 insertions(+), 29 deletions(-) diff --git a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentSheetFormFactory/PaymentSheetFormFactory.swift b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentSheetFormFactory/PaymentSheetFormFactory.swift index 02abc8b87cd..806a591d815 100644 --- a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentSheetFormFactory/PaymentSheetFormFactory.swift +++ b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentSheetFormFactory/PaymentSheetFormFactory.swift @@ -133,10 +133,8 @@ class PaymentSheetFormFactory { func make() -> PaymentMethodElement { switch paymentMethod { - case .instantDebits: - return makeInstantDebits(requireLinkedBank: true) - case .linkCardBrand: - return makeInstantDebits(requireLinkedBank: false) + case .instantDebits, .linkCardBrand: + return makeInstantDebits() case .external: return makeExternalPaymentMethodForm() case .stripe(let paymentMethod): @@ -644,7 +642,7 @@ extension PaymentSheetFormFactory { return StaticElement(view: label) } - func makeInstantDebits(requireLinkedBank: Bool) -> PaymentMethodElement { + func makeInstantDebits() -> PaymentMethodElement { return InstantDebitsPaymentMethodElement( configuration: configuration, titleElement: { @@ -658,7 +656,6 @@ extension PaymentSheetFormFactory { } }(), emailElement: makeEmail(), - requireLinkedBank: requireLinkedBank, theme: theme ) } diff --git a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/USBankAccount/InstantDebitsPaymentMethodElement.swift b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/USBankAccount/InstantDebitsPaymentMethodElement.swift index db5fb953c16..8501f59dcc8 100644 --- a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/USBankAccount/InstantDebitsPaymentMethodElement.swift +++ b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/USBankAccount/InstantDebitsPaymentMethodElement.swift @@ -26,7 +26,6 @@ final class InstantDebitsPaymentMethodElement: ContainerElement { private let emailElement: TextFieldElement private let linkedBankInfoView: BankAccountInfoView private var linkedBank: InstantDebitsLinkedBank? - private let requireLinkedBank: Bool private let theme: ElementsUITheme var presentingViewControllerDelegate: PresentingViewControllerDelegate? @@ -76,7 +75,6 @@ final class InstantDebitsPaymentMethodElement: ContainerElement { configuration: PaymentSheetFormFactoryConfig, titleElement: StaticElement?, emailElement: PaymentMethodElementWrapper, - requireLinkedBank: Bool, theme: ElementsUITheme = .default ) { self.configuration = configuration @@ -89,7 +87,6 @@ final class InstantDebitsPaymentMethodElement: ContainerElement { self.emailElement = emailElement.element self.linkedBank = nil self.linkedBankInfoSectionElement.view.isHidden = true - self.requireLinkedBank = requireLinkedBank self.theme = theme let allElements: [Element?] = [ @@ -188,12 +185,9 @@ extension InstantDebitsPaymentMethodElement: PaymentMethodElement { return nil } - if requireLinkedBank, let linkedBank { - updatedParams.instantDebitsLinkedBank = linkedBank - return updatedParams - } else { - return updatedParams - } + guard let linkedBank else { return nil } + updatedParams.instantDebitsLinkedBank = linkedBank + return updatedParams } } diff --git a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/ViewControllers/PaymentMethodFormViewController.swift b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/ViewControllers/PaymentMethodFormViewController.swift index 06a9c30e865..23538bd70c4 100644 --- a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/ViewControllers/PaymentMethodFormViewController.swift +++ b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/ViewControllers/PaymentMethodFormViewController.swift @@ -215,7 +215,7 @@ extension PaymentMethodFormViewController { } else { return true } - } else if paymentMethodType == .instantDebits { + } else if paymentMethodType == .instantDebits || paymentMethodType == .linkCardBrand { // only override buy button (show "Continue") IF we don't have a linked bank return instantDebitsFormElement?.getLinkedBank() == nil } @@ -225,11 +225,12 @@ extension PaymentMethodFormViewController { var overridePrimaryButtonState: OverridePrimaryButtonState? { guard shouldOverridePrimaryButton else { return nil } let isEnabled: Bool = { - if paymentMethodType == .stripe(.USBankAccount) && usBankAccountFormElement?.canLinkAccount ?? false { - true - } else if paymentMethodType == .instantDebits && instantDebitsFormElement?.enableCTA ?? false { - true - } else { + switch paymentMethodType { + case .stripe(let paymentMethod): + paymentMethod == .USBankAccount && (usBankAccountFormElement?.canLinkAccount ?? false) + case .instantDebits, .linkCardBrand: + instantDebitsFormElement?.enableCTA ?? false + default: false } }() @@ -255,6 +256,8 @@ extension PaymentMethodFormViewController { handleCollectBankAccount(from: viewController) case .instantDebits: handleCollectInstantDebits(from: viewController) + case .linkCardBrand: + handleCollectInstantDebits(from: viewController) default: return } diff --git a/StripePayments/StripePayments/Source/API Bindings/Models/Shared/LinkSettings.swift b/StripePayments/StripePayments/Source/API Bindings/Models/Shared/LinkSettings.swift index a63b506b2be..9f98bd845ad 100644 --- a/StripePayments/StripePayments/Source/API Bindings/Models/Shared/LinkSettings.swift +++ b/StripePayments/StripePayments/Source/API Bindings/Models/Shared/LinkSettings.swift @@ -58,19 +58,19 @@ import Foundation fromAPIResponse response: [AnyHashable: Any]? ) -> Self? { guard - let response = response -// let fundingSourcesStrings = response["link_funding_sources"] as? [String] + let response = response, + let fundingSourcesStrings = response["link_funding_sources"] as? [String] else { return nil } // Server may send down funding sources we haven't implemented yet, so we'll just ignore any unknown sources -// let validFundingSources = Set(fundingSourcesStrings.compactMap(FundingSource.init)) + let validFundingSources = Set(fundingSourcesStrings.compactMap(FundingSource.init)) let webviewOption = PopupWebviewOption(rawValue: response["link_popup_webview_option"] as? String ?? "") let passthroughModeEnabled = response["link_passthrough_mode_enabled"] as? Bool ?? false let disableSignup = response["link_mobile_disable_signup"] as? Bool ?? false -// let linkMode = (response["link_mode"] as? String).flatMap { LinkMode(rawValue: $0) } + let linkMode = (response["link_mode"] as? String).flatMap { LinkMode(rawValue: $0) } // Collect the flags for the URL generator let linkFlags = response.reduce(into: [String: Bool]()) { partialResult, element in @@ -80,13 +80,11 @@ import Foundation } return LinkSettings( - fundingSources: [.card, .bankAccount], -// fundingSources: validFundingSources, + fundingSources: validFundingSources, popupWebviewOption: webviewOption, passthroughModeEnabled: passthroughModeEnabled, disableSignup: disableSignup, - linkMode: .linkCardBrand, -// linkMode: linkMode, + linkMode: linkMode, linkFlags: linkFlags, allResponseFields: response ) as? Self From 6c59a10861ad919c8d420a681f4968e6ae28a12b Mon Sep 17 00:00:00 2001 From: Mat Schmid Date: Thu, 5 Sep 2024 16:06:39 -0400 Subject: [PATCH 5/5] Call share endpoint in FC for Panther --- .../FinancialConnectionsAPIClient.swift | 30 +++++++++++++++++++ .../FinancialConnectionsSessionManifest.swift | 5 ++++ .../Source/Native/NativeFlowController.swift | 18 +++++++---- 3 files changed, 48 insertions(+), 5 deletions(-) diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/API Bindings/FinancialConnectionsAPIClient.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/API Bindings/FinancialConnectionsAPIClient.swift index ad54a6168d1..66a91b50880 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/API Bindings/FinancialConnectionsAPIClient.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/API Bindings/FinancialConnectionsAPIClient.swift @@ -219,6 +219,14 @@ protocol FinancialConnectionsAPI { bankAccountId: String ) -> Future + // TODO: Figure out return type for this request. + // We'll need to sort out the down-scoped client secret first. + func sharePaymentDetails( + consumerSessionClientSecret: String, + paymentDetailsId: String, + expectedPaymentMethodType: String + ) -> Future + func paymentMethods( consumerSessionClientSecret: String, paymentDetailsId: String @@ -944,6 +952,27 @@ extension FinancialConnectionsAPIClient: FinancialConnectionsAPI { ) } + func sharePaymentDetails( + consumerSessionClientSecret: String, + paymentDetailsId: String, + expectedPaymentMethodType: String + ) -> Future { + let parameters: [String: Any] = [ + "request_surface": requestSurface, + "id": paymentDetailsId, + "credentials": [ + "consumer_session_client_secret": consumerSessionClientSecret + ], + "expected_payment_method_type": expectedPaymentMethodType, + "expand": ["payment_method"], + ] + return post( + resource: APIEndpointSharePaymentDetails, + parameters: parameters, + useConsumerPublishableKeyIfNeeded: false + ) + } + func paymentMethods( consumerSessionClientSecret: String, paymentDetailsId: String @@ -995,4 +1024,5 @@ private let APIEndpointPollAccountNumbers = "link_account_sessions/poll_account_ private let APIEndpointLinkAccountsSignUp = "consumers/accounts/sign_up" private let APIEndpointAttachLinkConsumerToLinkAccountSession = "consumers/attach_link_consumer_to_link_account_session" private let APIEndpointPaymentDetails = "consumers/payment_details" +private let APIEndpointSharePaymentDetails = "consumers/payment_details/share" private let APIEndpointPaymentMethods = "payment_methods" diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/API Bindings/Models/FinancialConnectionsSessionManifest.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/API Bindings/Models/FinancialConnectionsSessionManifest.swift index 6066790f65b..5963dee4266 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/API Bindings/Models/FinancialConnectionsSessionManifest.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/API Bindings/Models/FinancialConnectionsSessionManifest.swift @@ -126,6 +126,11 @@ struct FinancialConnectionsSessionManifest: Decodable { !livemode } + var isPantherPayment: Bool { + let isLinkPaymentMethod = paymentMethodType == .link + return isProductInstantDebits && isLinkPaymentMethod + } + init( accountholderCustomerEmailAddress: String? = nil, accountholderIsLinkConsumer: Bool? = nil, diff --git a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NativeFlowController.swift b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NativeFlowController.swift index f89be5c9364..dbc2d41eaf7 100644 --- a/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NativeFlowController.swift +++ b/StripeFinancialConnections/StripeFinancialConnections/Source/Native/NativeFlowController.swift @@ -513,10 +513,18 @@ extension NativeFlowController { } bankAccountDetails = paymentDetails.redactedPaymentDetails.bankAccountDetails - return self.dataManager.createPaymentMethod( - consumerSessionClientSecret: consumerSession.clientSecret, - paymentDetailsId: paymentDetails.redactedPaymentDetails.id - ) + if self.dataManager.manifest.isPantherPayment { + return self.dataManager.apiClient.sharePaymentDetails( + consumerSessionClientSecret: consumerSession.clientSecret, + paymentDetailsId: paymentDetails.redactedPaymentDetails.id, + expectedPaymentMethodType: "card" + ) + } else { + return self.dataManager.createPaymentMethod( + consumerSessionClientSecret: consumerSession.clientSecret, + paymentDetailsId: paymentDetails.redactedPaymentDetails.id + ) + } } .observe { result in switch result { @@ -801,7 +809,7 @@ extension NativeFlowController: ManualEntryViewControllerDelegate { // to the Link signup/save call later in the flow. We don't need them anymore since we know // they've failed us in some way at this point. dataManager.linkedAccounts = nil - + dataManager.paymentAccountResource = paymentAccountResource dataManager.accountNumberLast4 = accountNumberLast4