Skip to content

Commit

Permalink
Updates lookup call to use mobile endpoint on verified flows
Browse files Browse the repository at this point in the history
  • Loading branch information
carlosmuvi-stripe committed Dec 23, 2024
1 parent ade027d commit d45c6e2
Show file tree
Hide file tree
Showing 11 changed files with 126 additions and 32 deletions.
4 changes: 1 addition & 3 deletions .idea/codestyles/Project.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -442,9 +442,7 @@ enum class Merchant(
Networking("networking"),
LiveTesting("live_testing", canSwitchBetweenTestAndLive = false),
TestMode("testmode", canSwitchBetweenTestAndLive = false),
NmeDefaultVerification("nme", canSwitchBetweenTestAndLive = true),
NmeABAVVerification("nme_abav", canSwitchBetweenTestAndLive = true),
NmeSkipVerification("nme_skip", canSwitchBetweenTestAndLive = true),
Trusted("trusted", canSwitchBetweenTestAndLive = false),
Custom("other");

companion object {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,38 @@
package com.stripe.android.financialconnections.domain

import android.app.Application
import com.stripe.android.financialconnections.FinancialConnectionsSheet
import com.stripe.android.financialconnections.repository.FinancialConnectionsConsumerSessionRepository
import com.stripe.android.model.ConsumerSessionLookup
import com.stripe.attestation.IntegrityRequestManager
import javax.inject.Inject

internal class LookupAccount @Inject constructor(
private val application: Application,
private val integrityRequestManager: IntegrityRequestManager,
private val consumerSessionRepository: FinancialConnectionsConsumerSessionRepository,
val configuration: FinancialConnectionsSheet.Configuration,
) {

suspend operator fun invoke(
email: String
): ConsumerSessionLookup = requireNotNull(
consumerSessionRepository.lookupConsumerSession(
email = email.lowercase().trim(),
clientSecret = configuration.financialConnectionsSessionClientSecret
)
)
email: String,
verifiedFlow: Boolean
): ConsumerSessionLookup {
return if (verifiedFlow) {
requireNotNull(
consumerSessionRepository.mobileLookupConsumerSession(
email = email.lowercase().trim(),
verificationToken = integrityRequestManager.requestToken().getOrThrow(),
appId = application.packageName
)
)
} else {
requireNotNull(
consumerSessionRepository.postConsumerSession(
email = email.lowercase().trim(),
clientSecret = configuration.financialConnectionsSessionClientSecret
)
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,14 @@ internal class LookupConsumerAndStartVerification @Inject constructor(
email: String,
businessName: String?,
verificationType: VerificationType,
appVerificationEnabled: Boolean,
onConsumerNotFound: suspend () -> Unit,
onLookupError: suspend (Throwable) -> Unit,
onStartVerification: suspend () -> Unit,
onVerificationStarted: suspend (ConsumerSession) -> Unit,
onStartVerificationError: suspend (Throwable) -> Unit
) {
runCatching { lookupAccount(email) }
runCatching { lookupAccount(email, appVerificationEnabled) }
.onSuccess { session ->
if (session.exists) {
onStartVerification()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ internal class NetworkingLinkSignupPreviewParameterProvider :
initiallySelectedCountryCode = null,
),
isInstantDebits = false,
appVerificationEnabled = false,
content = networkingLinkSignupPane(),
)
),
Expand All @@ -49,6 +50,7 @@ internal class NetworkingLinkSignupPreviewParameterProvider :
initiallySelectedCountryCode = null,
),
isInstantDebits = false,
appVerificationEnabled = false,
content = networkingLinkSignupPane(),
)
),
Expand All @@ -74,6 +76,7 @@ internal class NetworkingLinkSignupPreviewParameterProvider :
initiallySelectedCountryCode = null,
),
isInstantDebits = false,
appVerificationEnabled = false,
content = networkingLinkSignupPane(),
)
),
Expand All @@ -99,6 +102,7 @@ internal class NetworkingLinkSignupPreviewParameterProvider :
initiallySelectedCountryCode = null,
),
isInstantDebits = true,
appVerificationEnabled = false,
content = linkLoginPane(),
)
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ internal class NetworkingLinkSignupViewModel @AssistedInject constructor(
NetworkingLinkSignupState.Payload(
content = requireNotNull(content),
merchantName = sync.manifest.getBusinessName(),
appVerificationEnabled = sync.manifest.appVerificationEnabled,
emailController = SimpleTextFieldController(
textFieldConfig = EmailConfig(label = R.string.stripe_networking_signup_email_label),
initialValue = sync.manifest.accountholderCustomerEmailAddress ?: prefillDetails?.email,
Expand Down Expand Up @@ -195,15 +196,18 @@ internal class NetworkingLinkSignupViewModel @AssistedInject constructor(
/**
* @param validEmail valid email, or null if entered email is invalid.
*/
private suspend fun onEmailEntered(
private fun onEmailEntered(
validEmail: String?
) {
setState { copy(validEmail = validEmail) }
if (validEmail != null) {
logger.debug("VALID EMAIL ADDRESS $validEmail.")
searchJob += suspend {
delay(getLookupDelayMs(validEmail))
lookupAccount(validEmail)
lookupAccount(
email = validEmail,
verifiedFlow = stateFlow.value.payload()?.appVerificationEnabled == true
)
}.execute { copy(lookupAccount = if (it.isCancellationError()) Uninitialized else it) }
} else {
setState { copy(lookupAccount = Uninitialized) }
Expand Down Expand Up @@ -342,6 +346,7 @@ internal data class NetworkingLinkSignupState(
data class Payload(
val merchantName: String?,
val emailController: SimpleTextFieldController,
val appVerificationEnabled: Boolean,
val phoneController: PhoneNumberController,
val isInstantDebits: Boolean,
val content: Content,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ internal class NetworkingLinkVerificationViewModel @AssistedInject constructor(
return InitData(
businessName = manifest.businessName,
emailAddress = requireNotNull(email),
appVerificationEnabled = manifest.appVerificationEnabled,
initialInstitution = manifest.initialInstitution,
)
}
Expand All @@ -108,6 +109,7 @@ internal class NetworkingLinkVerificationViewModel @AssistedInject constructor(
) {
lookupConsumerAndStartVerification(
email = initData.emailAddress,
appVerificationEnabled = initData.appVerificationEnabled,
businessName = initData.businessName,
verificationType = VerificationType.SMS,
onConsumerNotFound = {
Expand Down Expand Up @@ -228,6 +230,7 @@ internal class NetworkingLinkVerificationViewModel @AssistedInject constructor(
private data class InitData(
val businessName: String?,
val emailAddress: String,
val appVerificationEnabled: Boolean,
val initialInstitution: FinancialConnectionsInstitution?,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ internal data class FinancialConnectionsSessionManifest(
@SerialName(value = "institution_search_disabled")
val institutionSearchDisabled: Boolean,

val appVerificationEnabled: Boolean = true,

@SerialName(value = "livemode")
val livemode: Boolean,

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ import com.stripe.android.financialconnections.ui.toLocalTheme
import com.stripe.android.financialconnections.utils.UriUtils
import com.stripe.android.financialconnections.utils.get
import com.stripe.android.financialconnections.utils.updateWithNewEntry
import com.stripe.attestation.IntegrityRequestManager
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
Expand All @@ -86,6 +87,7 @@ internal class FinancialConnectionsSheetNativeViewModel @Inject constructor(
savedStateHandle: SavedStateHandle,
private val nativeAuthFlowCoordinator: NativeAuthFlowCoordinator,
private val uriUtils: UriUtils,
private val integrityRequestManager: IntegrityRequestManager,
private val completeFinancialConnectionsSession: CompleteFinancialConnectionsSession,
private val createInstantDebitsResult: CreateInstantDebitsResult,
private val eventTracker: FinancialConnectionsAnalyticsTracker,
Expand Down Expand Up @@ -124,6 +126,7 @@ internal class FinancialConnectionsSheetNativeViewModel @Inject constructor(
init {
savedStateHandle.registerSavedStateProvider()
setState { copy(firstInit = false) }
prepareIntegrityRequestManager()
viewModelScope.launch {
nativeAuthFlowCoordinator().collect { message ->
when (message) {
Expand All @@ -147,6 +150,12 @@ internal class FinancialConnectionsSheetNativeViewModel @Inject constructor(
}
}

private fun prepareIntegrityRequestManager() {
viewModelScope.launch {
integrityRequestManager.prepare()
}
}

private fun SavedStateHandle.registerSavedStateProvider() {
setSavedStateProvider(KEY_SAVED_STATE) {
val state = stateFlow.value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,23 @@ internal interface FinancialConnectionsConsumerSessionRepository {

suspend fun getCachedConsumerSession(): CachedConsumerSession?

suspend fun postConsumerSession(
email: String,
clientSecret: String
): ConsumerSessionLookup

suspend fun mobileLookupConsumerSession(
email: String,
verificationToken: String,
appId: String
): ConsumerSessionLookup

suspend fun signUp(
email: String,
phoneNumber: String,
country: String,
): ConsumerSessionSignup

suspend fun lookupConsumerSession(
email: String,
clientSecret: String
): ConsumerSessionLookup

suspend fun startConsumerVerification(
consumerSessionClientSecret: String,
connectionsMerchantName: String?,
Expand Down Expand Up @@ -123,15 +129,6 @@ private class FinancialConnectionsConsumerSessionRepositoryImpl(
consumerSessionRepository.provideConsumerSession()
}

override suspend fun lookupConsumerSession(
email: String,
clientSecret: String
): ConsumerSessionLookup = mutex.withLock {
postConsumerSession(email, clientSecret).also { lookup ->
updateCachedConsumerSessionFromLookup(lookup)
}
}

override suspend fun signUp(
email: String,
phoneNumber: String,
Expand Down Expand Up @@ -238,14 +235,30 @@ private class FinancialConnectionsConsumerSessionRepositoryImpl(
).getOrThrow()
}

private suspend fun postConsumerSession(
override suspend fun postConsumerSession(
email: String,
clientSecret: String
): ConsumerSessionLookup = financialConnectionsConsumersApiService.postConsumerSession(
email = email,
clientSecret = clientSecret,
requestSurface = requestSurface,
)
).also {
updateCachedConsumerSessionFromLookup(it)
}

override suspend fun mobileLookupConsumerSession(
email: String,
verificationToken: String,
appId: String
): ConsumerSessionLookup = consumersApiService.mobileLookupConsumerSession(
email = email,
verificationToken = verificationToken,
appId = appId,
requestSurface = requestSurface,
requestOptions = provideApiRequestOptions(useConsumerPublishableKey = false),
).also {
updateCachedConsumerSessionFromLookup(it)
}

private fun updateCachedConsumerSession(
source: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import com.stripe.android.model.parsers.ConsumerSessionLookupJsonParser
import com.stripe.android.model.parsers.ConsumerSessionSignupJsonParser
import com.stripe.android.model.parsers.SharePaymentDetailsJsonParser
import java.util.Locale
import java.util.UUID

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
interface ConsumersApiService {
Expand All @@ -50,6 +51,14 @@ interface ConsumersApiService {
requestOptions: ApiRequest.Options
): ConsumerSessionLookup

suspend fun mobileLookupConsumerSession(
email: String,
requestSurface: String,
verificationToken: String,
appId: String,
requestOptions: ApiRequest.Options
): ConsumerSessionLookup

suspend fun startConsumerVerification(
consumerSessionClientSecret: String,
locale: Locale,
Expand Down Expand Up @@ -176,6 +185,35 @@ class ConsumersApiServiceImpl(
)
}

/**
* Retrieves the ConsumerSession if the given email is associated with a Link account.
*/
override suspend fun mobileLookupConsumerSession(
email: String,
requestSurface: String,
verificationToken: String,
appId: String,
requestOptions: ApiRequest.Options
): ConsumerSessionLookup {
return executeRequestWithModelJsonParser(
stripeErrorJsonParser = stripeErrorJsonParser,
stripeNetworkClient = stripeNetworkClient,
request = apiRequestFactory.createPost(
mobileConsumerSessionLookupUrl,
requestOptions,
mapOf(
"request_surface" to requestSurface,
"email_address" to email.lowercase(),
"android_verification_token" to verificationToken,
"session_id" to "12345", // TODO (carlosmuvi): remove this when we have a real session id
"email_source" to "user_action", // TODO (carlosmuvi): remove this when we have a real app id
"app_id" to appId
)
),
responseJsonParser = ConsumerSessionLookupJsonParser()
)
}

/**
* Triggers a verification for the consumer corresponding to the given client secret.
*/
Expand Down Expand Up @@ -328,6 +366,12 @@ class ConsumersApiServiceImpl(
internal val consumerSessionLookupUrl: String =
getApiUrl("consumers/sessions/lookup")

/**
* @return `https://api.stripe.com/v1/consumers/sessions/lookup`
*/
internal val mobileConsumerSessionLookupUrl: String =
getApiUrl("consumers/mobile/sessions/lookup")

/**
* @return `https://api.stripe.com/v1/consumers/sessions/start_verification`
*/
Expand Down

0 comments on commit d45c6e2

Please sign in to comment.