[Link] Determine payment types from funding sources (#2075)

* [Link] Determine payment types from funding sources

* Fix tests

* Add link_settings to JSON mocks

* Put link_settings where it belongs

* Add explanatory comments around JSON parsing
This commit is contained in:
bmelts-stripe 2022-11-10 13:52:41 -08:00 committed by GitHub
parent d82f8bcb25
commit dbaa98dd0f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 44 additions and 15 deletions

View File

@ -472,7 +472,7 @@ extension PaymentSheetLinkAccount {
/// - Parameter intent: The Intent that the user is trying to confirm.
/// - Returns: A set containing the supported Payment Details types.
func supportedPaymentDetailsTypes(for intent: Intent) -> Set<ConsumerPaymentDetails.DetailsType> {
guard let currentSession = currentSession else {
guard let currentSession = currentSession, let fundingSources = intent.linkFundingSources else {
return []
}
@ -480,10 +480,12 @@ extension PaymentSheetLinkAccount {
currentSession.supportedPaymentDetailsTypes ?? []
)
if !intent.linkBankOnboardingEnabled {
supportedPaymentDetailsTypes.remove(.bankAccount)
}
// Take the intersection of the consumer session types and the merchant-provided Link funding sources
let fundingSourceDetailsTypes = Set(fundingSources.compactMap { $0.detailsType })
supportedPaymentDetailsTypes.formIntersection(fundingSourceDetailsTypes)
// Special testmode handling
if !intent.livemode && Self.emailSupportsMultipleFundingSourcesOnTestMode(email) {
supportedPaymentDetailsTypes.insert(.bankAccount)
}
@ -526,3 +528,16 @@ private extension PaymentSheetLinkAccount {
}
}
private extension LinkSettings.FundingSource {
var detailsType: ConsumerPaymentDetails.DetailsType? {
switch self {
case .card:
return .card
case .bankAccount:
return .bankAccount
@unknown default:
return nil
}
}
}

View File

@ -18,12 +18,12 @@ extension Intent {
}
}
var linkBankOnboardingEnabled: Bool {
var linkFundingSources: Set<LinkSettings.FundingSource>? {
switch self {
case .paymentIntent(let paymentIntent):
return paymentIntent.linkSettings?.bankOnboardingEnabled ?? false
return paymentIntent.linkSettings?.fundingSources
case .setupIntent(let setupIntent):
return setupIntent.linkSettings?.bankOnboardingEnabled ?? false
return setupIntent.linkSettings?.fundingSources
}
}

View File

@ -11,16 +11,20 @@ import Foundation
/// For internal SDK use only
@objc(STP_Internal_LinkSettings)
@_spi(STP) public final class LinkSettings: NSObject, STPAPIResponseDecodable {
@_spi(STP) public enum FundingSource: String {
case card = "CARD"
case bankAccount = "BANK_ACCOUNT"
}
@_spi(STP) public let bankOnboardingEnabled: Bool
@_spi(STP) public let fundingSources: Set<FundingSource>
@_spi(STP) public let allResponseFields: [AnyHashable: Any]
@_spi(STP) public init(
bankOnboardingEnabled: Bool,
fundingSources: Set<FundingSource>,
allResponseFields: [AnyHashable: Any]
) {
self.bankOnboardingEnabled = bankOnboardingEnabled
self.fundingSources = fundingSources
self.allResponseFields = allResponseFields
}
@ -29,13 +33,16 @@ import Foundation
) -> Self? {
guard
let response = response,
let bankOnboardingEnabled = response["link_bank_onboarding_enabled"] as? Bool
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))
return LinkSettings(
bankOnboardingEnabled: bankOnboardingEnabled,
fundingSources: validFundingSources,
allResponseFields: response
) as? Self
}

View File

@ -106,10 +106,17 @@ class PayWithLinkViewController_WalletViewModelTests: XCTestCase {
extension PayWithLinkViewController_WalletViewModelTests {
func makeSUT() throws -> PayWithLinkViewController.WalletViewModel {
// Link settings don't live in the PaymentIntent object itself, but in the /elements/sessions API response
// So we construct a minimal response (see STPPaymentIntentTest.testDecodedObjectFromAPIResponseMapping) to parse them
let paymentIntentJson = try XCTUnwrap(STPTestUtils.jsonNamed(STPTestJSONPaymentIntent))
let orderedPaymentJson = ["card", "link"]
let paymentIntentResponse = ["payment_intent": paymentIntentJson,
"ordered_payment_method_types": orderedPaymentJson] as [String : Any]
let linkSettingsJson = ["link_funding_sources": ["CARD"]]
let response = ["payment_method_preference": paymentIntentResponse,
"link_settings": linkSettingsJson]
let paymentIntent = try XCTUnwrap(
STPPaymentIntent.decodedObject(
fromAPIResponse: STPTestUtils.jsonNamed(STPTestJSONPaymentIntent)
)
STPPaymentIntent.decodedObject(fromAPIResponse: response)
)
return PayWithLinkViewController.WalletViewModel(