Skip to content

Commit

Permalink
Allow instant_or_skip verification for ACH
Browse files Browse the repository at this point in the history
  • Loading branch information
tillh-stripe committed Nov 22, 2024
1 parent 5c60031 commit 51c82c1
Show file tree
Hide file tree
Showing 9 changed files with 62 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ sealed class FinancialConnectionsSheetResult : Parcelable {
*/
@Parcelize
data class Completed(
val financialConnectionsSession: FinancialConnectionsSession
val financialConnectionsSession: FinancialConnectionsSession,
val usesMicrodeposits: Boolean,
) : FinancialConnectionsSheetResult()

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,10 @@ internal class FinancialConnectionsSheetViewModel @Inject constructor(
}.onSuccess {
finishWithResult(
state = state,
result = Completed(financialConnectionsSession = it)
result = Completed(
financialConnectionsSession = it,
usesMicrodeposits = state.manifest?.manualEntryUsesMicrodeposits ?: false,
)
)
}.onFailure { error ->
finishWithResult(stateFlow.value, Failed(error))
Expand All @@ -322,7 +325,11 @@ internal class FinancialConnectionsSheetViewModel @Inject constructor(
}.onSuccess { (las, token) ->
finishWithResult(
state = state,
result = Completed(financialConnectionsSession = las, token = token)
result = Completed(
financialConnectionsSession = las,
token = token,
usesMicrodeposits = state.manifest?.manualEntryUsesMicrodeposits ?: false,
)
)
}.onFailure { error ->
finishWithResult(stateFlow.value, Failed(error))
Expand Down Expand Up @@ -458,7 +465,8 @@ internal class FinancialConnectionsSheetViewModel @Inject constructor(
bankName = url.getQueryParameter(QUERY_BANK_NAME)
),
financialConnectionsSession = null,
token = null
token = null,
usesMicrodeposits = false,
)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ internal sealed class FinancialConnectionsSheetActivityResult : Parcelable {
* @param financialConnectionsSession The financial connections session connected
*/
@Parcelize
data class Completed(
data class Completed constructor(
// Instant Debits sessions: return payment method id and bank details.
val instantDebits: InstantDebitsResult? = null,
// non-Link sessions: return full LinkedAccountSession
val financialConnectionsSession: FinancialConnectionsSession? = null,
// Bank account Token sessions: session + token.
val token: Token? = null
val token: Token? = null,
// Temporary field to expose to callers whether the session used microdeposits.
val usesMicrodeposits: Boolean // = false,
) : FinancialConnectionsSheetActivityResult()

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ internal class FinancialConnectionsSheetForDataContract :
)

else -> FinancialConnectionsSheetResult.Completed(
financialConnectionsSession = financialConnectionsSession
financialConnectionsSession = financialConnectionsSession,
usesMicrodeposits = usesMicrodeposits,
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ internal class FinancialConnectionsSheetNativeViewModel @Inject constructor(
if (state.isLinkWithStripe) {
handleInstantDebitsCompletion(session)
} else {
handleFinancialConnectionsCompletion(session)
handleFinancialConnectionsCompletion(session, state.manualEntryUsesMicrodeposits)
}
}

Expand Down Expand Up @@ -364,7 +364,10 @@ internal class FinancialConnectionsSheetNativeViewModel @Inject constructor(
}
}

private fun handleFinancialConnectionsCompletion(session: FinancialConnectionsSession) {
private fun handleFinancialConnectionsCompletion(
session: FinancialConnectionsSession,
manualEntryUsesMicrodeposits: Boolean,
) {
FinancialConnections.emitEvent(
name = Name.SUCCESS,
metadata = Metadata(
Expand All @@ -374,7 +377,8 @@ internal class FinancialConnectionsSheetNativeViewModel @Inject constructor(
finishWithResult(
Completed(
financialConnectionsSession = session,
token = session.parsedToken
token = session.parsedToken,
usesMicrodeposits = manualEntryUsesMicrodeposits,
)
)
}
Expand All @@ -385,7 +389,10 @@ internal class FinancialConnectionsSheetNativeViewModel @Inject constructor(
}

val result = if (instantDebits != null) {
Completed(instantDebits = instantDebits)
Completed(
instantDebits = instantDebits,
usesMicrodeposits = false,
)
} else {
Failed(
error = UnclassifiedError(
Expand Down Expand Up @@ -514,6 +521,7 @@ internal data class FinancialConnectionsSheetNativeState(
val initialPane: Pane,
val theme: Theme,
val isLinkWithStripe: Boolean,
val manualEntryUsesMicrodeposits: Boolean,
val elementsSessionContext: ElementsSessionContext?,
) {

Expand All @@ -535,6 +543,7 @@ internal data class FinancialConnectionsSheetNativeState(
theme = args.initialSyncResponse.manifest.theme?.toLocalTheme() ?: Theme.default,
viewEffect = null,
isLinkWithStripe = args.initialSyncResponse.manifest.isLinkWithStripe ?: false,
manualEntryUsesMicrodeposits = args.initialSyncResponse.manifest.manualEntryUsesMicrodeposits,
elementsSessionContext = args.elementsSessionContext,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ data class CollectBankAccountResponseInternal(
@Parcelize
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
data class USBankAccountData(
val financialConnectionsSession: FinancialConnectionsSession
val financialConnectionsSession: FinancialConnectionsSession,
val usesMicrodeposits: Boolean,
) : StripeModel

@Parcelize
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,11 @@ internal class CollectBankAccountViewModel @Inject constructor(
is FinancialConnectionsSheetResult.Failed -> finishWithError(result.error)

is FinancialConnectionsSheetResult.Completed -> when {
args.attachToIntent -> attachSessionToIntent(result.financialConnectionsSession)
else -> finishWithSession(result.financialConnectionsSession)
args.attachToIntent -> attachSessionToIntent(
financialConnectionsSession = result.financialConnectionsSession,
usesMicrodeposits = result.usesMicrodeposits,
)
else -> finishWithSession(result.financialConnectionsSession, result.usesMicrodeposits)
}
}
}
Expand Down Expand Up @@ -173,13 +176,15 @@ internal class CollectBankAccountViewModel @Inject constructor(
}

private fun finishWithSession(
financialConnectionsSession: FinancialConnectionsSession
financialConnectionsSession: FinancialConnectionsSession,
usesMicrodeposits: Boolean,
) {
finishWithRefreshedIntent { intent ->
CollectBankAccountResponseInternal(
intent = intent,
usBankAccountData = USBankAccountData(
financialConnectionsSession
financialConnectionsSession = financialConnectionsSession,
usesMicrodeposits = usesMicrodeposits,
),
instantDebitsData = null,
)
Expand Down Expand Up @@ -237,7 +242,10 @@ internal class CollectBankAccountViewModel @Inject constructor(
}
}

private fun attachSessionToIntent(financialConnectionsSession: FinancialConnectionsSession) {
private fun attachSessionToIntent(
financialConnectionsSession: FinancialConnectionsSession,
usesMicrodeposits: Boolean,
) {
viewModelScope.launch {
when (args) {
is CollectBankAccountContract.Args.ForDeferredPaymentIntent,
Expand All @@ -264,7 +272,10 @@ internal class CollectBankAccountViewModel @Inject constructor(
Completed(
CollectBankAccountResponseInternal(
intent = stripeIntent,
usBankAccountData = USBankAccountData(financialConnectionsSession),
usBankAccountData = USBankAccountData(
financialConnectionsSession = financialConnectionsSession,
usesMicrodeposits = usesMicrodeposits,
),
instantDebitsData = null
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,18 @@ internal enum class AddPaymentMethodRequirement {
/** Requires a valid us bank verification method. */
ValidUsBankVerificationMethod {
override fun isMetBy(metadata: PaymentMethodMetadata): Boolean {
// Verification method is always 'automatic' for deferred intents
val isDeferred = metadata.stripeIntent.clientSecret == null
return isDeferred || supportedVerificationMethodForNonDeferredIntent(metadata)
}

private fun supportedVerificationMethodForNonDeferredIntent(
metadata: PaymentMethodMetadata,
): Boolean {
val pmo = metadata.stripeIntent.getPaymentMethodOptions()[USBankAccount.code]
val verificationMethod = (pmo as? Map<*, *>)?.get("verification_method") as? String
val supportsVerificationMethod = verificationMethod in setOf("instant", "automatic")
val isDeferred = metadata.stripeIntent.clientSecret == null
return supportsVerificationMethod || isDeferred
val supportsVerificationMethod = verificationMethod in setOf("automatic", "instant", "instant_or_skip")
return supportsVerificationMethod
}
},

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ internal class USBankAccountFormViewModel @Inject internal constructor(
intentId = intentId,
financialConnectionsSessionId = usBankAccountData.financialConnectionsSession.id,
mandateText = buildMandateText(isVerifyWithMicrodeposits = true),
isVerifyingWithMicrodeposits = true,
isVerifyingWithMicrodeposits = usBankAccountData.usesMicrodeposits,
)
)
}
Expand Down

0 comments on commit 51c82c1

Please sign in to comment.