diff --git a/example/dependencies/dependencies.txt b/example/dependencies/dependencies.txt index 693b4a10ad8..4449cc766a1 100644 --- a/example/dependencies/dependencies.txt +++ b/example/dependencies/dependencies.txt @@ -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 (*) diff --git a/financial-connections-example/dependencies/dependencies.txt b/financial-connections-example/dependencies/dependencies.txt index beb14bafeeb..aeeaf0e04f8 100644 --- a/financial-connections-example/dependencies/dependencies.txt +++ b/financial-connections-example/dependencies/dependencies.txt @@ -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 (*) @@ -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 diff --git a/financial-connections/build.gradle b/financial-connections/build.gradle index a4000f3b55f..a72d929a7ec 100644 --- a/financial-connections/build.gradle +++ b/financial-connections/build.gradle @@ -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 diff --git a/financial-connections/dependencies/dependencies.txt b/financial-connections/dependencies/dependencies.txt index 39c1da4ee4a..65769fa3ed2 100644 --- a/financial-connections/dependencies/dependencies.txt +++ b/financial-connections/dependencies/dependencies.txt @@ -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 (*) diff --git a/financial-connections/src/main/java/com/stripe/android/financialconnections/FinancialConnectionsSheetViewModel.kt b/financial-connections/src/main/java/com/stripe/android/financialconnections/FinancialConnectionsSheetViewModel.kt index 4c32eb7c616..9280b6f2d93 100644 --- a/financial-connections/src/main/java/com/stripe/android/financialconnections/FinancialConnectionsSheetViewModel.kt +++ b/financial-connections/src/main/java/com/stripe/android/financialconnections/FinancialConnectionsSheetViewModel.kt @@ -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 @@ -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 @@ -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, @@ -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") @@ -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)) @@ -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. * diff --git a/financial-connections/src/main/java/com/stripe/android/financialconnections/di/FinancialConnectionsSingletonSharedComponentHolder.kt b/financial-connections/src/main/java/com/stripe/android/financialconnections/di/FinancialConnectionsSingletonSharedComponentHolder.kt index f3a11b80d2e..20506c22ba2 100644 --- a/financial-connections/src/main/java/com/stripe/android/financialconnections/di/FinancialConnectionsSingletonSharedComponentHolder.kt +++ b/financial-connections/src/main/java/com/stripe/android/financialconnections/di/FinancialConnectionsSingletonSharedComponentHolder.kt @@ -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 /** @@ -32,6 +38,8 @@ internal object FinancialConnectionsSingletonSharedComponentHolder { @Component(modules = [FinancialConnectionsSingletonSharedModule::class]) internal interface FinancialConnectionsSingletonSharedComponent { + fun providesIntegrityRequestManager(): IntegrityRequestManager + @Component.Factory interface Factory { fun create(@BindsInstance application: Application): FinancialConnectionsSingletonSharedComponent @@ -39,4 +47,15 @@ internal interface 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) + ) +} diff --git a/financial-connections/src/test/java/com/stripe/android/financialconnections/FinancialConnectionsSheetViewModelTest.kt b/financial-connections/src/test/java/com/stripe/android/financialconnections/FinancialConnectionsSheetViewModelTest.kt index ed07b47973a..ae728bef74a 100644 --- a/financial-connections/src/test/java/com/stripe/android/financialconnections/FinancialConnectionsSheetViewModelTest.kt +++ b/financial-connections/src/test/java/com/stripe/android/financialconnections/FinancialConnectionsSheetViewModelTest.kt @@ -821,6 +821,7 @@ class FinancialConnectionsSheetViewModelTest { browserManager = browserManager, savedStateHandle = SavedStateHandle(), nativeAuthFlowCoordinator = mock(), + integrityRequestManager = mock(), logger = Logger.noop() ) } diff --git a/paymentsheet-example/dependencies/dependencies.txt b/paymentsheet-example/dependencies/dependencies.txt index 0a0cb90ae27..c6af9188644 100644 --- a/paymentsheet-example/dependencies/dependencies.txt +++ b/paymentsheet-example/dependencies/dependencies.txt @@ -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 (*) diff --git a/stripe-attestation/src/main/java/com/stripe/attestation/IntegrityStandardRequestManager.kt b/stripe-attestation/src/main/java/com/stripe/attestation/IntegrityStandardRequestManager.kt index 3cb6e6dc0cf..e8acdccc9bb 100644 --- a/stripe-attestation/src/main/java/com/stripe/attestation/IntegrityStandardRequestManager.kt +++ b/stripe-attestation/src/main/java/com/stripe/attestation/IntegrityStandardRequestManager.kt @@ -40,6 +40,10 @@ class IntegrityStandardRequestManager( private var integrityTokenProvider: StandardIntegrityTokenProvider? = null override suspend fun prepare(): Result = runCatching { + if (integrityTokenProvider != null) { + return Result.success(Unit) + } + val finishedTask: Task = standardIntegrityManager .prepareIntegrityToken( PrepareIntegrityTokenRequest.builder()