diff --git a/Example/PaymentSheet Example/PaymentSheetUITest/PaymentSheetUITest.swift b/Example/PaymentSheet Example/PaymentSheetUITest/PaymentSheetUITest.swift index 1172f110583..70de67d5b14 100644 --- a/Example/PaymentSheet Example/PaymentSheetUITest/PaymentSheetUITest.swift +++ b/Example/PaymentSheet Example/PaymentSheetUITest/PaymentSheetUITest.swift @@ -196,7 +196,7 @@ class PaymentSheetStandardUITests: PaymentSheetUITestCase { // `mc_load_succeeded` event `selected_lpm` should be "apple_pay", the default payment method. XCTAssertEqual(analyticsLog[2][string: "selected_lpm"], "apple_pay") app.buttons["+ Add"].waitForExistenceAndTap() - XCTAssertTrue(app.staticTexts["Add a card"].waitForExistence(timeout: 2)) + XCTAssertTrue(app.staticTexts["Card information"].waitForExistence(timeout: 2)) // Should fire the `mc_form_shown` event w/ `selected_lpm` = card XCTAssertEqual(analyticsLog.last?[string: "event"], "mc_form_shown") diff --git a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentMethodType.swift b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentMethodType.swift index fdd474ceabb..51a3a354576 100644 --- a/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentMethodType.swift +++ b/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentMethodType.swift @@ -165,12 +165,29 @@ extension PaymentSheet { // External Payment Methods + elementsSession.externalPaymentMethods.map { PaymentMethodType.external($0) } - if - elementsSession.orderedPaymentMethodTypes.contains(.link), - !elementsSession.orderedPaymentMethodTypes.contains(.USBankAccount), - !intent.isDeferredIntent, + // We should manually add Instant Debits as a payment method when: + // - Link is an available payment method. + // - US Bank Account is *not* an available payment method. + // - Not a deferred intent flow. + // - Link Funding Sources contains Bank Account. + var eligibleForInstantDebits: Bool { + elementsSession.orderedPaymentMethodTypes.contains(.link) && + !elementsSession.orderedPaymentMethodTypes.contains(.USBankAccount) && + !intent.isDeferredIntent && elementsSession.linkFundingSources?.contains(.bankAccount) == true - { + } + + // We should manually add Link Card Brand as a payment method when: + // - Link Funding Sources contains Bank Account. + // - US Bank Account is *not* an available payment method. + // - Link Card Brand is the Link Mode + var eligibleForLinkCardBrand: Bool { + elementsSession.linkFundingSources?.contains(.bankAccount) == true && + !elementsSession.orderedPaymentMethodTypes.contains(.USBankAccount) && + elementsSession.linkSettings?.linkMode == .linkCardBrand + } + + if eligibleForInstantDebits { let availabilityStatus = configurationSatisfiesRequirements( requirements: [.financialConnectionsSDK], configuration: configuration, @@ -179,6 +196,16 @@ extension PaymentSheet { if availabilityStatus == .supported { recommendedPaymentMethodTypes.append(.instantDebits) } + // Else if here so we don't show both Instant Debits and Link Card Brand together. + } else if eligibleForLinkCardBrand { + let availabilityStatus = configurationSatisfiesRequirements( + requirements: [.financialConnectionsSDK], + configuration: configuration, + intent: intent + ) + if availabilityStatus == .supported { + recommendedPaymentMethodTypes.append(.linkCardBrand) + } } if let merchantPaymentMethodOrder = configuration.paymentMethodOrder?.map({ $0.lowercased() }) { diff --git a/StripePaymentSheet/StripePaymentSheetTests/PaymentSheet/PaymentSheetPaymentMethodTypeTest.swift b/StripePaymentSheet/StripePaymentSheetTests/PaymentSheet/PaymentSheetPaymentMethodTypeTest.swift index dd2ba7ed42e..77ab4523042 100644 --- a/StripePaymentSheet/StripePaymentSheetTests/PaymentSheet/PaymentSheetPaymentMethodTypeTest.swift +++ b/StripePaymentSheet/StripePaymentSheetTests/PaymentSheet/PaymentSheetPaymentMethodTypeTest.swift @@ -340,6 +340,8 @@ class PaymentSheetPaymentMethodTypeTest: XCTestCase { XCTAssertEqual(elementsSession.orderedPaymentMethodTypes, [.klarna, .card]) } + // MARK: - Payment Method Types + func testPaymentIntentFilteredPaymentMethodTypes() { let intent = Intent._testPaymentIntent(paymentMethodTypes: [.card, .klarna, .przelewy24]) var configuration = PaymentSheet.Configuration() @@ -407,6 +409,23 @@ class PaymentSheetPaymentMethodTypeTest: XCTestCase { XCTAssertEqual(types, [.stripe(.card)]) } + func testPaymentMethodTypesLinkCardBrand() { + let intent = Intent._testPaymentIntent(paymentMethodTypes: [.card]) + let configuration = PaymentSheet.Configuration() + let types = PaymentSheet.PaymentMethodType.filteredPaymentMethodTypes( + from: intent, + elementsSession: ._testValue( + intent: intent, + linkMode: .linkCardBrand, + linkFundingSources: [.card, .bankAccount] + ), + configuration: configuration + ) + XCTAssertEqual(types, [.stripe(.card), .linkCardBrand]) + } + + // MARK: Other + func testUnknownPMTypeIsUnsupported() { let setupIntent = STPFixtures.makeSetupIntent(paymentMethodTypes: [.unknown]) let paymentMethod = STPPaymentMethod.type(from: "luxe_bucks") @@ -518,7 +537,7 @@ extension STPFixtures { captureMethod: String = "automatic", confirmationMethod: String = "automatic", shippingProvided: Bool = false, - paymentMethodJson: [String:Any]? = nil + paymentMethodJson: [String: Any]? = nil ) -> STPPaymentIntent { var json = STPTestUtils.jsonNamed(STPTestJSONPaymentIntent)! if let setupFutureUsage = setupFutureUsage { @@ -539,7 +558,7 @@ extension STPFixtures { } if let paymentMethodJson = paymentMethodJson { json["payment_method"] = paymentMethodJson - + } if let paymentMethodOptions = paymentMethodOptions { json["payment_method_options"] = paymentMethodOptions.dictionaryValue @@ -550,7 +569,7 @@ extension STPFixtures { static func makeSetupIntent( paymentMethodTypes: [STPPaymentMethodType] = [.card], usage: String = "off_session", - paymentMethodJson: [String:Any]? = nil + paymentMethodJson: [String: Any]? = nil ) -> STPSetupIntent { var json = STPTestUtils.jsonNamed(STPTestJSONSetupIntent)! json["usage"] = usage @@ -559,7 +578,7 @@ extension STPFixtures { } if let paymentMethodJson = paymentMethodJson { json["payment_method"] = paymentMethodJson - + } return STPSetupIntent.decodedObject(fromAPIResponse: json)! } diff --git a/StripePaymentSheet/StripePaymentSheetTests/PaymentSheet/STPFixtures+PaymentSheet.swift b/StripePaymentSheet/StripePaymentSheetTests/PaymentSheet/STPFixtures+PaymentSheet.swift index 15079f05c27..9dd6cb1bb38 100644 --- a/StripePaymentSheet/StripePaymentSheetTests/PaymentSheet/STPFixtures+PaymentSheet.swift +++ b/StripePaymentSheet/StripePaymentSheetTests/PaymentSheet/STPFixtures+PaymentSheet.swift @@ -37,6 +37,8 @@ extension STPElementsSession { customerSessionData: [String: Any]? = nil, cardBrandChoiceData: [String: Any]? = nil, isLinkPassthroughModeEnabled: Bool? = nil, + linkMode: LinkSettings.LinkMode? = nil, + linkFundingSources: Set = [], disableLinkSignup: Bool? = nil ) -> STPElementsSession { var json = STPTestUtils.jsonNamed("ElementsSession")! @@ -70,6 +72,12 @@ extension STPElementsSession { json[jsonDict: "link_settings"]!["link_passthrough_mode_enabled"] = isLinkPassthroughModeEnabled } + if let linkMode { + json[jsonDict: "link_settings"]!["link_mode"] = linkMode.rawValue + } + + json[jsonDict: "link_settings"]!["link_funding_sources"] = linkFundingSources.map(\.rawValue) + if let disableLinkSignup { json[jsonDict: "link_settings"]!["link_mobile_disable_signup"] = disableLinkSignup } @@ -78,7 +86,11 @@ extension STPElementsSession { return elementsSession } - static func _testValue(intent: Intent) -> STPElementsSession { + static func _testValue( + intent: Intent, + linkMode: LinkSettings.LinkMode? = nil, + linkFundingSources: Set = [] + ) -> STPElementsSession { let paymentMethodTypes: [String] = { switch intent { case .paymentIntent(let paymentIntent): @@ -89,7 +101,11 @@ extension STPElementsSession { return intentConfig.paymentMethodTypes ?? [] } }() - return STPElementsSession._testValue(paymentMethodTypes: paymentMethodTypes) + return STPElementsSession._testValue( + paymentMethodTypes: paymentMethodTypes, + linkMode: linkMode, + linkFundingSources: linkFundingSources + ) } }