diff --git a/paymentsheet-example/src/main/java/com/stripe/android/paymentsheet/example/playground/settings/LinkTypeSettingsDefinition.kt b/paymentsheet-example/src/main/java/com/stripe/android/paymentsheet/example/playground/settings/LinkTypeSettingsDefinition.kt index fbf26ea07cd..83733261f63 100644 --- a/paymentsheet-example/src/main/java/com/stripe/android/paymentsheet/example/playground/settings/LinkTypeSettingsDefinition.kt +++ b/paymentsheet-example/src/main/java/com/stripe/android/paymentsheet/example/playground/settings/LinkTypeSettingsDefinition.kt @@ -7,7 +7,7 @@ internal object LinkTypeSettingsDefinition : PlaygroundSettingDefinition.Saveable by EnumSaveable( key = "LinkType", values = LinkType.entries.toTypedArray(), - defaultValue = LinkType.Native + defaultValue = LinkType.Web ), PlaygroundSettingDefinition.Displayable { override val displayName: String = "Link Type" diff --git a/paymentsheet/src/main/java/com/stripe/android/customersheet/CustomerAdapter.kt b/paymentsheet/src/main/java/com/stripe/android/customersheet/CustomerAdapter.kt index f15a480f800..6f293bafef0 100644 --- a/paymentsheet/src/main/java/com/stripe/android/customersheet/CustomerAdapter.kt +++ b/paymentsheet/src/main/java/com/stripe/android/customersheet/CustomerAdapter.kt @@ -151,7 +151,7 @@ interface CustomerAdapter { } is Link -> { - PaymentSelection.Link + PaymentSelection.Link() } is StripeId -> { diff --git a/paymentsheet/src/main/java/com/stripe/android/customersheet/CustomerSheetLoader.kt b/paymentsheet/src/main/java/com/stripe/android/customersheet/CustomerSheetLoader.kt index 86a0bda0c4b..89f3a0193bd 100644 --- a/paymentsheet/src/main/java/com/stripe/android/customersheet/CustomerSheetLoader.kt +++ b/paymentsheet/src/main/java/com/stripe/android/customersheet/CustomerSheetLoader.kt @@ -135,7 +135,7 @@ internal class DefaultCustomerSheetLoader( val paymentSelection = customerSheetSession.savedSelection?.let { selection -> when (selection) { is SavedSelection.GooglePay -> PaymentSelection.GooglePay - is SavedSelection.Link -> PaymentSelection.Link + is SavedSelection.Link -> PaymentSelection.Link() is SavedSelection.PaymentMethod -> { paymentMethods.find { paymentMethod -> paymentMethod.id == selection.id diff --git a/paymentsheet/src/main/java/com/stripe/android/link/LinkConfigurationCoordinator.kt b/paymentsheet/src/main/java/com/stripe/android/link/LinkConfigurationCoordinator.kt index 978958ca5fb..910d0a51881 100644 --- a/paymentsheet/src/main/java/com/stripe/android/link/LinkConfigurationCoordinator.kt +++ b/paymentsheet/src/main/java/com/stripe/android/link/LinkConfigurationCoordinator.kt @@ -1,5 +1,6 @@ package com.stripe.android.link +import com.stripe.android.link.gate.LinkGate import com.stripe.android.link.injection.LinkComponent import com.stripe.android.link.model.AccountStatus import com.stripe.android.link.ui.inline.UserInput @@ -22,6 +23,8 @@ internal interface LinkConfigurationCoordinator { fun getAccountStatusFlow(configuration: LinkConfiguration): Flow + fun linkGate(configuration: LinkConfiguration): LinkGate + suspend fun signInWithUserInput( configuration: LinkConfiguration, userInput: UserInput @@ -62,6 +65,10 @@ internal class RealLinkConfigurationCoordinator @Inject internal constructor( override fun getAccountStatusFlow(configuration: LinkConfiguration): Flow = getLinkPaymentLauncherComponent(configuration).linkAccountManager.accountStatus + override fun linkGate(configuration: LinkConfiguration): LinkGate { + return getLinkPaymentLauncherComponent(configuration).linkGate + } + /** * Trigger Link sign in with the input collected from the user inline in PaymentSheet, whether * it's a new or existing account. diff --git a/paymentsheet/src/main/java/com/stripe/android/link/LinkPaymentLauncher.kt b/paymentsheet/src/main/java/com/stripe/android/link/LinkPaymentLauncher.kt index 95eff0ca6cb..0440cc5b0c8 100644 --- a/paymentsheet/src/main/java/com/stripe/android/link/LinkPaymentLauncher.kt +++ b/paymentsheet/src/main/java/com/stripe/android/link/LinkPaymentLauncher.kt @@ -73,12 +73,13 @@ internal class LinkPaymentLauncher @Inject internal constructor( */ fun present( configuration: LinkConfiguration, - linkAccount: LinkAccount? + linkAccount: LinkAccount?, + useLinkExpress: Boolean ) { val args = LinkActivityContract.Args( configuration = configuration, - startWithVerificationDialog = false, - linkAccount = linkAccount + linkAccount = linkAccount, + startWithVerificationDialog = useLinkExpress ) linkActivityResultLauncher?.launch(args) analyticsHelper.onLinkLaunched() diff --git a/paymentsheet/src/main/java/com/stripe/android/link/gate/DefaultLinkGate.kt b/paymentsheet/src/main/java/com/stripe/android/link/gate/DefaultLinkGate.kt index fdba8aae800..affd32eef1f 100644 --- a/paymentsheet/src/main/java/com/stripe/android/link/gate/DefaultLinkGate.kt +++ b/paymentsheet/src/main/java/com/stripe/android/link/gate/DefaultLinkGate.kt @@ -23,6 +23,11 @@ internal class DefaultLinkGate @Inject constructor( return FeatureFlags.nativeLinkAttestationEnabled.isEnabled } + override val suppress2faModal: Boolean + get() { + return useNativeLink.not() || configuration.suppress2faModal + } + class Factory @Inject constructor() : LinkGate.Factory { override fun create(configuration: LinkConfiguration): LinkGate { return DefaultLinkGate(configuration) diff --git a/paymentsheet/src/main/java/com/stripe/android/link/gate/LinkGate.kt b/paymentsheet/src/main/java/com/stripe/android/link/gate/LinkGate.kt index 849ec4fc543..6f6ced3f2d5 100644 --- a/paymentsheet/src/main/java/com/stripe/android/link/gate/LinkGate.kt +++ b/paymentsheet/src/main/java/com/stripe/android/link/gate/LinkGate.kt @@ -5,6 +5,7 @@ import com.stripe.android.link.LinkConfiguration internal interface LinkGate { val useNativeLink: Boolean val useAttestationEndpoints: Boolean + val suppress2faModal: Boolean fun interface Factory { fun create(configuration: LinkConfiguration): LinkGate diff --git a/paymentsheet/src/main/java/com/stripe/android/link/injection/LinkComponent.kt b/paymentsheet/src/main/java/com/stripe/android/link/injection/LinkComponent.kt index feb3bb8ff47..0022fab6aa7 100644 --- a/paymentsheet/src/main/java/com/stripe/android/link/injection/LinkComponent.kt +++ b/paymentsheet/src/main/java/com/stripe/android/link/injection/LinkComponent.kt @@ -3,6 +3,7 @@ package com.stripe.android.link.injection import com.stripe.android.link.LinkConfiguration import com.stripe.android.link.LinkPaymentLauncher import com.stripe.android.link.account.LinkAccountManager +import com.stripe.android.link.gate.LinkGate import dagger.BindsInstance import dagger.Subcomponent import javax.inject.Scope @@ -24,6 +25,7 @@ internal annotation class LinkScope internal abstract class LinkComponent { internal abstract val linkAccountManager: LinkAccountManager internal abstract val configuration: LinkConfiguration + internal abstract val linkGate: LinkGate internal abstract val inlineSignupViewModelFactory: LinkInlineSignupAssistedViewModelFactory @Subcomponent.Builder diff --git a/paymentsheet/src/main/java/com/stripe/android/link/injection/LinkModule.kt b/paymentsheet/src/main/java/com/stripe/android/link/injection/LinkModule.kt index e27471b787e..d2949327022 100644 --- a/paymentsheet/src/main/java/com/stripe/android/link/injection/LinkModule.kt +++ b/paymentsheet/src/main/java/com/stripe/android/link/injection/LinkModule.kt @@ -9,6 +9,8 @@ import com.stripe.android.link.account.DefaultLinkAccountManager import com.stripe.android.link.account.LinkAccountManager import com.stripe.android.link.analytics.DefaultLinkEventsReporter import com.stripe.android.link.analytics.LinkEventsReporter +import com.stripe.android.link.gate.DefaultLinkGate +import com.stripe.android.link.gate.LinkGate import com.stripe.android.link.repositories.LinkApiRepository import com.stripe.android.link.repositories.LinkRepository import com.stripe.android.repository.ConsumersApiService @@ -32,6 +34,10 @@ internal interface LinkModule { @LinkScope fun bindLinkAccountManager(linkAccountManager: DefaultLinkAccountManager): LinkAccountManager + @Binds + @LinkScope + fun bindsLinkGate(linkGate: DefaultLinkGate): LinkGate + companion object { @Provides @LinkScope diff --git a/paymentsheet/src/main/java/com/stripe/android/paymentelement/confirmation/ConfirmationOptionKtx.kt b/paymentsheet/src/main/java/com/stripe/android/paymentelement/confirmation/ConfirmationOptionKtx.kt index 4345e20da10..a9b46af7e68 100644 --- a/paymentsheet/src/main/java/com/stripe/android/paymentelement/confirmation/ConfirmationOptionKtx.kt +++ b/paymentsheet/src/main/java/com/stripe/android/paymentelement/confirmation/ConfirmationOptionKtx.kt @@ -116,6 +116,9 @@ private fun PaymentSelection.Link.toConfirmationOption( linkConfiguration: LinkConfiguration? ): LinkConfirmationOption? { return linkConfiguration?.let { - LinkConfirmationOption(configuration = linkConfiguration) + LinkConfirmationOption( + configuration = linkConfiguration, + useLinkExpress = useLinkExpress + ) } } diff --git a/paymentsheet/src/main/java/com/stripe/android/paymentelement/confirmation/link/LinkConfirmationDefinition.kt b/paymentsheet/src/main/java/com/stripe/android/paymentelement/confirmation/link/LinkConfirmationDefinition.kt index ba7f2c768ef..3c685712471 100644 --- a/paymentsheet/src/main/java/com/stripe/android/paymentelement/confirmation/link/LinkConfirmationDefinition.kt +++ b/paymentsheet/src/main/java/com/stripe/android/paymentelement/confirmation/link/LinkConfirmationDefinition.kt @@ -56,7 +56,8 @@ internal class LinkConfirmationDefinition @Inject constructor( ) { launcher.present( configuration = confirmationOption.configuration, - linkAccount = linkAccountHolder.linkAccount.value + linkAccount = linkAccountHolder.linkAccount.value, + useLinkExpress = confirmationOption.useLinkExpress ) } diff --git a/paymentsheet/src/main/java/com/stripe/android/paymentelement/confirmation/link/LinkConfirmationOption.kt b/paymentsheet/src/main/java/com/stripe/android/paymentelement/confirmation/link/LinkConfirmationOption.kt index 6f0db93e443..1d6aea56136 100644 --- a/paymentsheet/src/main/java/com/stripe/android/paymentelement/confirmation/link/LinkConfirmationOption.kt +++ b/paymentsheet/src/main/java/com/stripe/android/paymentelement/confirmation/link/LinkConfirmationOption.kt @@ -7,4 +7,5 @@ import kotlinx.parcelize.Parcelize @Parcelize internal data class LinkConfirmationOption( val configuration: LinkConfiguration, + val useLinkExpress: Boolean ) : ConfirmationHandler.Option diff --git a/paymentsheet/src/main/java/com/stripe/android/paymentelement/embedded/content/PaymentOptionDisplayDataFactory.kt b/paymentsheet/src/main/java/com/stripe/android/paymentelement/embedded/content/PaymentOptionDisplayDataFactory.kt index 54c407c730e..9a9aa246aa9 100644 --- a/paymentsheet/src/main/java/com/stripe/android/paymentelement/embedded/content/PaymentOptionDisplayDataFactory.kt +++ b/paymentsheet/src/main/java/com/stripe/android/paymentelement/embedded/content/PaymentOptionDisplayDataFactory.kt @@ -43,7 +43,7 @@ internal class PaymentOptionDisplayDataFactory @Inject constructor( } is PaymentSelection.ExternalPaymentMethod -> null PaymentSelection.GooglePay -> null - PaymentSelection.Link -> null + is PaymentSelection.Link -> null } return EmbeddedPaymentElement.PaymentOptionDisplayData( diff --git a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/LinkHandler.kt b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/LinkHandler.kt index 8dc7f703d53..fe121af0215 100644 --- a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/LinkHandler.kt +++ b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/LinkHandler.kt @@ -30,6 +30,21 @@ internal class LinkHandler @Inject constructor( _linkConfiguration.value = state.configuration } + fun setupLinkWithEagerLaunch(state: LinkState?): Boolean { + setupLink(state) + + val configuration = state?.configuration ?: return false + val linkGate = linkConfigurationCoordinator.linkGate(configuration) + + if (linkGate.suppress2faModal) return false + + return when (state.loginState) { + LinkState.LoginState.LoggedIn, + LinkState.LoginState.NeedsVerification -> true + LinkState.LoginState.LoggedOut -> false + } + } + @OptIn(DelicateCoroutinesApi::class) fun logOut() { val configuration = linkConfiguration.value ?: return diff --git a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/PaymentOptionsStateFactory.kt b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/PaymentOptionsStateFactory.kt index 1b3c7b1d0c8..c0ff4b19201 100644 --- a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/PaymentOptionsStateFactory.kt +++ b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/PaymentOptionsStateFactory.kt @@ -98,7 +98,7 @@ internal fun PaymentOptionsItem.toPaymentSelection(): PaymentSelection? { return when (this) { is PaymentOptionsItem.AddCard -> null is PaymentOptionsItem.GooglePay -> PaymentSelection.GooglePay - is PaymentOptionsItem.Link -> PaymentSelection.Link + is PaymentOptionsItem.Link -> PaymentSelection.Link() is PaymentOptionsItem.SavedPaymentMethod -> PaymentSelection.Saved(paymentMethod) } } diff --git a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/PaymentOptionsViewModel.kt b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/PaymentOptionsViewModel.kt index 7832692edc7..78e1e8768be 100644 --- a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/PaymentOptionsViewModel.kt +++ b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/PaymentOptionsViewModel.kt @@ -104,7 +104,7 @@ internal class PaymentOptionsViewModel @Inject constructor( onUserSelection() }, onLinkPressed = { - updateSelection(PaymentSelection.Link) + updateSelection(PaymentSelection.Link()) onUserSelection() }, isSetupIntent = paymentMethodMetadata.stripeIntent is SetupIntent diff --git a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/PaymentSheetViewModel.kt b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/PaymentSheetViewModel.kt index 4ebbea3efe1..829047b1aea 100644 --- a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/PaymentSheetViewModel.kt +++ b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/PaymentSheetViewModel.kt @@ -273,7 +273,7 @@ internal class PaymentSheetViewModel @Inject internal constructor( setPaymentMethodMetadata(state.paymentMethodMetadata) - linkHandler.setupLink(state.paymentMethodMetadata.linkState) + val shouldLaunchEagerly = linkHandler.setupLinkWithEagerLaunch(state.paymentMethodMetadata.linkState) val pendingFailedPaymentResult = confirmationHandler.awaitResult() as? ConfirmationHandler.Result.Failed @@ -287,6 +287,10 @@ internal class PaymentSheetViewModel @Inject internal constructor( ) ) + if (shouldLaunchEagerly) { + checkoutWithLinkExpress() + } + viewModelScope.launch { confirmationHandler.state.collectLatest { state -> when (state) { @@ -340,7 +344,11 @@ internal class PaymentSheetViewModel @Inject internal constructor( } fun checkoutWithLink() { - checkout(PaymentSelection.Link, CheckoutIdentifier.SheetTopWallet) + checkout(PaymentSelection.Link(useLinkExpress = false), CheckoutIdentifier.SheetTopWallet) + } + + private fun checkoutWithLinkExpress() { + checkout(PaymentSelection.Link(useLinkExpress = true), CheckoutIdentifier.SheetTopWallet) } private fun checkout( diff --git a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/flowcontroller/DefaultFlowController.kt b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/flowcontroller/DefaultFlowController.kt index d9c178ffac4..02e48e86cb3 100644 --- a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/flowcontroller/DefaultFlowController.kt +++ b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/flowcontroller/DefaultFlowController.kt @@ -426,7 +426,7 @@ internal class DefaultFlowController @Inject internal constructor( is PaymentSelection.Saved -> { when (currentSelection.walletType) { PaymentSelection.Saved.WalletType.GooglePay -> PaymentSelection.GooglePay - PaymentSelection.Saved.WalletType.Link -> PaymentSelection.Link + PaymentSelection.Saved.WalletType.Link -> PaymentSelection.Link() else -> currentSelection } } diff --git a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/model/PaymentSelection.kt b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/model/PaymentSelection.kt index 8b521169567..34f295b4c3c 100644 --- a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/model/PaymentSelection.kt +++ b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/model/PaymentSelection.kt @@ -64,7 +64,9 @@ internal sealed class PaymentSelection : Parcelable { } @Parcelize - data object Link : PaymentSelection() { + data class Link( + val useLinkExpress: Boolean = false + ) : PaymentSelection() { override val requiresConfirmation: Boolean get() = false @@ -107,7 +109,7 @@ internal sealed class PaymentSelection : Parcelable { ) : PaymentSelection() { enum class WalletType(val paymentSelection: PaymentSelection) { - GooglePay(PaymentSelection.GooglePay), Link(PaymentSelection.Link) + GooglePay(PaymentSelection.GooglePay), Link(Link()) } val showMandateAbovePrimaryButton: Boolean @@ -309,7 +311,7 @@ internal val PaymentSelection.drawableResourceId: Int get() = when (this) { is PaymentSelection.ExternalPaymentMethod -> iconResource PaymentSelection.GooglePay -> R.drawable.stripe_google_pay_mark - PaymentSelection.Link -> getLinkIcon() + is PaymentSelection.Link -> getLinkIcon() is PaymentSelection.New.Card -> brand.getCardBrandIcon() is PaymentSelection.New.GenericPaymentMethod -> iconResource is PaymentSelection.New.LinkInline -> getLinkIcon() @@ -334,7 +336,7 @@ internal val PaymentSelection.lightThemeIconUrl: String? get() = when (this) { is PaymentSelection.ExternalPaymentMethod -> lightThemeIconUrl PaymentSelection.GooglePay -> null - PaymentSelection.Link -> null + is PaymentSelection.Link -> null is PaymentSelection.New.Card -> null is PaymentSelection.New.GenericPaymentMethod -> lightThemeIconUrl is PaymentSelection.New.LinkInline -> null @@ -346,7 +348,7 @@ internal val PaymentSelection.darkThemeIconUrl: String? get() = when (this) { is PaymentSelection.ExternalPaymentMethod -> darkThemeIconUrl PaymentSelection.GooglePay -> null - PaymentSelection.Link -> null + is PaymentSelection.Link -> null is PaymentSelection.New.Card -> null is PaymentSelection.New.GenericPaymentMethod -> darkThemeIconUrl is PaymentSelection.New.LinkInline -> null @@ -358,7 +360,7 @@ internal val PaymentSelection.label: ResolvableString get() = when (this) { is PaymentSelection.ExternalPaymentMethod -> label PaymentSelection.GooglePay -> StripeR.string.stripe_google_pay.resolvableString - PaymentSelection.Link -> StripeR.string.stripe_link.resolvableString + is PaymentSelection.Link -> StripeR.string.stripe_link.resolvableString is PaymentSelection.New.Card -> createCardLabel(last4).orEmpty() is PaymentSelection.New.GenericPaymentMethod -> label is PaymentSelection.New.LinkInline -> createCardLabel(last4).orEmpty() @@ -380,7 +382,7 @@ internal val PaymentSelection.paymentMethodType: String get() = when (this) { is PaymentSelection.ExternalPaymentMethod -> type PaymentSelection.GooglePay -> "google_pay" - PaymentSelection.Link -> "link" + is PaymentSelection.Link -> "link" is PaymentSelection.New -> paymentMethodCreateParams.typeCode is PaymentSelection.Saved -> paymentMethod.type?.name ?: "card" } @@ -389,7 +391,7 @@ internal val PaymentSelection.billingDetails: PaymentMethod.BillingDetails? get() = when (this) { is PaymentSelection.ExternalPaymentMethod -> billingDetails PaymentSelection.GooglePay -> null - PaymentSelection.Link -> null + is PaymentSelection.Link -> null is PaymentSelection.New -> paymentMethodCreateParams.billingDetails is PaymentSelection.Saved -> paymentMethod.billingDetails } diff --git a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/state/PaymentElementLoader.kt b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/state/PaymentElementLoader.kt index 9e2bfd6e9e6..73adca1a37f 100644 --- a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/state/PaymentElementLoader.kt +++ b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/state/PaymentElementLoader.kt @@ -574,7 +574,7 @@ internal class DefaultPaymentElementLoader @Inject constructor( CustomerState.DefaultPaymentMethodState.Disabled, null -> { when (val selection = savedSelection.await()) { is SavedSelection.GooglePay -> PaymentSelection.GooglePay - is SavedSelection.Link -> PaymentSelection.Link + is SavedSelection.Link -> PaymentSelection.Link() is SavedSelection.PaymentMethod -> { customer?.paymentMethods?.find { it.id == selection.id }?.toPaymentSelection() } diff --git a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/ui/SavedPaymentMethodTabLayoutUI.kt b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/ui/SavedPaymentMethodTabLayoutUI.kt index 7daec61a87f..666c21e26ea 100644 --- a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/ui/SavedPaymentMethodTabLayoutUI.kt +++ b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/ui/SavedPaymentMethodTabLayoutUI.kt @@ -389,7 +389,7 @@ private fun LinkTab( iconTint = null, labelText = stringResource(StripeR.string.stripe_link), description = stringResource(StripeR.string.stripe_link), - onItemSelectedListener = { onItemSelected(PaymentSelection.Link) }, + onItemSelectedListener = { onItemSelected(PaymentSelection.Link()) }, modifier = modifier, ) } diff --git a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/ui/SelectSavedPaymentMethodsInteractor.kt b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/ui/SelectSavedPaymentMethodsInteractor.kt index 9dfd22e359f..477061c5707 100644 --- a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/ui/SelectSavedPaymentMethodsInteractor.kt +++ b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/ui/SelectSavedPaymentMethodsInteractor.kt @@ -140,7 +140,7 @@ internal class DefaultSelectSavedPaymentMethodsInteractor( coroutineScope.launch { currentSelection.filter { selection -> selection is PaymentSelection.Saved || - selection == PaymentSelection.Link || + selection is PaymentSelection.Link || selection == PaymentSelection.GooglePay }.collect { _paymentOptionsRelevantSelection.value = it @@ -170,7 +170,7 @@ internal class DefaultSelectSavedPaymentMethodsInteractor( paymentOptionsItems: List, ): PaymentOptionsItem? { val paymentSelection = when (selection) { - is PaymentSelection.Saved, PaymentSelection.Link, PaymentSelection.GooglePay -> selection + is PaymentSelection.Saved, is PaymentSelection.Link, PaymentSelection.GooglePay -> selection is PaymentSelection.New, is PaymentSelection.ExternalPaymentMethod, null -> savedSelection?.let { PaymentSelection.Saved(it) diff --git a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/verticalmode/ManageScreenInteractor.kt b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/verticalmode/ManageScreenInteractor.kt index 4e8086916a6..cda19b203a5 100644 --- a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/verticalmode/ManageScreenInteractor.kt +++ b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/verticalmode/ManageScreenInteractor.kt @@ -204,7 +204,7 @@ internal class DefaultManageScreenInteractor( null, is PaymentSelection.ExternalPaymentMethod, PaymentSelection.GooglePay, - PaymentSelection.Link, + is PaymentSelection.Link, is PaymentSelection.New -> return null is PaymentSelection.Saved -> selection.paymentMethod.id } diff --git a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/verticalmode/PaymentMethodVerticalLayoutInteractor.kt b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/verticalmode/PaymentMethodVerticalLayoutInteractor.kt index 6f5d06c20e7..86e9da41423 100644 --- a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/verticalmode/PaymentMethodVerticalLayoutInteractor.kt +++ b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/verticalmode/PaymentMethodVerticalLayoutInteractor.kt @@ -287,7 +287,7 @@ internal class DefaultPaymentMethodVerticalLayoutInteractor( iconRequiresTinting = false, subtitle = PaymentsCoreR.string.stripe_link_simple_secure_payments.resolvableString, onClick = { - updateSelection(PaymentSelection.Link) + updateSelection(PaymentSelection.Link()) }, ) } diff --git a/paymentsheet/src/test/java/com/stripe/android/customersheet/CustomerSheetViewModelTest.kt b/paymentsheet/src/test/java/com/stripe/android/customersheet/CustomerSheetViewModelTest.kt index 5d16d0dc361..9640f37c800 100644 --- a/paymentsheet/src/test/java/com/stripe/android/customersheet/CustomerSheetViewModelTest.kt +++ b/paymentsheet/src/test/java/com/stripe/android/customersheet/CustomerSheetViewModelTest.kt @@ -425,7 +425,7 @@ class CustomerSheetViewModelTest { val error = assertFailsWith { viewModel.handleViewAction( CustomerSheetViewAction.OnItemSelected( - selection = PaymentSelection.Link + selection = PaymentSelection.Link() ) ) } diff --git a/paymentsheet/src/test/java/com/stripe/android/link/LinkPaymentLauncherTest.kt b/paymentsheet/src/test/java/com/stripe/android/link/LinkPaymentLauncherTest.kt index 074c05923e7..6396b8b9f10 100644 --- a/paymentsheet/src/test/java/com/stripe/android/link/LinkPaymentLauncherTest.kt +++ b/paymentsheet/src/test/java/com/stripe/android/link/LinkPaymentLauncherTest.kt @@ -89,7 +89,8 @@ internal class LinkPaymentLauncherTest { linkPaymentLauncher.present( configuration = TestFactory.LINK_CONFIGURATION, - linkAccount = TestFactory.LINK_ACCOUNT + linkAccount = TestFactory.LINK_ACCOUNT, + useLinkExpress = true ) val launchCall = awaitLaunchCall() @@ -98,8 +99,8 @@ internal class LinkPaymentLauncherTest { .isEqualTo( LinkActivityContract.Args( configuration = TestFactory.LINK_CONFIGURATION, - startWithVerificationDialog = false, - linkAccount = TestFactory.LINK_ACCOUNT + startWithVerificationDialog = true, + linkAccount = TestFactory.LINK_ACCOUNT, ) ) @@ -107,6 +108,28 @@ internal class LinkPaymentLauncherTest { } } + @Test + fun `present should launch with correct args when useLinkExpress is false`() = runTest { + DummyActivityResultCaller.test { + val linkPaymentLauncher = createLinkPaymentLauncher() + + linkPaymentLauncher.register(activityResultCaller) {} + + val registerCall = awaitRegisterCall() + assertThat(registerCall).isNotNull() + + linkPaymentLauncher.present( + configuration = TestFactory.LINK_CONFIGURATION, + linkAccount = TestFactory.LINK_ACCOUNT, + useLinkExpress = false + ) + + val launchCall = awaitLaunchCall() as? LinkActivityContract.Args + assertThat(launchCall?.startWithVerificationDialog).isFalse() + awaitNextRegisteredLauncher() + } + } + @Test fun `ActivityResultRegistry callback should handle Completed result correctly`() { testActivityResultCallbackWithActivityResultRegistry( @@ -203,7 +226,8 @@ internal class LinkPaymentLauncherTest { linkPaymentLauncher.present( configuration = TestFactory.LINK_CONFIGURATION, - linkAccount = TestFactory.LINK_ACCOUNT + linkAccount = TestFactory.LINK_ACCOUNT, + useLinkExpress = true ) verifyActivityResultCallback( @@ -233,7 +257,8 @@ internal class LinkPaymentLauncherTest { linkPaymentLauncher.present( configuration = TestFactory.LINK_CONFIGURATION, - linkAccount = TestFactory.LINK_ACCOUNT + linkAccount = TestFactory.LINK_ACCOUNT, + useLinkExpress = true ) val registerCall = awaitRegisterCall() diff --git a/paymentsheet/src/test/java/com/stripe/android/link/gate/DefaultLinkGateTest.kt b/paymentsheet/src/test/java/com/stripe/android/link/gate/DefaultLinkGateTest.kt index 9089ec22035..f5596e2ab72 100644 --- a/paymentsheet/src/test/java/com/stripe/android/link/gate/DefaultLinkGateTest.kt +++ b/paymentsheet/src/test/java/com/stripe/android/link/gate/DefaultLinkGateTest.kt @@ -109,9 +109,114 @@ internal class DefaultLinkGateTest { assertThat(gate.useAttestationEndpoints).isTrue() } + @Test + fun `suppress2faModal - test mode - is true when useNativeLink is false and suppress2faModal is false`() { + attestationFeatureFlagTestRule.setEnabled(false) + nativeLinkFeatureFlagTestRule.setEnabled(false) + val gate = gate( + isLiveMode = false, + useAttestationEndpoints = false, + suppress2faModal = false + ) + + assertThat(gate.suppress2faModal).isTrue() + } + + @Test + fun `suppress2faModal - test mode - is true when useNativeLink is true and suppress2faModal is true`() { + attestationFeatureFlagTestRule.setEnabled(true) + nativeLinkFeatureFlagTestRule.setEnabled(true) + val gate = gate( + isLiveMode = false, + useAttestationEndpoints = true, + suppress2faModal = true + ) + + assertThat(gate.suppress2faModal).isTrue() + } + + @Test + fun `suppress2faModal - test mode - is true when useNativeLink is false and suppress2faModal is true`() { + attestationFeatureFlagTestRule.setEnabled(false) + nativeLinkFeatureFlagTestRule.setEnabled(false) + val gate = gate( + isLiveMode = false, + useAttestationEndpoints = false, + suppress2faModal = true + ) + + assertThat(gate.suppress2faModal).isTrue() + } + + @Test + fun `suppress2faModal - test mode - is true when useNativeLink is true and suppress2faModal is false`() { + attestationFeatureFlagTestRule.setEnabled(true) + nativeLinkFeatureFlagTestRule.setEnabled(true) + val gate = gate( + isLiveMode = false, + useAttestationEndpoints = true, + suppress2faModal = false + ) + + assertThat(gate.suppress2faModal).isFalse() + } + + @Test + fun `suppress2faModal - live mode - is true when useNativeLink is false and suppress2faModal is false`() { + attestationFeatureFlagTestRule.setEnabled(false) + nativeLinkFeatureFlagTestRule.setEnabled(false) + val gate = gate( + isLiveMode = true, + useAttestationEndpoints = false, + suppress2faModal = false + ) + + assertThat(gate.suppress2faModal).isTrue() + } + + @Test + fun `suppress2faModal - live mode - is true when useNativeLink is true and suppress2faModal is true`() { + attestationFeatureFlagTestRule.setEnabled(true) + nativeLinkFeatureFlagTestRule.setEnabled(true) + val gate = gate( + isLiveMode = true, + useAttestationEndpoints = true, + suppress2faModal = true + ) + + assertThat(gate.suppress2faModal).isTrue() + } + + @Test + fun `suppress2faModal - live mode - is true when useNativeLink is false and suppress2faModal is true`() { + attestationFeatureFlagTestRule.setEnabled(false) + nativeLinkFeatureFlagTestRule.setEnabled(false) + val gate = gate( + isLiveMode = true, + useAttestationEndpoints = false, + suppress2faModal = true + ) + + assertThat(gate.suppress2faModal).isTrue() + } + + @Test + fun `suppress2faModal - live mode - is true when useNativeLink is true and suppress2faModal is false`() { + attestationFeatureFlagTestRule.setEnabled(true) + nativeLinkFeatureFlagTestRule.setEnabled(true) + val gate = gate( + isLiveMode = true, + useAttestationEndpoints = true, + suppress2faModal = false + ) + + assertThat(gate.suppress2faModal).isFalse() + } + private fun gate( isLiveMode: Boolean = true, - useAttestationEndpoints: Boolean = true + useAttestationEndpoints: Boolean = true, + suppress2faModal: Boolean = false ): DefaultLinkGate { val newIntent = when (val intent = TestFactory.LINK_CONFIGURATION.stripeIntent) { is PaymentIntent -> { @@ -125,6 +230,7 @@ internal class DefaultLinkGateTest { return DefaultLinkGate( configuration = TestFactory.LINK_CONFIGURATION.copy( useAttestationEndpointsForLink = useAttestationEndpoints, + suppress2faModal = suppress2faModal, stripeIntent = newIntent ) ) diff --git a/paymentsheet/src/test/java/com/stripe/android/link/gate/FakeLinkGate.kt b/paymentsheet/src/test/java/com/stripe/android/link/gate/FakeLinkGate.kt index 64d2de56687..78b7aadba73 100644 --- a/paymentsheet/src/test/java/com/stripe/android/link/gate/FakeLinkGate.kt +++ b/paymentsheet/src/test/java/com/stripe/android/link/gate/FakeLinkGate.kt @@ -9,6 +9,10 @@ internal class FakeLinkGate : LinkGate { override val useAttestationEndpoints: Boolean get() = _useAttestationEndpoints + private var _suppress2faModal: Boolean = false + override val suppress2faModal: Boolean + get() = _suppress2faModal + fun setUseNativeLink(value: Boolean) { _useNativeLink = value } @@ -16,4 +20,8 @@ internal class FakeLinkGate : LinkGate { fun setUseAttestationEndpoints(value: Boolean) { _useAttestationEndpoints = value } + + fun setSuppress2faModal(value: Boolean) { + _suppress2faModal = value + } } diff --git a/paymentsheet/src/test/java/com/stripe/android/lpmfoundations/paymentmethod/link/LinkFormElementTest.kt b/paymentsheet/src/test/java/com/stripe/android/lpmfoundations/paymentmethod/link/LinkFormElementTest.kt index 9f92948759b..39f1d6455ba 100644 --- a/paymentsheet/src/test/java/com/stripe/android/lpmfoundations/paymentmethod/link/LinkFormElementTest.kt +++ b/paymentsheet/src/test/java/com/stripe/android/lpmfoundations/paymentmethod/link/LinkFormElementTest.kt @@ -13,6 +13,8 @@ import com.stripe.android.link.LinkPaymentDetails import com.stripe.android.link.account.FakeLinkAccountManager import com.stripe.android.link.account.LinkAccountManager import com.stripe.android.link.analytics.FakeLinkEventsReporter +import com.stripe.android.link.gate.FakeLinkGate +import com.stripe.android.link.gate.LinkGate import com.stripe.android.link.injection.LinkComponent import com.stripe.android.link.injection.LinkInlineSignupAssistedViewModelFactory import com.stripe.android.link.model.AccountStatus @@ -155,6 +157,10 @@ class LinkFormElementTest { error("Not implemented!") } + override fun linkGate(configuration: LinkConfiguration): LinkGate { + TODO("Not yet implemented") + } + override suspend fun signInWithUserInput( configuration: LinkConfiguration, userInput: UserInput @@ -178,6 +184,7 @@ class LinkFormElementTest { override val configuration: LinkConfiguration, ) : LinkComponent() { override val linkAccountManager: LinkAccountManager = FakeLinkAccountManager() + override val linkGate: LinkGate = FakeLinkGate() override val inlineSignupViewModelFactory: LinkInlineSignupAssistedViewModelFactory = FakeLinkInlineSignupAssistedViewModelFactory(linkAccountManager, configuration) diff --git a/paymentsheet/src/test/java/com/stripe/android/paymentelement/confirmation/ConfirmationHandlerOptionKtxTest.kt b/paymentsheet/src/test/java/com/stripe/android/paymentelement/confirmation/ConfirmationHandlerOptionKtxTest.kt index ddb757156be..91fcbc31012 100644 --- a/paymentsheet/src/test/java/com/stripe/android/paymentelement/confirmation/ConfirmationHandlerOptionKtxTest.kt +++ b/paymentsheet/src/test/java/com/stripe/android/paymentelement/confirmation/ConfirmationHandlerOptionKtxTest.kt @@ -245,7 +245,7 @@ class ConfirmationHandlerOptionKtxTest { @Test fun `On Link selection but with no configuration, should return null`() { assertThat( - PaymentSelection.Link.toConfirmationOption( + PaymentSelection.Link().toConfirmationOption( configuration = PaymentSheetFixtures.CONFIG_CUSTOMER.asCommonConfiguration(), linkConfiguration = null, ) @@ -255,13 +255,31 @@ class ConfirmationHandlerOptionKtxTest { @Test fun `On Link selection with configuration, should return Link confirmation option`() { assertThat( - PaymentSelection.Link.toConfirmationOption( + PaymentSelection.Link().toConfirmationOption( configuration = PaymentSheetFixtures.CONFIG_CUSTOMER.asCommonConfiguration(), linkConfiguration = LINK_CONFIGURATION, ) ).isEqualTo( LinkConfirmationOption( configuration = LINK_CONFIGURATION, + useLinkExpress = false + ) + ) + } + + @Test + fun `On Link selection with express configuration, should return Link confirmation option with express enabled`() { + assertThat( + PaymentSelection.Link( + useLinkExpress = true + ).toConfirmationOption( + configuration = PaymentSheetFixtures.CONFIG_CUSTOMER.asCommonConfiguration(), + linkConfiguration = LINK_CONFIGURATION, + ) + ).isEqualTo( + LinkConfirmationOption( + configuration = LINK_CONFIGURATION, + useLinkExpress = true ) ) } diff --git a/paymentsheet/src/test/java/com/stripe/android/paymentelement/confirmation/link/LinkConfirmationActivityTest.kt b/paymentsheet/src/test/java/com/stripe/android/paymentelement/confirmation/link/LinkConfirmationActivityTest.kt index 3bcc6ed80cb..509cdf36928 100644 --- a/paymentsheet/src/test/java/com/stripe/android/paymentelement/confirmation/link/LinkConfirmationActivityTest.kt +++ b/paymentsheet/src/test/java/com/stripe/android/paymentelement/confirmation/link/LinkConfirmationActivityTest.kt @@ -213,6 +213,7 @@ internal class LinkConfirmationActivityTest(private val nativeLinkEnabled: Boole val LINK_CONFIRMATION_OPTION = LinkConfirmationOption( configuration = TestFactory.LINK_CONFIGURATION, + useLinkExpress = true ) val CONFIRMATION_PARAMETERS = ConfirmationDefinition.Parameters( diff --git a/paymentsheet/src/test/java/com/stripe/android/paymentelement/confirmation/link/LinkConfirmationDefinitionTest.kt b/paymentsheet/src/test/java/com/stripe/android/paymentelement/confirmation/link/LinkConfirmationDefinitionTest.kt index 0d16c645e10..5c3dd89f7ec 100644 --- a/paymentsheet/src/test/java/com/stripe/android/paymentelement/confirmation/link/LinkConfirmationDefinitionTest.kt +++ b/paymentsheet/src/test/java/com/stripe/android/paymentelement/confirmation/link/LinkConfirmationDefinitionTest.kt @@ -137,8 +137,27 @@ internal class LinkConfirmationDefinitionTest { val presentCall = launcherScenario.presentCalls.awaitItem() + assertThat(presentCall.useLinkExpress).isTrue() + } + + @Test + fun `'launch' should launch properly with provided parameters when useLinkExpress is false`() = test { + val definition = createLinkConfirmationDefinition() + + definition.launch( + confirmationOption = LINK_CONFIRMATION_OPTION.copy( + useLinkExpress = false + ), + confirmationParameters = CONFIRMATION_PARAMETERS, + launcher = launcherScenario.launcher, + arguments = Unit, + ) + + val presentCall = launcherScenario.presentCalls.awaitItem() + assertThat(presentCall.configuration).isEqualTo(LINK_CONFIRMATION_OPTION.configuration) - assertThat(presentCall.linkAccount).isEqualTo(TestFactory.LINK_ACCOUNT) + assertThat(presentCall.linkAccount).isNull() + assertThat(presentCall.useLinkExpress).isFalse() } @Test @@ -329,6 +348,7 @@ internal class LinkConfirmationDefinitionTest { private val LINK_CONFIRMATION_OPTION = LinkConfirmationOption( configuration = TestFactory.LINK_CONFIGURATION, + useLinkExpress = true ) } } diff --git a/paymentsheet/src/test/java/com/stripe/android/paymentelement/confirmation/link/LinkConfirmationFlowTest.kt b/paymentsheet/src/test/java/com/stripe/android/paymentelement/confirmation/link/LinkConfirmationFlowTest.kt index 97ba71c5fdd..c4b36bcbfe6 100644 --- a/paymentsheet/src/test/java/com/stripe/android/paymentelement/confirmation/link/LinkConfirmationFlowTest.kt +++ b/paymentsheet/src/test/java/com/stripe/android/paymentelement/confirmation/link/LinkConfirmationFlowTest.kt @@ -150,6 +150,7 @@ class LinkConfirmationFlowTest { private val LINK_CONFIRMATION_OPTION = LinkConfirmationOption( configuration = TestFactory.LINK_CONFIGURATION, + useLinkExpress = true ) } } diff --git a/paymentsheet/src/test/java/com/stripe/android/paymentelement/confirmation/linkinline/LinkInlineSignupConfirmationDefinitionTest.kt b/paymentsheet/src/test/java/com/stripe/android/paymentelement/confirmation/linkinline/LinkInlineSignupConfirmationDefinitionTest.kt index aed82e2b38d..d88693a8017 100644 --- a/paymentsheet/src/test/java/com/stripe/android/paymentelement/confirmation/linkinline/LinkInlineSignupConfirmationDefinitionTest.kt +++ b/paymentsheet/src/test/java/com/stripe/android/paymentelement/confirmation/linkinline/LinkInlineSignupConfirmationDefinitionTest.kt @@ -11,6 +11,7 @@ import com.stripe.android.link.LinkPaymentDetails import com.stripe.android.link.account.LinkStore import com.stripe.android.link.analytics.FakeLinkAnalyticsHelper import com.stripe.android.link.analytics.LinkAnalyticsHelper +import com.stripe.android.link.gate.LinkGate import com.stripe.android.link.injection.LinkComponent import com.stripe.android.link.model.AccountStatus import com.stripe.android.link.ui.inline.SignUpConsentAction @@ -761,6 +762,10 @@ internal class LinkInlineSignupConfirmationDefinitionTest { return accountStatusFlow } + override fun linkGate(configuration: LinkConfiguration): LinkGate { + throw NotImplementedError() + } + override suspend fun signInWithUserInput( configuration: LinkConfiguration, userInput: UserInput, diff --git a/paymentsheet/src/test/java/com/stripe/android/paymentelement/embedded/content/DefaultEmbeddedConfirmationHelperTest.kt b/paymentsheet/src/test/java/com/stripe/android/paymentelement/embedded/content/DefaultEmbeddedConfirmationHelperTest.kt index eb374770d2c..f3886d34200 100644 --- a/paymentsheet/src/test/java/com/stripe/android/paymentelement/embedded/content/DefaultEmbeddedConfirmationHelperTest.kt +++ b/paymentsheet/src/test/java/com/stripe/android/paymentelement/embedded/content/DefaultEmbeddedConfirmationHelperTest.kt @@ -103,7 +103,7 @@ internal class DefaultEmbeddedConfirmationHelperTest { @Test fun confirmCallsConfirmationHandlerStartWithLink() = testScenario( loadedState = defaultLoadedState().copy( - selection = PaymentSelection.Link, + selection = PaymentSelection.Link(), paymentMethodMetadata = PaymentMethodMetadataFactory.create( linkState = LinkState( configuration = LinkTestUtils.createLinkConfiguration(), diff --git a/paymentsheet/src/test/java/com/stripe/android/paymentsheet/DefaultPrefsRepositoryTest.kt b/paymentsheet/src/test/java/com/stripe/android/paymentsheet/DefaultPrefsRepositoryTest.kt index be4e1c51205..60f14cf352d 100644 --- a/paymentsheet/src/test/java/com/stripe/android/paymentsheet/DefaultPrefsRepositoryTest.kt +++ b/paymentsheet/src/test/java/com/stripe/android/paymentsheet/DefaultPrefsRepositoryTest.kt @@ -57,7 +57,7 @@ internal class DefaultPrefsRepositoryTest { isLinkAvailable = false prefsRepository.savePaymentSelection( - PaymentSelection.Link + PaymentSelection.Link() ) assertThat( prefsRepository.getSavedSelection(isGooglePayReady, isLinkAvailable) diff --git a/paymentsheet/src/test/java/com/stripe/android/paymentsheet/LinkHandlerTest.kt b/paymentsheet/src/test/java/com/stripe/android/paymentsheet/LinkHandlerTest.kt index 5bb531d9724..b520b434ace 100644 --- a/paymentsheet/src/test/java/com/stripe/android/paymentsheet/LinkHandlerTest.kt +++ b/paymentsheet/src/test/java/com/stripe/android/paymentsheet/LinkHandlerTest.kt @@ -9,6 +9,8 @@ import com.stripe.android.link.LinkPaymentDetails import com.stripe.android.link.TestFactory import com.stripe.android.link.account.LinkStore import com.stripe.android.link.analytics.LinkAnalyticsHelper +import com.stripe.android.link.gate.FakeLinkGate +import com.stripe.android.link.gate.LinkGate import com.stripe.android.link.model.AccountStatus import com.stripe.android.link.ui.inline.LinkSignupMode import com.stripe.android.paymentsheet.model.PaymentSelection @@ -54,15 +56,79 @@ class LinkHandlerTest { assertThat(handler.isLinkEnabled.first()).isTrue() assertThat(savedStateHandle.get(SAVE_SELECTION)).isNull() } + + @Test + fun `setupLinkWithEagerLaunch returns true in LoggedIn states`() = runLinkTest { + val shouldLaunchEagerly = handler.setupLinkWithEagerLaunch( + state = createLinkState( + loginState = LinkState.LoginState.LoggedIn, + signupMode = LinkSignupMode.AlongsideSaveForFutureUse + ) + ) + + assertThat(shouldLaunchEagerly).isTrue() + } + + @Test + fun `setupLinkWithEagerLaunch returns true in NeedsVerification states`() = runLinkTest { + val shouldLaunchEagerly = handler.setupLinkWithEagerLaunch( + state = createLinkState( + loginState = LinkState.LoginState.NeedsVerification, + signupMode = LinkSignupMode.AlongsideSaveForFutureUse + ) + ) + + assertThat(shouldLaunchEagerly).isTrue() + } + + @Test + fun `setupLinkWithEagerLaunch returns false in LoggedOut state`() = runLinkTest { + val shouldLaunchEagerly = handler.setupLinkWithEagerLaunch( + state = createLinkState( + loginState = LinkState.LoginState.LoggedOut, + signupMode = LinkSignupMode.AlongsideSaveForFutureUse + ) + ) + + assertThat(shouldLaunchEagerly).isFalse() + } + + @Test + fun `setupLinkWithEagerLaunch returns false when suppress2faModal is true`() { + val linkGate = FakeLinkGate() + linkGate.setSuppress2faModal(false) + runLinkTest( + linkGate = linkGate + ) { + val shouldLaunchEagerly = handler.setupLinkWithEagerLaunch( + state = createLinkState( + loginState = LinkState.LoginState.LoggedOut, + signupMode = LinkSignupMode.AlongsideSaveForFutureUse + ) + ) + + assertThat(shouldLaunchEagerly).isFalse() + } + } + + @Test + fun `setupLinkWithEagerLaunch returns false when state is null`() = runLinkTest { + val shouldLaunchEagerly = handler.setupLinkWithEagerLaunch(state = null) + + assertThat(shouldLaunchEagerly).isFalse() + } } private fun runLinkTest( accountStatusFlow: MutableSharedFlow = MutableSharedFlow(replay = 1), linkConfiguration: LinkConfiguration = defaultLinkConfiguration(), attachNewCardToAccountResult: Result? = null, + linkGate: LinkGate = FakeLinkGate(), testBlock: suspend LinkTestData.() -> Unit ): Unit = runTest { val linkConfigurationCoordinator = mock() + whenever(linkConfigurationCoordinator.linkGate(linkConfiguration)) + .thenReturn(linkGate) val savedStateHandle = SavedStateHandle() val linkAnalyticsHelper = mock() val linkStore = mock() diff --git a/paymentsheet/src/test/java/com/stripe/android/paymentsheet/PaymentOptionsStateFactoryTest.kt b/paymentsheet/src/test/java/com/stripe/android/paymentsheet/PaymentOptionsStateFactoryTest.kt index 137b1abb6a6..fbb9c2a633f 100644 --- a/paymentsheet/src/test/java/com/stripe/android/paymentsheet/PaymentOptionsStateFactoryTest.kt +++ b/paymentsheet/src/test/java/com/stripe/android/paymentsheet/PaymentOptionsStateFactoryTest.kt @@ -36,7 +36,7 @@ class PaymentOptionsStateFactoryTest { paymentMethods = paymentMethods, showGooglePay = false, showLink = false, - currentSelection = PaymentSelection.Link, + currentSelection = PaymentSelection.Link(), nameProvider = { it!!.resolvableString }, isCbcEligible = false, defaultPaymentMethodId = null, @@ -65,7 +65,7 @@ class PaymentOptionsStateFactoryTest { paymentMethods = paymentMethods, showGooglePay = false, showLink = false, - currentSelection = PaymentSelection.Link, + currentSelection = PaymentSelection.Link(), nameProvider = { it!!.resolvableString }, isCbcEligible = true, defaultPaymentMethodId = null, @@ -136,7 +136,7 @@ class PaymentOptionsStateFactoryTest { paymentMethods = paymentMethods, showGooglePay = false, showLink = false, - currentSelection = PaymentSelection.Link, + currentSelection = PaymentSelection.Link(), nameProvider = { it!!.resolvableString }, isCbcEligible = true, defaultPaymentMethodId = null, diff --git a/paymentsheet/src/test/java/com/stripe/android/paymentsheet/PaymentOptionsViewModelTest.kt b/paymentsheet/src/test/java/com/stripe/android/paymentsheet/PaymentOptionsViewModelTest.kt index 7901b40d715..2f76149e2d5 100644 --- a/paymentsheet/src/test/java/com/stripe/android/paymentsheet/PaymentOptionsViewModelTest.kt +++ b/paymentsheet/src/test/java/com/stripe/android/paymentsheet/PaymentOptionsViewModelTest.kt @@ -224,7 +224,7 @@ internal class PaymentOptionsViewModelTest { ), ) - assertThat(viewModel.selection.value).isNotEqualTo(PaymentSelection.Link) + assertThat(viewModel.selection.value).isNotEqualTo(PaymentSelection.Link()) assertThat(viewModel.linkHandler.isLinkEnabled.value).isTrue() } @@ -234,7 +234,7 @@ internal class PaymentOptionsViewModelTest { linkState = null, ) - assertThat(viewModel.selection.value).isNotEqualTo(PaymentSelection.Link) + assertThat(viewModel.selection.value).isNotEqualTo(PaymentSelection.Link()) assertThat(viewModel.linkHandler.isLinkEnabled.value).isFalse() } @@ -486,16 +486,16 @@ internal class PaymentOptionsViewModelTest { runTest { val selection = PaymentSelection.Saved(PaymentMethodFixtures.US_BANK_ACCOUNT) - val viewModel = createViewModel().apply { updateSelection(PaymentSelection.Link) } + val viewModel = createViewModel().apply { updateSelection(PaymentSelection.Link()) } viewModel.paymentOptionResult.test { viewModel.handlePaymentMethodSelected(selection) expectNoEvents() - viewModel.handlePaymentMethodSelected(PaymentSelection.Link) + viewModel.handlePaymentMethodSelected(PaymentSelection.Link()) val result = awaitItem() as? PaymentOptionResult.Succeeded - assertThat(result?.paymentSelection).isEqualTo(PaymentSelection.Link) + assertThat(result?.paymentSelection).isEqualTo(PaymentSelection.Link()) } } diff --git a/paymentsheet/src/test/java/com/stripe/android/paymentsheet/PaymentSheetActivityTest.kt b/paymentsheet/src/test/java/com/stripe/android/paymentsheet/PaymentSheetActivityTest.kt index 10abeb92ac6..221fece8a52 100644 --- a/paymentsheet/src/test/java/com/stripe/android/paymentsheet/PaymentSheetActivityTest.kt +++ b/paymentsheet/src/test/java/com/stripe/android/paymentsheet/PaymentSheetActivityTest.kt @@ -41,7 +41,6 @@ import com.stripe.android.googlepaylauncher.GooglePayPaymentMethodLauncherContra import com.stripe.android.googlepaylauncher.injection.GooglePayPaymentMethodLauncherFactory import com.stripe.android.isInstanceOf import com.stripe.android.link.LinkActivityResult -import com.stripe.android.link.LinkConfigurationCoordinator import com.stripe.android.link.LinkPaymentLauncher import com.stripe.android.link.model.AccountStatus import com.stripe.android.link.ui.LinkButtonTestTag @@ -85,9 +84,9 @@ import com.stripe.android.testing.FakeErrorReporter import com.stripe.android.ui.core.cbc.CardBrandChoiceEligibility import com.stripe.android.ui.core.elements.TEST_TAG_DIALOG_CONFIRM_BUTTON import com.stripe.android.uicore.elements.bottomsheet.BottomSheetContentTestTag -import com.stripe.android.uicore.utils.stateFlowOf import com.stripe.android.utils.FakeCustomerRepository import com.stripe.android.utils.FakeIntentConfirmationInterceptor +import com.stripe.android.utils.FakeLinkConfigurationCoordinator import com.stripe.android.utils.FakePaymentElementLoader import com.stripe.android.utils.InjectableActivityScenario import com.stripe.android.utils.NullCardAccountRangeRepositoryFactory @@ -97,7 +96,6 @@ import com.stripe.android.utils.injectableActivityScenario import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.resetMain @@ -110,7 +108,6 @@ import org.mockito.kotlin.any import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock -import org.mockito.kotlin.stub import org.mockito.kotlin.verify import org.robolectric.annotation.Config import java.util.concurrent.CountDownLatch @@ -1137,10 +1134,10 @@ internal class PaymentSheetActivityTest { args: PaymentSheetContractV2.Args = PaymentSheetFixtures.ARGS_CUSTOMER_WITH_GOOGLEPAY, cbcEligibility: CardBrandChoiceEligibility = CardBrandChoiceEligibility.Ineligible, ): PaymentSheetViewModel = runBlocking { - val coordinator = mock().stub { - onBlocking { getAccountStatusFlow(any()) }.thenReturn(flowOf(AccountStatus.SignedOut)) - on { emailFlow } doReturn stateFlowOf("email@email.com") - } + val coordinator = FakeLinkConfigurationCoordinator( + accountStatus = AccountStatus.SignedOut, + email = "email@email.com" + ) TestViewModelFactory.create( linkConfigurationCoordinator = coordinator, diff --git a/paymentsheet/src/test/java/com/stripe/android/paymentsheet/PaymentSheetViewModelTest.kt b/paymentsheet/src/test/java/com/stripe/android/paymentsheet/PaymentSheetViewModelTest.kt index 3956c426410..53ff5259895 100644 --- a/paymentsheet/src/test/java/com/stripe/android/paymentsheet/PaymentSheetViewModelTest.kt +++ b/paymentsheet/src/test/java/com/stripe/android/paymentsheet/PaymentSheetViewModelTest.kt @@ -56,13 +56,16 @@ import com.stripe.android.model.PaymentMethodUpdateParams import com.stripe.android.model.SetupIntentFixtures import com.stripe.android.model.StripeIntent import com.stripe.android.paymentelement.confirmation.ConfirmationDefinition +import com.stripe.android.paymentelement.confirmation.ConfirmationHandler import com.stripe.android.paymentelement.confirmation.ConfirmationMediator import com.stripe.android.paymentelement.confirmation.DefaultConfirmationHandler +import com.stripe.android.paymentelement.confirmation.FakeConfirmationHandler import com.stripe.android.paymentelement.confirmation.PaymentMethodConfirmationOption import com.stripe.android.paymentelement.confirmation.createTestConfirmationHandlerFactory import com.stripe.android.paymentelement.confirmation.intent.DeferredIntentConfirmationType import com.stripe.android.paymentelement.confirmation.intent.IntentConfirmationInterceptor import com.stripe.android.paymentelement.confirmation.intent.InvalidDeferredIntentUsageException +import com.stripe.android.paymentelement.confirmation.link.LinkConfirmationOption import com.stripe.android.payments.core.analytics.ErrorReporter import com.stripe.android.payments.paymentlauncher.InternalPaymentResult import com.stripe.android.payments.paymentlauncher.PaymentLauncherContract @@ -595,7 +598,7 @@ internal class PaymentSheetViewModelTest { viewModel.error.test { assertThat(awaitItem()).isNull() - viewModel.updateSelection(PaymentSelection.Link) + viewModel.updateSelection(PaymentSelection.Link()) viewModel.checkout() assertThat(errorReporter.getLoggedErrors()).contains( @@ -746,6 +749,76 @@ internal class PaymentSheetViewModelTest { assertThat(viewModel.linkHandler.isLinkEnabled.value).isFalse() } + @Test + fun `Link Express is launched when viewmodel is started with logged in link account`() = runTest { + val confirmationHandler = FakeConfirmationHandler() + confirmationHandler.awaitResultTurbine.add(null) + confirmationHandler.awaitResultTurbine.add(null) + + createViewModel( + linkState = LinkState( + configuration = TestFactory.LINK_CONFIGURATION, + loginState = LinkState.LoginState.LoggedIn, + signupMode = null, + ), + confirmationHandlerFactory = { + confirmationHandler + } + ) + + val confirmationArgs = confirmationHandler.startTurbine.awaitItem() + assertThat(confirmationArgs.confirmationOption).isInstanceOf() + val confirmationOption = confirmationArgs.confirmationOption as? LinkConfirmationOption + assertThat(confirmationOption?.useLinkExpress).isTrue() + } + + @Test + fun `Link Express is not launched when viewmodel is started with logged out link account`() = runTest { + val confirmationHandler = FakeConfirmationHandler() + confirmationHandler.awaitResultTurbine.add(null) + confirmationHandler.awaitResultTurbine.add(null) + + createViewModel( + linkState = LinkState( + configuration = TestFactory.LINK_CONFIGURATION, + loginState = LinkState.LoginState.LoggedOut, + signupMode = null, + ), + confirmationHandlerFactory = { + confirmationHandler + } + ) + + confirmationHandler.startTurbine.ensureAllEventsConsumed() + } + + @Test + fun `checkout with Link starts confirmation with correct arguments`() = runTest { + val confirmationHandler = FakeConfirmationHandler() + confirmationHandler.awaitResultTurbine.add(null) + confirmationHandler.awaitResultTurbine.add(null) + + val viewModel = createViewModel( + linkState = LinkState( + configuration = TestFactory.LINK_CONFIGURATION, + loginState = LinkState.LoginState.LoggedOut, + signupMode = null, + ), + confirmationHandlerFactory = { + confirmationHandler + } + ) + + confirmationHandler.startTurbine.ensureAllEventsConsumed() + + viewModel.checkoutWithLink() + + val confirmationArgs = confirmationHandler.startTurbine.awaitItem() + assertThat(confirmationArgs.confirmationOption).isInstanceOf() + val confirmationOption = confirmationArgs.confirmationOption as? LinkConfirmationOption + assertThat(confirmationOption?.useLinkExpress).isFalse() + } + @Test fun `Google Pay checkout cancelled returns to Idle state`() = runTest { val viewModel = createViewModel() @@ -3176,6 +3249,7 @@ internal class PaymentSheetViewModelTest { cvcRecollectionHandler: CvcRecollectionHandler = this.cvcRecollectionHandler, cvcRecollectionInteractor: FakeCvcRecollectionInteractor = FakeCvcRecollectionInteractor(), shouldRegister: Boolean = true, + confirmationHandlerFactory: ConfirmationHandler.Factory? = null ): PaymentSheetViewModel { val paymentConfiguration = PaymentConfiguration(ApiKeyFixtures.FAKE_PUBLISHABLE_KEY) return TestViewModelFactory.create( @@ -3192,7 +3266,7 @@ internal class PaymentSheetViewModelTest { workContext = testDispatcher, savedStateHandle = thisSavedStateHandle, linkHandler = linkHandler, - confirmationHandlerFactory = createTestConfirmationHandlerFactory( + confirmationHandlerFactory = confirmationHandlerFactory ?: createTestConfirmationHandlerFactory( intentConfirmationInterceptor = intentConfirmationInterceptor, savedStateHandle = thisSavedStateHandle, bacsMandateConfirmationLauncherFactory = bacsMandateConfirmationLauncherFactory, diff --git a/paymentsheet/src/test/java/com/stripe/android/paymentsheet/analytics/PaymentSheetEventTest.kt b/paymentsheet/src/test/java/com/stripe/android/paymentsheet/analytics/PaymentSheetEventTest.kt index fa9c6a2f3d4..eecfd9bf92e 100644 --- a/paymentsheet/src/test/java/com/stripe/android/paymentsheet/analytics/PaymentSheetEventTest.kt +++ b/paymentsheet/src/test/java/com/stripe/android/paymentsheet/analytics/PaymentSheetEventTest.kt @@ -430,7 +430,7 @@ class PaymentSheetEventTest { linkMode = null, googlePaySupported = false, duration = (5L).seconds, - paymentSelection = PaymentSelection.Link, + paymentSelection = PaymentSelection.Link(), initializationMode = paymentIntentInitializationMode, orderedLpms = listOf("card"), ) @@ -617,7 +617,7 @@ class PaymentSheetEventTest { fun `Link payment method event should return expected event`() { val linkEvent = PaymentSheetEvent.Payment( mode = EventReporter.Mode.Complete, - paymentSelection = PaymentSelection.Link, + paymentSelection = PaymentSelection.Link(), duration = 1.milliseconds, result = PaymentSheetEvent.Payment.Result.Success, currency = "usd", @@ -885,7 +885,7 @@ class PaymentSheetEventTest { fun `Link payment method failure event should return expected event`() { val linkEvent = PaymentSheetEvent.Payment( mode = EventReporter.Mode.Complete, - paymentSelection = PaymentSelection.Link, + paymentSelection = PaymentSelection.Link(), duration = 1.milliseconds, result = PaymentSheetEvent.Payment.Result.Failure( error = PaymentSheetConfirmationError.Stripe(APIException()), diff --git a/paymentsheet/src/test/java/com/stripe/android/paymentsheet/flowcontroller/DefaultFlowControllerTest.kt b/paymentsheet/src/test/java/com/stripe/android/paymentsheet/flowcontroller/DefaultFlowControllerTest.kt index 42774d90597..7267440fe43 100644 --- a/paymentsheet/src/test/java/com/stripe/android/paymentsheet/flowcontroller/DefaultFlowControllerTest.kt +++ b/paymentsheet/src/test/java/com/stripe/android/paymentsheet/flowcontroller/DefaultFlowControllerTest.kt @@ -799,7 +799,7 @@ internal class DefaultFlowControllerTest { flowController.configureExpectingSuccess() flowController.confirmPaymentSelection( - paymentSelection = PaymentSelection.Link, + paymentSelection = PaymentSelection.Link(), state = PAYMENT_SHEET_STATE_FULL, appearance = PaymentSheetFixtures.CONFIG_CUSTOMER.appearance, initializationMode = INITIALIZATION_MODE, @@ -815,7 +815,7 @@ internal class DefaultFlowControllerTest { @Test fun `confirmPaymentSelection() with link payment method should launch LinkPaymentLauncher`() = runTest { val flowController = createFlowController( - paymentSelection = PaymentSelection.Link, + paymentSelection = PaymentSelection.Link(), stripeIntent = PaymentIntentFixtures.PI_REQUIRES_PAYMENT_METHOD.copy( paymentMethodTypes = PaymentIntentFixtures.PI_REQUIRES_PAYMENT_METHOD.paymentMethodTypes.plus("link") ) @@ -827,14 +827,14 @@ internal class DefaultFlowControllerTest { flowController.confirm() - verify(linkPaymentLauncher).present(any(), anyOrNull()) + verify(linkPaymentLauncher).present(any(), anyOrNull(), any()) } @Test fun `confirmPaymentSelection() with LinkInline and user not signed in should confirm with PaymentLauncher`() = runTest { val flowController = createFlowController( - paymentSelection = PaymentSelection.Link, + paymentSelection = PaymentSelection.Link(), stripeIntent = PaymentIntentFixtures.PI_REQUIRES_PAYMENT_METHOD.copy( paymentMethodTypes = PaymentIntentFixtures.PI_REQUIRES_PAYMENT_METHOD.paymentMethodTypes.plus("link") ) @@ -880,7 +880,7 @@ internal class DefaultFlowControllerTest { @Test fun `confirmPaymentSelection() with Link and shipping should have shipping details in confirm params`() = runTest { val flowController = createFlowController( - paymentSelection = PaymentSelection.Link, + paymentSelection = PaymentSelection.Link(), stripeIntent = PaymentIntentFixtures.PI_REQUIRES_PAYMENT_METHOD.copy( paymentMethodTypes = PaymentIntentFixtures.PI_REQUIRES_PAYMENT_METHOD.paymentMethodTypes.plus("link") ) @@ -938,7 +938,7 @@ internal class DefaultFlowControllerTest { fun `confirmPaymentSelection() with Link and no shipping should not have shipping details in confirm params`() = runTest { val flowController = createFlowController( - paymentSelection = PaymentSelection.Link, + paymentSelection = PaymentSelection.Link(), stripeIntent = PaymentIntentFixtures.PI_REQUIRES_PAYMENT_METHOD.copy( paymentMethodTypes = PaymentIntentFixtures.PI_REQUIRES_PAYMENT_METHOD.paymentMethodTypes.plus("link") ) @@ -1213,11 +1213,11 @@ internal class DefaultFlowControllerTest { configuration = PaymentSheetFixtures.CONFIG_CUSTOMER_WITH_GOOGLEPAY ) flowController.onPaymentOptionResult( - PaymentOptionResult.Succeeded(PaymentSelection.Link) + PaymentOptionResult.Succeeded(PaymentSelection.Link()) ) flowController.confirm() - verify(linkPaymentLauncher).present(any(), anyOrNull()) + verify(linkPaymentLauncher).present(any(), anyOrNull(), any()) } @Test diff --git a/paymentsheet/src/test/java/com/stripe/android/paymentsheet/flowcontroller/FlowControllerConfigurationHandlerTest.kt b/paymentsheet/src/test/java/com/stripe/android/paymentsheet/flowcontroller/FlowControllerConfigurationHandlerTest.kt index 4ede5e38b36..3b698fcedb9 100644 --- a/paymentsheet/src/test/java/com/stripe/android/paymentsheet/flowcontroller/FlowControllerConfigurationHandlerTest.kt +++ b/paymentsheet/src/test/java/com/stripe/android/paymentsheet/flowcontroller/FlowControllerConfigurationHandlerTest.kt @@ -90,7 +90,7 @@ class FlowControllerConfigurationHandlerTest { assertThat(configureErrors.awaitItem()).isNull() assertThat(viewModel.previousConfigureRequest).isNotNull() assertThat(configurationHandler.isConfigured).isTrue() - assertThat(viewModel.paymentSelection).isEqualTo(PaymentSelection.Link) + assertThat(viewModel.paymentSelection).isEqualTo(PaymentSelection.Link()) assertThat(viewModel.state).isNotNull() verify(eventReporter).onInit( configuration = PaymentSheetFixtures.CONFIG_CUSTOMER_WITH_GOOGLEPAY, @@ -169,7 +169,7 @@ class FlowControllerConfigurationHandlerTest { assertThat(configureErrors.awaitItem()).isNull() assertThat(viewModel.previousConfigureRequest).isEqualTo(newConfigureRequest) assertThat(configurationHandler.isConfigured).isTrue() - assertThat(viewModel.paymentSelection).isEqualTo(PaymentSelection.Link) + assertThat(viewModel.paymentSelection).isEqualTo(PaymentSelection.Link()) // We're running a new config, so we DO expect an interaction. verify(eventReporter).onInit( @@ -208,7 +208,7 @@ class FlowControllerConfigurationHandlerTest { assertThat(configureErrors.awaitItem()).isNull() assertThat(viewModel.previousConfigureRequest).isEqualTo(newConfigureRequest) assertThat(configurationHandler.isConfigured).isTrue() - assertThat(viewModel.paymentSelection).isEqualTo(PaymentSelection.Link) + assertThat(viewModel.paymentSelection).isEqualTo(PaymentSelection.Link()) // We're running a new config, so we DO expect an interaction. verify(eventReporter).onInit( @@ -513,7 +513,7 @@ class FlowControllerConfigurationHandlerTest { return FakePaymentElementLoader( customer = PaymentSheetFixtures.EMPTY_CUSTOMER_STATE, stripeIntent = PaymentIntentFixtures.PI_REQUIRES_PAYMENT_METHOD, - paymentSelection = PaymentSelection.Link, + paymentSelection = PaymentSelection.Link(), linkState = LinkState( configuration = mock(), loginState = LinkState.LoginState.LoggedIn, diff --git a/paymentsheet/src/test/java/com/stripe/android/paymentsheet/model/PaymentSelectionTest.kt b/paymentsheet/src/test/java/com/stripe/android/paymentsheet/model/PaymentSelectionTest.kt index 3e6ee47fbc7..7cfcc7b25bd 100644 --- a/paymentsheet/src/test/java/com/stripe/android/paymentsheet/model/PaymentSelectionTest.kt +++ b/paymentsheet/src/test/java/com/stripe/android/paymentsheet/model/PaymentSelectionTest.kt @@ -15,7 +15,7 @@ class PaymentSelectionTest { @Test fun `Doesn't display a mandate for Link`() { - val link = PaymentSelection.Link + val link = PaymentSelection.Link() val result = link.mandateText( merchantName = "Merchant", isSetupFlow = false, diff --git a/paymentsheet/src/test/java/com/stripe/android/paymentsheet/state/DefaultPaymentElementLoaderTest.kt b/paymentsheet/src/test/java/com/stripe/android/paymentsheet/state/DefaultPaymentElementLoaderTest.kt index 6301273d331..800b86c1c39 100644 --- a/paymentsheet/src/test/java/com/stripe/android/paymentsheet/state/DefaultPaymentElementLoaderTest.kt +++ b/paymentsheet/src/test/java/com/stripe/android/paymentsheet/state/DefaultPaymentElementLoaderTest.kt @@ -1113,7 +1113,7 @@ internal class DefaultPaymentElementLoaderTest { @Test fun `Emits correct events when loading succeeds with saved Link selection`() = runTest { testSuccessfulLoadSendsEventsCorrectly( - paymentSelection = PaymentSelection.Link + paymentSelection = PaymentSelection.Link() ) } diff --git a/paymentsheet/src/test/java/com/stripe/android/paymentsheet/ui/DefaultSelectSavedPaymentMethodsInteractorTest.kt b/paymentsheet/src/test/java/com/stripe/android/paymentsheet/ui/DefaultSelectSavedPaymentMethodsInteractorTest.kt index 03d8a5386b1..9dbe29b0fe7 100644 --- a/paymentsheet/src/test/java/com/stripe/android/paymentsheet/ui/DefaultSelectSavedPaymentMethodsInteractorTest.kt +++ b/paymentsheet/src/test/java/com/stripe/android/paymentsheet/ui/DefaultSelectSavedPaymentMethodsInteractorTest.kt @@ -201,7 +201,7 @@ class DefaultSelectSavedPaymentMethodsInteractorTest { @Test fun selectedPaymentOptionItem_currentSelectionIsLink() { - val currentSelectionFlow = MutableStateFlow(PaymentSelection.Link) + val currentSelectionFlow = MutableStateFlow(PaymentSelection.Link()) runScenario( paymentOptionsItems = MutableStateFlow( @@ -222,7 +222,7 @@ class DefaultSelectSavedPaymentMethodsInteractorTest { @Test fun selectedPaymentOptionItem_currentSelectionIsLink_canBeChangedToGooglePay() { val currentSelectionFlow: MutableStateFlow = - MutableStateFlow(PaymentSelection.Link) + MutableStateFlow(PaymentSelection.Link()) runScenario( paymentOptionsItems = MutableStateFlow( @@ -251,7 +251,7 @@ class DefaultSelectSavedPaymentMethodsInteractorTest { @Test fun selectedPaymentOptionItem_currentSelectionIsLink_doesNotChangeWhenSelectionBecomesNew() { val currentSelectionFlow: MutableStateFlow = - MutableStateFlow(PaymentSelection.Link) + MutableStateFlow(PaymentSelection.Link()) runScenario( paymentOptionsItems = MutableStateFlow( @@ -369,7 +369,7 @@ class DefaultSelectSavedPaymentMethodsInteractorTest { } } - currentSelectionFlow.value = PaymentSelection.Link + currentSelectionFlow.value = PaymentSelection.Link() interactor.state.test { awaitItem().run { diff --git a/paymentsheet/src/test/java/com/stripe/android/paymentsheet/verticalmode/DefaultPaymentMethodVerticalLayoutInteractorTest.kt b/paymentsheet/src/test/java/com/stripe/android/paymentsheet/verticalmode/DefaultPaymentMethodVerticalLayoutInteractorTest.kt index be408a5f622..732a04d41e3 100644 --- a/paymentsheet/src/test/java/com/stripe/android/paymentsheet/verticalmode/DefaultPaymentMethodVerticalLayoutInteractorTest.kt +++ b/paymentsheet/src/test/java/com/stripe/android/paymentsheet/verticalmode/DefaultPaymentMethodVerticalLayoutInteractorTest.kt @@ -558,7 +558,7 @@ class DefaultPaymentMethodVerticalLayoutInteractorTest { val displayablePaymentMethods = interactor.state.value.displayablePaymentMethods displayablePaymentMethods.first { it.code == "link" }.onClick() - assertThat(selectedWallet).isEqualTo(PaymentSelection.Link) + assertThat(selectedWallet).isEqualTo(PaymentSelection.Link()) displayablePaymentMethods.first { it.code == "google_pay" }.onClick() assertThat(selectedWallet).isEqualTo(PaymentSelection.GooglePay) } @@ -862,7 +862,7 @@ class DefaultPaymentMethodVerticalLayoutInteractorTest { @Test fun verticalModeScreenSelection_isNotUpdatedToNullWhenOnAnotherScreen() { - val expectedPaymentSelection = PaymentSelection.Link + val expectedPaymentSelection = PaymentSelection.Link() runScenario(initialSelection = expectedPaymentSelection, updateSelection = {}) { selectionSource.value = null @@ -876,10 +876,10 @@ class DefaultPaymentMethodVerticalLayoutInteractorTest { @Test fun verticalModeScreenSelection_isUpdatedToNullWhenCurrentScreen() { - runScenario(initialSelection = PaymentSelection.Link, updateSelection = {}) { + runScenario(initialSelection = PaymentSelection.Link(), updateSelection = {}) { interactor.state.test { awaitItem().run { - assertThat(selection).isEqualTo(PaymentSelection.Link) + assertThat(selection).isEqualTo(PaymentSelection.Link()) } isCurrentScreenSource.value = true selectionSource.value = null @@ -892,7 +892,7 @@ class DefaultPaymentMethodVerticalLayoutInteractorTest { @Test fun verticalModeScreenSelection_isNeverUpdatedToNewPmWithFormFields() { - val initialPaymentSelection = PaymentSelection.Link + val initialPaymentSelection = PaymentSelection.Link() runScenario( initialSelection = initialPaymentSelection, formTypeForCode = { FormType.UserInteractionRequired }, @@ -925,7 +925,7 @@ class DefaultPaymentMethodVerticalLayoutInteractorTest { @Test fun verticalModeScreenSelection_canBeANewPmWithoutFormFields() { - val initialPaymentSelection = PaymentSelection.Link + val initialPaymentSelection = PaymentSelection.Link() runScenario( initialSelection = initialPaymentSelection, formTypeForCode = { FormType.Empty }, @@ -944,7 +944,7 @@ class DefaultPaymentMethodVerticalLayoutInteractorTest { @Test fun verticalModeScreenSelection_canBeAnEpm() { - val initialPaymentSelection = PaymentSelection.Link + val initialPaymentSelection = PaymentSelection.Link() runScenario( initialSelection = initialPaymentSelection, formTypeForCode = { FormType.Empty }, @@ -965,7 +965,7 @@ class DefaultPaymentMethodVerticalLayoutInteractorTest { @Test fun verticalModeScreenSelection_canBeASavedPm() { - val initialPaymentSelection = PaymentSelection.Link + val initialPaymentSelection = PaymentSelection.Link() runScenario( initialSelection = initialPaymentSelection, formTypeForCode = { FormType.Empty }, @@ -992,7 +992,7 @@ class DefaultPaymentMethodVerticalLayoutInteractorTest { formTypeForCode = { FormType.Empty }, updateSelection = {}, ) { - val newSelection = PaymentSelection.Link + val newSelection = PaymentSelection.Link() selectionSource.value = newSelection interactor.state.test { @@ -1005,7 +1005,7 @@ class DefaultPaymentMethodVerticalLayoutInteractorTest { @Test fun verticalModeScreenSelection_canBeGooglePay() { - val initialPaymentSelection = PaymentSelection.Link + val initialPaymentSelection = PaymentSelection.Link() runScenario( initialSelection = initialPaymentSelection, formTypeForCode = { FormType.Empty }, diff --git a/paymentsheet/src/test/java/com/stripe/android/utils/FakeLinkConfigurationCoordinator.kt b/paymentsheet/src/test/java/com/stripe/android/utils/FakeLinkConfigurationCoordinator.kt index 780f57beac3..597235c507a 100644 --- a/paymentsheet/src/test/java/com/stripe/android/utils/FakeLinkConfigurationCoordinator.kt +++ b/paymentsheet/src/test/java/com/stripe/android/utils/FakeLinkConfigurationCoordinator.kt @@ -4,6 +4,8 @@ import com.stripe.android.core.model.CountryCode import com.stripe.android.link.LinkConfiguration import com.stripe.android.link.LinkConfigurationCoordinator import com.stripe.android.link.LinkPaymentDetails +import com.stripe.android.link.gate.FakeLinkGate +import com.stripe.android.link.gate.LinkGate import com.stripe.android.link.injection.LinkComponent import com.stripe.android.link.model.AccountStatus import com.stripe.android.link.ui.inline.UserInput @@ -39,10 +41,12 @@ internal class FakeLinkConfigurationCoordinator( ) ), private val accountStatus: AccountStatus = AccountStatus.SignedOut, + private val linkGate: LinkGate = FakeLinkGate(), + private val email: String? = null ) : LinkConfigurationCoordinator { override val emailFlow: StateFlow - get() = stateFlowOf(null) + get() = stateFlowOf(email) override fun getComponent(configuration: LinkConfiguration): LinkComponent { return mock() @@ -52,6 +56,10 @@ internal class FakeLinkConfigurationCoordinator( return flowOf(accountStatus) } + override fun linkGate(configuration: LinkConfiguration): LinkGate { + return linkGate + } + override suspend fun signInWithUserInput(configuration: LinkConfiguration, userInput: UserInput): Result { return Result.success(true) } diff --git a/paymentsheet/src/test/java/com/stripe/android/utils/RecordingLinkPaymentLauncher.kt b/paymentsheet/src/test/java/com/stripe/android/utils/RecordingLinkPaymentLauncher.kt index 60c0a0f1c94..c4f7966442e 100644 --- a/paymentsheet/src/test/java/com/stripe/android/utils/RecordingLinkPaymentLauncher.kt +++ b/paymentsheet/src/test/java/com/stripe/android/utils/RecordingLinkPaymentLauncher.kt @@ -39,13 +39,14 @@ internal object RecordingLinkPaymentLauncher { unregisterCalls.add(Unit) } - on { present(any(), anyOrNull()) } doAnswer { invocation -> + on { present(any(), anyOrNull(), any()) } doAnswer { invocation -> val arguments = invocation.arguments presentCalls.add( PresentCall( configuration = arguments[0] as LinkConfiguration, - linkAccount = arguments[1] as? LinkAccount + linkAccount = arguments[1] as? LinkAccount, + useLinkExpress = arguments[2] as Boolean ) ) } @@ -79,6 +80,7 @@ internal object RecordingLinkPaymentLauncher { data class PresentCall( val configuration: LinkConfiguration, - val linkAccount: LinkAccount? + val linkAccount: LinkAccount?, + val useLinkExpress: Boolean ) }