Skip to content

Commit

Permalink
[FC] Prepares integrity on first sync call (#9818)
Browse files Browse the repository at this point in the history
# Summary
Call `Integrity#prepare` on Activity A (`FinancialConnectionsSheetActivity`) and reuse the same Integrity instance in Activity B (`FinancialConnectionsNativeActivity`) when generating tokens.

# Motivation

https://docs.google.com/document/d/1joKz5UZHLVazmecfMHbq6gB6n4wj5u8To6AtqYgq_tc/edit?tab=t.0#heading=h.cz1xkpga7giy

# Testing
- [x] Manually verified
- [x] Added error tracking for failed integrity checks
  • Loading branch information
carlosmuvi-stripe authored Jan 10, 2025
1 parent 923851d commit 1a9b1e7
Show file tree
Hide file tree
Showing 9 changed files with 85 additions and 8 deletions.
7 changes: 7 additions & 0 deletions example/dependencies/dependencies.txt
Original file line number Diff line number Diff line change
Expand Up @@ -956,6 +956,13 @@
| +--- project :stripe-ui-core (*)
| +--- project :payments-model (*)
| +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.21 (*)
| +--- project :stripe-attestation
| | +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.21 (*)
| | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0 (*)
| | \--- com.google.android.play:integrity:1.4.0
| | +--- com.google.android.gms:play-services-basement:18.4.0 (*)
| | +--- com.google.android.gms:play-services-tasks:18.2.0 (*)
| | \--- com.google.android.play:core-common:2.0.4
| +--- androidx.activity:activity-ktx:1.8.2 (*)
| +--- androidx.annotation:annotation:1.9.0 (*)
| +--- androidx.appcompat:appcompat:1.7.0 (*)
Expand Down
17 changes: 12 additions & 5 deletions financial-connections-example/dependencies/dependencies.txt
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,17 @@
| | +--- org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3 (*)
| | \--- org.jetbrains.kotlin:kotlin-parcelize-runtime:2.0.21 (*)
| +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.21 (*)
| +--- project :stripe-attestation
| | +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.21 (*)
| | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0 (*)
| | \--- com.google.android.play:integrity:1.4.0
| | +--- com.google.android.gms:play-services-basement:18.4.0
| | | +--- androidx.collection:collection:1.0.0 -> 1.4.0 (*)
| | | +--- androidx.core:core:1.2.0 -> 1.13.1 (*)
| | | \--- androidx.fragment:fragment:1.1.0 -> 1.8.4 (*)
| | +--- com.google.android.gms:play-services-tasks:18.2.0
| | | \--- com.google.android.gms:play-services-basement:18.4.0 (*)
| | \--- com.google.android.play:core-common:2.0.4
| +--- androidx.activity:activity-ktx:1.8.2 (*)
| +--- androidx.annotation:annotation:1.9.0 (*)
| +--- androidx.appcompat:appcompat:1.7.0 (*)
Expand Down Expand Up @@ -847,11 +858,7 @@
| +--- org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.7.3 -> 1.9.0
| | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0 (*)
| | +--- org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.9.0 (*)
| | +--- com.google.android.gms:play-services-tasks:16.0.1 -> 18.2.0
| | | \--- com.google.android.gms:play-services-basement:18.4.0
| | | +--- androidx.collection:collection:1.0.0 -> 1.4.0 (*)
| | | +--- androidx.core:core:1.2.0 -> 1.13.1 (*)
| | | \--- androidx.fragment:fragment:1.1.0 -> 1.8.4 (*)
| | +--- com.google.android.gms:play-services-tasks:16.0.1 -> 18.2.0 (*)
| | \--- org.jetbrains.kotlin:kotlin-stdlib:2.0.0 -> 2.0.21 (*)
| +--- com.google.android.material:material:1.12.0 (*)
| +--- com.google.android.gms:play-services-wallet:19.4.0
Expand Down
1 change: 1 addition & 0 deletions financial-connections/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ dependencies {
api project(":stripe-core")
api project(":stripe-ui-core")
api project(":payments-model")
implementation project(":stripe-attestation")

implementation libs.androidx.activity
implementation libs.androidx.annotation
Expand Down
11 changes: 11 additions & 0 deletions financial-connections/dependencies/dependencies.txt
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,17 @@
| +--- org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3 (*)
| \--- org.jetbrains.kotlin:kotlin-parcelize-runtime:2.0.21 (*)
+--- org.jetbrains.kotlin:kotlin-stdlib:2.0.21 (*)
+--- project :stripe-attestation
| +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.21 (*)
| +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0 (*)
| \--- com.google.android.play:integrity:1.4.0
| +--- com.google.android.gms:play-services-basement:18.4.0
| | +--- androidx.collection:collection:1.0.0 -> 1.4.0 (*)
| | +--- androidx.core:core:1.2.0 -> 1.13.1 (*)
| | \--- androidx.fragment:fragment:1.1.0 -> 1.5.4 (*)
| +--- com.google.android.gms:play-services-tasks:18.2.0
| | \--- com.google.android.gms:play-services-basement:18.4.0 (*)
| \--- com.google.android.play:core-common:2.0.4
+--- androidx.activity:activity-ktx:1.8.2 (*)
+--- androidx.annotation:annotation:1.9.0 (*)
+--- androidx.appcompat:appcompat:1.7.0 (*)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import com.stripe.android.financialconnections.FinancialConnectionsSheetViewEffe
import com.stripe.android.financialconnections.FinancialConnectionsSheetViewEffect.OpenAuthFlowWithUrl
import com.stripe.android.financialconnections.FinancialConnectionsSheetViewEffect.OpenNativeAuthFlow
import com.stripe.android.financialconnections.FinancialConnectionsSheetViewModel.Companion.QUERY_PARAM_PAYMENT_METHOD
import com.stripe.android.financialconnections.analytics.FinancialConnectionsAnalyticsEvent
import com.stripe.android.financialconnections.analytics.FinancialConnectionsAnalyticsTracker
import com.stripe.android.financialconnections.analytics.FinancialConnectionsEvent.ErrorCode
import com.stripe.android.financialconnections.analytics.FinancialConnectionsEvent.Metadata
Expand Down Expand Up @@ -58,6 +59,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.attestation.IntegrityRequestManager
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
Expand All @@ -68,6 +70,7 @@ internal class FinancialConnectionsSheetViewModel @Inject constructor(
@Named(APPLICATION_ID) private val applicationId: String,
savedStateHandle: SavedStateHandle,
private val getOrFetchSync: GetOrFetchSync,
private val integrityRequestManager: IntegrityRequestManager,
private val fetchFinancialConnectionsSession: FetchFinancialConnectionsSession,
private val fetchFinancialConnectionsSessionForToken: FetchFinancialConnectionsSessionForToken,
private val logger: Logger,
Expand All @@ -86,7 +89,9 @@ internal class FinancialConnectionsSheetViewModel @Inject constructor(
if (initialState.initialArgs.isValid()) {
eventReporter.onPresented(initialState.initialArgs.configuration)
// avoid re-fetching manifest if already exists (this will happen on process recreations)
if (initialState.manifest == null) fetchManifest()
if (initialState.manifest == null) {
initAuthFlow()
}
} else {
val result = Failed(
IllegalStateException("Invalid configuration provided when instantiating activity")
Expand All @@ -108,9 +113,10 @@ internal class FinancialConnectionsSheetViewModel @Inject constructor(
* Fetches the [FinancialConnectionsSessionManifest] from the Stripe API to get the hosted auth flow URL
* as well as the success and cancel callback URLs to verify.
*/
private fun fetchManifest() {
private fun initAuthFlow() {
viewModelScope.launch {
kotlin.runCatching {
prepareStandardRequestManager()
getOrFetchSync(refetchCondition = Always)
}.onFailure {
finishWithResult(stateFlow.value, Failed(it))
Expand All @@ -120,6 +126,20 @@ internal class FinancialConnectionsSheetViewModel @Inject constructor(
}
}

private suspend fun prepareStandardRequestManager(): Boolean {
val result = integrityRequestManager.prepare()
result.onFailure {
analyticsTracker.track(
FinancialConnectionsAnalyticsEvent.Error(
extraMessage = "Failed to warm up the IntegrityStandardRequestManager",
pane = Pane.CONSENT,
exception = it
)
)
}
return result.isSuccess
}

/**
* Builds the ChromeCustomTab intent to launch the hosted auth flow and launches it.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
package com.stripe.android.financialconnections.di

import android.app.Application
import com.stripe.android.core.Logger
import com.stripe.attestation.BuildConfig
import com.stripe.attestation.IntegrityRequestManager
import com.stripe.attestation.IntegrityStandardRequestManager
import com.stripe.attestation.RealStandardIntegrityManagerFactory
import dagger.BindsInstance
import dagger.Component
import dagger.Module
import dagger.Provides
import javax.inject.Singleton

/**
Expand Down Expand Up @@ -32,11 +38,24 @@ internal object FinancialConnectionsSingletonSharedComponentHolder {
@Component(modules = [FinancialConnectionsSingletonSharedModule::class])
internal interface FinancialConnectionsSingletonSharedComponent {

fun providesIntegrityRequestManager(): IntegrityRequestManager

@Component.Factory
interface Factory {
fun create(@BindsInstance application: Application): FinancialConnectionsSingletonSharedComponent
}
}

@Module
internal class FinancialConnectionsSingletonSharedModule
internal class FinancialConnectionsSingletonSharedModule {

@Provides
@Singleton
fun providesIntegrityStandardRequestManager(
context: Application
): IntegrityRequestManager = IntegrityStandardRequestManager(
cloudProjectNumber = 527113280969, // stripe-financial-connections
logError = { message, error -> Logger.getInstance(BuildConfig.DEBUG).error(message, error) },
factory = RealStandardIntegrityManagerFactory(context)
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -821,6 +821,7 @@ class FinancialConnectionsSheetViewModelTest {
browserManager = browserManager,
savedStateHandle = SavedStateHandle(),
nativeAuthFlowCoordinator = mock(),
integrityRequestManager = mock(),
logger = Logger.noop()
)
}
Expand Down
7 changes: 7 additions & 0 deletions paymentsheet-example/dependencies/dependencies.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1100,6 +1100,13 @@
| +--- project :stripe-ui-core (*)
| +--- project :payments-model (*)
| +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.21 (*)
| +--- project :stripe-attestation
| | +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.21 (*)
| | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0 (*)
| | \--- com.google.android.play:integrity:1.4.0
| | +--- com.google.android.gms:play-services-basement:18.4.0 (*)
| | +--- com.google.android.gms:play-services-tasks:18.2.0 (*)
| | \--- com.google.android.play:core-common:2.0.4
| +--- androidx.activity:activity-ktx:1.8.2 (*)
| +--- androidx.annotation:annotation:1.9.0 (*)
| +--- androidx.appcompat:appcompat:1.7.0 (*)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ class IntegrityStandardRequestManager(
private var integrityTokenProvider: StandardIntegrityTokenProvider? = null

override suspend fun prepare(): Result<Unit> = runCatching {
if (integrityTokenProvider != null) {
return Result.success(Unit)
}

val finishedTask: Task<StandardIntegrityTokenProvider> = standardIntegrityManager
.prepareIntegrityToken(
PrepareIntegrityTokenRequest.builder()
Expand Down

0 comments on commit 1a9b1e7

Please sign in to comment.