Skip to content

Commit

Permalink
Pass link_mode to Instant Debits web flow (#9321)
Browse files Browse the repository at this point in the history
  • Loading branch information
tillh-stripe authored Sep 27, 2024
1 parent 2d86f55 commit d36b4dc
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import com.stripe.android.financialconnections.navigation.topappbar.TopAppBarSta
import com.stripe.android.financialconnections.presentation.FinancialConnectionsViewModel
import com.stripe.android.financialconnections.ui.FinancialConnectionsSheetNativeActivity
import com.stripe.android.financialconnections.utils.parcelable
import com.stripe.android.model.LinkMode
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
Expand All @@ -72,7 +73,7 @@ internal class FinancialConnectionsSheetViewModel @Inject constructor(
private val analyticsTracker: FinancialConnectionsAnalyticsTracker,
private val nativeRouter: NativeAuthFlowRouter,
nativeAuthFlowCoordinator: NativeAuthFlowCoordinator,
initialState: FinancialConnectionsSheetState,
private val initialState: FinancialConnectionsSheetState,
) : FinancialConnectionsViewModel<FinancialConnectionsSheetState>(initialState, nativeAuthFlowCoordinator) {

private val mutex = Mutex()
Expand Down Expand Up @@ -131,7 +132,13 @@ internal class FinancialConnectionsSheetViewModel @Inject constructor(
val isInstantDebits = stateFlow.value.isInstantDebits
val nativeAuthFlowEnabled = nativeRouter.nativeAuthFlowEnabled(manifest)
nativeRouter.logExposure(manifest)
val hostedAuthUrl = buildHostedAuthUrl(manifest.hostedAuthUrl, isInstantDebits)

val linkMode = initialState.initialArgs.elementsSessionContext?.linkMode
val hostedAuthUrl = buildHostedAuthUrl(
hostedAuthUrl = manifest.hostedAuthUrl,
isInstantDebits = isInstantDebits,
linkMode = linkMode,
)
if (hostedAuthUrl == null) {
finishWithResult(
state = stateFlow.value,
Expand Down Expand Up @@ -166,14 +173,22 @@ internal class FinancialConnectionsSheetViewModel @Inject constructor(

private fun buildHostedAuthUrl(
hostedAuthUrl: String?,
isInstantDebits: Boolean
): String? = when (isInstantDebits) {
/**
* For Instant Debits, add a query parameter to the hosted auth URL so that payment account creation
* takes place on the web side of the flow and the payment method ID is returned to the app.
*/
true -> hostedAuthUrl?.let { "$it&return_payment_method=true" }
false -> hostedAuthUrl
isInstantDebits: Boolean,
linkMode: LinkMode?,
): String? {
if (hostedAuthUrl == null) {
return null
}

val queryParams = mutableListOf(hostedAuthUrl)
if (isInstantDebits) {
// For Instant Debits, add a query parameter to the hosted auth URL so that payment account creation
// takes place on the web side of the flow and the payment method ID is returned to the app.
queryParams.add("return_payment_method=true")
linkMode?.let { queryParams.add("link_mode=${it.value}") }
}

return queryParams.joinToString("&")
}

private fun logNoBrowserAvailableAndFinish() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import com.stripe.android.core.Logger
import com.stripe.android.core.exception.APIException
import com.stripe.android.financialconnections.ApiKeyFixtures.sessionManifest
import com.stripe.android.financialconnections.ApiKeyFixtures.syncResponse
import com.stripe.android.financialconnections.FinancialConnectionsSheet.ElementsSessionContext
import com.stripe.android.financialconnections.FinancialConnectionsSheetState.AuthFlowStatus
import com.stripe.android.financialconnections.FinancialConnectionsSheetViewEffect.FinishWithResult
import com.stripe.android.financialconnections.FinancialConnectionsSheetViewEffect.OpenAuthFlowWithUrl
Expand All @@ -31,6 +32,7 @@ import com.stripe.android.financialconnections.model.FinancialConnectionsAccount
import com.stripe.android.financialconnections.model.FinancialConnectionsSession
import com.stripe.android.financialconnections.model.FinancialConnectionsSession.StatusDetails
import com.stripe.android.financialconnections.presentation.withState
import com.stripe.android.model.LinkMode
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
Expand Down Expand Up @@ -140,19 +142,52 @@ class FinancialConnectionsSheetViewModelTest {
// When
val viewModel = createViewModel(
defaultInitialState.copy(
initialArgs = ForInstantDebits(configuration)
initialArgs = ForInstantDebits(
configuration = configuration,
elementsSessionContext = ElementsSessionContext(
linkMode = LinkMode.LinkPaymentMethod,
),
)
)
)

// Then
withState(viewModel) {
val viewEffect = it.viewEffect as OpenAuthFlowWithUrl
assertThat(viewEffect.url).isEqualTo(
"${syncResponse.manifest.hostedAuthUrl}&return_payment_method=true"
"${syncResponse.manifest.hostedAuthUrl}&return_payment_method=true&link_mode=LINK_PAYMENT_METHOD"
)
}
}

@Test
fun `init - when instant debits flow, hosted auth url doesn't contain link_mode if unknown`() = runTest {
// Given
whenever(browserManager.canOpenHttpsUrl()).thenReturn(true)
whenever(getOrFetchSync(any())).thenReturn(syncResponse)
whenever(nativeRouter.nativeAuthFlowEnabled(any())).thenReturn(false)

// When
val viewModel = createViewModel(
defaultInitialState.copy(
initialArgs = ForInstantDebits(
configuration = configuration,
elementsSessionContext = ElementsSessionContext(
linkMode = null,
),
)
)
)

// Then
withState(viewModel) {
val viewEffect = it.viewEffect as OpenAuthFlowWithUrl
assertThat(viewEffect.url).isEqualTo(
"${syncResponse.manifest.hostedAuthUrl}&return_payment_method=true"
)
}
}

@Test
fun `init - when data flow and non-native, hosted auth url without query params is launched`() = runTest {
// Given
Expand Down

0 comments on commit d36b4dc

Please sign in to comment.