Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,19 @@ extension ConsumerPaymentDetails {
}
}

extension ConsumerPaymentDetails.DetailsType {
var fundingSource: LinkSettings.FundingSource? {
switch self {
case .card:
return .card
case .bankAccount:
return .bankAccount
case .unparsable:
return nil
}
}
}

// MARK: - Card checks

extension ConsumerPaymentDetails.Details {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@

extension STPElementsSession {
var supportsLink: Bool {
// Either Link is an allowed Payment Method in the elements/sessions response, or passthrough mode (Link as a Card PM) is allowed
orderedPaymentMethodTypes.contains(.link) || linkPassthroughModeEnabled
guard let linkSettings, linkSettings.fundingSourcesSupportedByClient else {
return false
}
return linkSettings.linkMode != nil
}

var linkPassthroughModeEnabled: Bool {
Expand All @@ -23,7 +25,7 @@ extension STPElementsSession {
}

var supportsLinkCard: Bool {
supportsLink && (linkFundingSources?.contains(.card) ?? false) || linkPassthroughModeEnabled
supportsLink && (linkFundingSources?.contains(.card) ?? false)
}

var linkFundingSources: Set<LinkSettings.FundingSource>? {
Expand Down Expand Up @@ -85,3 +87,11 @@ extension Intent {
}
}
}

extension LinkSettings {
/// Returns true if at least one of the `link_funding_sources` is supported by the client.
var fundingSourcesSupportedByClient: Bool {
let clientSupportedFundingSources = ConsumerPaymentDetails.DetailsType.allCases.compactMap(\.fundingSource)
return !fundingSources.isDisjoint(with: clientSupportedFundingSources)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -644,7 +644,7 @@ class CustomerSheetSnapshotTests: STPSnapshotTestCase {
}

private func updatePaymentMethodDetail(data: Data, variables: [String: String]) -> Data {
var template = String(decoding: data, as: UTF8.self)
var template = String(data: data, encoding: .utf8)!
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Drive-by fix.

for (templateKey, templateValue) in variables {
let translated = template.replacingOccurrences(of: templateKey, with: templateValue)
template = translated
Expand All @@ -661,6 +661,7 @@ class CustomerSheetSnapshotTests: STPSnapshotTestCase {
variables: [
"<paymentMethods>": "\(paymentMethods)",
"<currency>": "\"usd\"",
"<linkMode>": "null",
]
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,44 +10,21 @@ import XCTest

final class PaymentMethodAvailabilityTest: XCTestCase {

func testIsLinkEnabled_supportsLinkFalse_linkNotPresent() {
func testIsLinkEnabled_linkModeNil() {
let elementsSession = STPElementsSession._testValue(
paymentMethodTypes: ["card"],
isLinkPassthroughModeEnabled: false
linkMode: nil
)
let configuration = PaymentSheet.Configuration()
let isLinkEnabled = PaymentSheet.isLinkEnabled(elementsSession: elementsSession, configuration: configuration)

XCTAssertFalse(isLinkEnabled, "Link should be disabled when supportsLink is false and link is not in payment method types")
}

func testIsLinkEnabled_supportsLinkTrue_linkPresent() {
let elementsSession = STPElementsSession._testValue(
paymentMethodTypes: ["card", "link"],
isLinkPassthroughModeEnabled: false
)
let configuration = PaymentSheet.Configuration()
let isLinkEnabled = PaymentSheet.isLinkEnabled(elementsSession: elementsSession, configuration: configuration)

XCTAssertTrue(isLinkEnabled, "Link should be enabled when isLinkPassthroughModeEnabled is false, since Link is present in the payment method types")
}

func testIsLinkEnabled_supportsLinkTrue_linkNotPresent_passthroughEnabled() {
let elementsSession = STPElementsSession._testValue(
paymentMethodTypes: ["card"],
isLinkPassthroughModeEnabled: true
)
let configuration = PaymentSheet.Configuration()
let isLinkEnabled = PaymentSheet.isLinkEnabled(elementsSession: elementsSession, configuration: configuration)

XCTAssertTrue(isLinkEnabled, "Link should be enabled when supportsLink is true because passthrough mode is enabled")
XCTAssertFalse(isLinkEnabled, "Link should be disabled when linkMode is nil")
}

func testIsLinkEnabled_requiresBillingDetailCollection() {
let elementsSession = STPElementsSession._testValue(
paymentMethodTypes: ["card", "link"],
isLinkPassthroughModeEnabled: true

paymentMethodTypes: ["card"],
linkMode: .passthrough
)
var configuration = PaymentSheet.Configuration()
configuration.billingDetailsCollectionConfiguration.name = .always
Expand All @@ -58,9 +35,8 @@ final class PaymentMethodAvailabilityTest: XCTestCase {

func testIsLinkEnabled_cardBrandAcceptanceNotAll() {
let elementsSession = STPElementsSession._testValue(
paymentMethodTypes: ["card", "link"],
isLinkPassthroughModeEnabled: true

paymentMethodTypes: ["card"],
linkMode: .passthrough
)
var configuration = PaymentSheet.Configuration()
configuration.cardBrandAcceptance = .allowed(brands: [.visa])
Expand All @@ -69,33 +45,24 @@ final class PaymentMethodAvailabilityTest: XCTestCase {
XCTAssertFalse(isLinkEnabled, "Link should be disabled when card brand acceptance is not 'all'")
}

func testIsLinkEnabled_allConditionsMet() {
func testIsLinkEnabled_linkModePresent() {
// Given
let elementsSession = STPElementsSession._testValue(
paymentMethodTypes: ["card", "link"],
isLinkPassthroughModeEnabled: true
)
let configuration = PaymentSheet.Configuration()
let isLinkEnabled = PaymentSheet.isLinkEnabled(elementsSession: elementsSession, configuration: configuration)

XCTAssertTrue(isLinkEnabled, "Link should be enabled when all conditions are met")
}

func testIsLinkEnabled_linkNotExplicitlyAllowedButPassthroughEnabled() {
let elementsSession = STPElementsSession._testValue(
paymentMethodTypes: ["card"],
isLinkPassthroughModeEnabled: true
linkMode: .passthrough,
linkFundingSources: [.card, .bankAccount]
)
let configuration = PaymentSheet.Configuration()
let isLinkEnabled = PaymentSheet.isLinkEnabled(elementsSession: elementsSession, configuration: configuration)

XCTAssertTrue(isLinkEnabled, "Link should be enabled when passthrough mode is enabled, even if 'link' is not explicitly in payment method types")
XCTAssertTrue(isLinkEnabled, "Link should be enabled when all conditions are met")
}

func testIsLinkEnabled_linkDisplayAutomatic_linkPresent() {
let elementsSession = STPElementsSession._testValue(
paymentMethodTypes: ["card"],
isLinkPassthroughModeEnabled: true
linkMode: .passthrough,
linkFundingSources: [.card, .bankAccount]
)
var configuration = PaymentSheet.Configuration()
configuration.link = .init(display: .automatic)
Expand All @@ -107,7 +74,7 @@ final class PaymentMethodAvailabilityTest: XCTestCase {
func testIsLinkEnabled_linkDisplayNever_linkNotPresent() {
let elementsSession = STPElementsSession._testValue(
paymentMethodTypes: ["card"],
isLinkPassthroughModeEnabled: true
linkMode: .passthrough
)
var configuration = PaymentSheet.Configuration()
configuration.link = .init(display: .never)
Expand Down Expand Up @@ -181,17 +148,18 @@ extension LinkSettings {
static func _testValue(
disableSignup: Bool = false,
flags: [String: Bool]? = nil,
linkMode: LinkMode? = .passthrough,
linkSupportedPaymentMethodsOnboardingEnabled: [String] = ["CARD"]
) -> LinkSettings {
return .init(
fundingSources: [.card, .bankAccount],
popupWebviewOption: nil,
passthroughModeEnabled: true,
passthroughModeEnabled: linkMode == .passthrough || linkMode == .linkCardBrand,
disableSignup: disableSignup,
suppress2FAModal: false,
disableFlowControllerRUX: true,
useAttestationEndpoints: true,
linkMode: .passthrough,
linkMode: linkMode,
linkFlags: flags,
linkConsumerIncentive: nil,
linkDefaultOptIn: nil,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ final class PaymentSheetFlowControllerViewControllerSnapshotTests: STPSnapshotTe
func makeTestLoadResult(savedPaymentMethods: [STPPaymentMethod]) -> PaymentSheetLoader.LoadResult {
return .init(
intent: ._testValue(),
elementsSession: ._testValue(paymentMethodTypes: ["card"], isLinkPassthroughModeEnabled: false),
elementsSession: ._testValue(paymentMethodTypes: ["card"], linkMode: nil),
savedPaymentMethods: savedPaymentMethods,
paymentMethodTypes: [.stripe(.card)]
)
Expand Down Expand Up @@ -115,7 +115,7 @@ final class PaymentSheetFlowControllerViewControllerSnapshotTests: STPSnapshotTe
intent: ._testValue(),
elementsSession: ._testValue(
paymentMethodTypes: ["card"],
isLinkPassthroughModeEnabled: false
linkMode: nil
),
savedPaymentMethods: [],
paymentMethodTypes: [.stripe(.card)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1833,7 +1833,7 @@ class PaymentSheetFormFactoryTest: XCTestCase {
func makeForm(intent: Intent) -> PaymentMethodElement {
return PaymentSheetFormFactory(
intent: intent,
elementsSession: ._testValue(intent: intent, isLinkPassthroughModeEnabled: false),
elementsSession: ._testValue(intent: intent, linkMode: .linkPaymentMethod),
configuration: .paymentElement(configuration),
paymentMethod: .stripe(.card),
linkAccount: PaymentSheetLinkAccount(
Expand Down Expand Up @@ -1880,7 +1880,7 @@ class PaymentSheetFormFactoryTest: XCTestCase {
func makeForm(intent: Intent) -> PaymentMethodElement {
return PaymentSheetFormFactory(
intent: intent,
elementsSession: ._testValue(intent: intent, isLinkPassthroughModeEnabled: false),
elementsSession: ._testValue(intent: intent, linkMode: .linkPaymentMethod),
configuration: .paymentElement(configuration),
paymentMethod: .stripe(.card),
linkAccount: PaymentSheetLinkAccount(
Expand Down Expand Up @@ -1925,7 +1925,7 @@ class PaymentSheetFormFactoryTest: XCTestCase {
func makeForm(intent: Intent) -> PaymentMethodElement {
return PaymentSheetFormFactory(
intent: intent,
elementsSession: ._testValue(intent: intent, isLinkPassthroughModeEnabled: true),
elementsSession: ._testValue(intent: intent, linkMode: .passthrough),
configuration: .paymentElement(configuration),
paymentMethod: .stripe(.card),
linkAccount: PaymentSheetLinkAccount(
Expand Down Expand Up @@ -1970,7 +1970,7 @@ class PaymentSheetFormFactoryTest: XCTestCase {
func makeForm(intent: Intent) -> PaymentMethodElement {
return PaymentSheetFormFactory(
intent: intent,
elementsSession: ._testValue(intent: intent, isLinkPassthroughModeEnabled: true),
elementsSession: ._testValue(intent: intent, linkMode: .passthrough),
configuration: .paymentElement(configuration),
paymentMethod: .stripe(.card),
linkAccount: PaymentSheetLinkAccount(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,7 @@ class PaymentSheetSnapshotTests: STPSnapshotTestCase {
variables: [
"<paymentMethods>": "\"link\"",
"<currency>": "\"USD\"",
"<linkMode>": "\"PASSTHROUGH\"",
]
)
}
Expand All @@ -641,6 +642,7 @@ class PaymentSheetSnapshotTests: STPSnapshotTestCase {
)
presentPaymentSheet(darkMode: false)
verify(paymentSheet.bottomSheetViewController.view!)
UserDefaults.standard.removeObject(forKey: "FINANCIAL_CONNECTIONS_INSTANT_DEBITS_INCENTIVES")
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test started failing, and I assume it's because we never reset the value after enabling it at the start of the test.

}

func testPaymentSheetSingleLPM() {
Expand All @@ -652,6 +654,7 @@ class PaymentSheetSnapshotTests: STPSnapshotTestCase {
variables: [
"<paymentMethods>": "\"cashapp\"",
"<currency>": "\"usd\"",
"<linkMode>": "null",
]
)
}
Expand All @@ -678,6 +681,7 @@ class PaymentSheetSnapshotTests: STPSnapshotTestCase {
variables: [
"<paymentMethods>": "\"cashapp\"",
"<currency>": "\"usd\"",
"<linkMode>": "null",
]
)
}
Expand Down
Loading
Loading