diff --git a/CHANGELOG.md b/CHANGELOG.md index e148fb93527..98a513fb1f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,12 +7,14 @@ ### PaymentSheet -* [CHANGED][5487](https://github.com/stripe/stripe-android/pull/5487) Updated Google Pay button to - match new brand guidelines. -* [ADDED][5502](https://github.com/stripe/stripe-android/pull/5502) Added phone number minimum +* [ADDED][5502](https://github.com/stripe/stripe-android/pull/5502) Added phone number minimum length validation -* [ADDED][5518](https://github.com/stripe/stripe-android/pull/5518) Added state/province dropdown +* [ADDED][5518](https://github.com/stripe/stripe-android/pull/5518) Added state/province dropdown for US and Canada. +* [CHANGED][5487](https://github.com/stripe/stripe-android/pull/5487) Updated Google Pay button to + match new brand guidelines. +* [FIXED][5480](https://github.com/stripe/stripe-android/pull/5480) `FlowController` now correctly + preserves the previously selected payment method for guests. ## 20.11.0 - 2022-08-29 This release adds postal code validation for PaymentSheet and fixed a fileprovider naming bug for Identity. diff --git a/link/src/main/java/com/stripe/android/link/LinkPaymentLauncher.kt b/link/src/main/java/com/stripe/android/link/LinkPaymentLauncher.kt index b27bdbdaa0e..c41be796b40 100644 --- a/link/src/main/java/com/stripe/android/link/LinkPaymentLauncher.kt +++ b/link/src/main/java/com/stripe/android/link/LinkPaymentLauncher.kt @@ -242,7 +242,7 @@ class LinkPaymentLauncher @AssistedInject internal constructor( @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) companion object { - val LINK_ENABLED = BuildConfig.DEBUG + val LINK_ENABLED = false val supportedFundingSources = SupportedPaymentMethod.allTypes } } diff --git a/link/src/main/java/com/stripe/android/link/injection/NamedConstants.kt b/link/src/main/java/com/stripe/android/link/injection/NamedConstants.kt index 228f539b744..a5ac7e925d9 100644 --- a/link/src/main/java/com/stripe/android/link/injection/NamedConstants.kt +++ b/link/src/main/java/com/stripe/android/link/injection/NamedConstants.kt @@ -32,6 +32,12 @@ const val CUSTOMER_NAME = "customerName" @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) const val SHIPPING_VALUES = "shippingValues" +/** + * Identifies whether Link is enabled. + */ +@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) +const val LINK_ENABLED = "linkEnabled" + /** * Identifies the Stripe Intent being processed by Link. */ diff --git a/paymentsheet/api/paymentsheet.api b/paymentsheet/api/paymentsheet.api index 92c90d7a0a4..20a34392db8 100644 --- a/paymentsheet/api/paymentsheet.api +++ b/paymentsheet/api/paymentsheet.api @@ -1068,11 +1068,11 @@ public final class com/stripe/android/paymentsheet/flowcontroller/DefaultFlowCon } public final class com/stripe/android/paymentsheet/flowcontroller/DefaultFlowControllerInitializer_Factory : dagger/internal/Factory { - public fun (Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;)V - public static fun create (Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;)Lcom/stripe/android/paymentsheet/flowcontroller/DefaultFlowControllerInitializer_Factory; + public fun (Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;)V + public static fun create (Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;)Lcom/stripe/android/paymentsheet/flowcontroller/DefaultFlowControllerInitializer_Factory; public fun get ()Lcom/stripe/android/paymentsheet/flowcontroller/DefaultFlowControllerInitializer; public synthetic fun get ()Ljava/lang/Object; - public static fun newInstance (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lcom/stripe/android/paymentsheet/repositories/StripeIntentRepository;Lcom/stripe/android/paymentsheet/model/StripeIntentValidator;Lcom/stripe/android/paymentsheet/repositories/CustomerRepository;Lcom/stripe/android/ui/core/forms/resources/ResourceRepository;Lcom/stripe/android/core/Logger;Lcom/stripe/android/paymentsheet/analytics/EventReporter;Lkotlin/coroutines/CoroutineContext;)Lcom/stripe/android/paymentsheet/flowcontroller/DefaultFlowControllerInitializer; + public static fun newInstance (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lcom/stripe/android/paymentsheet/repositories/StripeIntentRepository;Lcom/stripe/android/paymentsheet/model/StripeIntentValidator;Lcom/stripe/android/paymentsheet/repositories/CustomerRepository;Lcom/stripe/android/ui/core/forms/resources/ResourceRepository;Lcom/stripe/android/core/Logger;Lcom/stripe/android/paymentsheet/analytics/EventReporter;Lkotlin/coroutines/CoroutineContext;Z)Lcom/stripe/android/paymentsheet/flowcontroller/DefaultFlowControllerInitializer; } public final class com/stripe/android/paymentsheet/flowcontroller/DefaultFlowController_Factory : dagger/internal/Factory { @@ -1206,6 +1206,14 @@ public final class com/stripe/android/paymentsheet/injection/FlowControllerModul public static fun provideEventReporterMode ()Lcom/stripe/android/paymentsheet/analytics/EventReporter$Mode; } +public final class com/stripe/android/paymentsheet/injection/FlowControllerModule_Companion_ProvideLinkEnabledFactory : dagger/internal/Factory { + public fun ()V + public static fun create ()Lcom/stripe/android/paymentsheet/injection/FlowControllerModule_Companion_ProvideLinkEnabledFactory; + public fun get ()Ljava/lang/Boolean; + public synthetic fun get ()Ljava/lang/Object; + public static fun provideLinkEnabled ()Z +} + public final class com/stripe/android/paymentsheet/injection/FlowControllerModule_Companion_ProvidePrefsRepositoryFactoryFactory : dagger/internal/Factory { public fun (Ljavax/inject/Provider;Ljavax/inject/Provider;)V public static fun create (Ljavax/inject/Provider;Ljavax/inject/Provider;)Lcom/stripe/android/paymentsheet/injection/FlowControllerModule_Companion_ProvidePrefsRepositoryFactoryFactory; diff --git a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/DefaultPrefsRepository.kt b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/DefaultPrefsRepository.kt index 37200cc8c10..739881f5bcc 100644 --- a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/DefaultPrefsRepository.kt +++ b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/DefaultPrefsRepository.kt @@ -9,7 +9,7 @@ import kotlin.coroutines.CoroutineContext internal class DefaultPrefsRepository( private val context: Context, - private val customerId: String, + private val customerId: String?, private val workContext: CoroutineContext ) : PrefsRepository { private val prefs: SharedPreferences by lazy { @@ -51,7 +51,7 @@ internal class DefaultPrefsRepository( } private fun getKey(): String { - return "customer[$customerId]" + return customerId?.let { "customer[$it]" } ?: "guest" } private companion object { diff --git a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/PrefsRepository.kt b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/PrefsRepository.kt index bbe612ea377..daf9ae1fe94 100644 --- a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/PrefsRepository.kt +++ b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/PrefsRepository.kt @@ -10,13 +10,4 @@ internal interface PrefsRepository { ): SavedSelection fun savePaymentSelection(paymentSelection: PaymentSelection?) - - class Noop : PrefsRepository { - override suspend fun getSavedSelection( - isGooglePayAvailable: Boolean, - isLinkAvailable: Boolean - ) = SavedSelection.None - - override fun savePaymentSelection(paymentSelection: PaymentSelection?) {} - } } diff --git a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/flowcontroller/DefaultFlowControllerInitializer.kt b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/flowcontroller/DefaultFlowControllerInitializer.kt index e8fd985b327..7f636442cbf 100644 --- a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/flowcontroller/DefaultFlowControllerInitializer.kt +++ b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/flowcontroller/DefaultFlowControllerInitializer.kt @@ -4,7 +4,7 @@ import com.stripe.android.core.Logger import com.stripe.android.core.injection.IOContext import com.stripe.android.googlepaylauncher.GooglePayEnvironment import com.stripe.android.googlepaylauncher.GooglePayRepository -import com.stripe.android.link.LinkPaymentLauncher +import com.stripe.android.link.injection.LINK_ENABLED import com.stripe.android.model.PaymentMethod import com.stripe.android.model.StripeIntent import com.stripe.android.paymentsheet.PaymentSheet @@ -23,6 +23,7 @@ import com.stripe.android.ui.core.forms.resources.ResourceRepository import kotlinx.coroutines.flow.first import kotlinx.coroutines.withContext import javax.inject.Inject +import javax.inject.Named import javax.inject.Singleton import kotlin.coroutines.CoroutineContext @@ -38,7 +39,8 @@ internal class DefaultFlowControllerInitializer @Inject constructor( private val lpmResourceRepository: ResourceRepository, private val logger: Logger, val eventReporter: EventReporter, - @IOContext private val workContext: CoroutineContext + @IOContext private val workContext: CoroutineContext, + @Named(LINK_ENABLED) private val isLinkEnabled: Boolean ) : FlowControllerInitializer { override suspend fun init( @@ -50,24 +52,16 @@ internal class DefaultFlowControllerInitializer @Inject constructor( retrieveStripeIntent(clientSecret) }.fold( onSuccess = { stripeIntent -> - val isLinkReady = LinkPaymentLauncher.LINK_ENABLED && + val isLinkReady = isLinkEnabled && stripeIntent.paymentMethodTypes.contains(PaymentMethod.Type.Link.code) - paymentSheetConfiguration?.customer?.let { customerConfig -> - createWithCustomer( - clientSecret, - stripeIntent, - customerConfig, - paymentSheetConfiguration, - isGooglePayReady, - isLinkReady - ) - } ?: createWithoutCustomer( - clientSecret, - stripeIntent, - paymentSheetConfiguration, - isGooglePayReady, - isLinkReady + create( + clientSecret = clientSecret, + stripeIntent = stripeIntent, + customerConfig = paymentSheetConfiguration?.customer, + config = paymentSheetConfiguration, + isGooglePayReady = isGooglePayReady, + isLinkReady = isLinkReady ) }, onFailure = { @@ -92,16 +86,50 @@ internal class DefaultFlowControllerInitializer @Inject constructor( }?.isReady()?.first() ?: false } - private suspend fun createWithCustomer( + private suspend fun create( clientSecret: ClientSecret, stripeIntent: StripeIntent, - customerConfig: PaymentSheet.CustomerConfiguration, + customerConfig: PaymentSheet.CustomerConfiguration?, config: PaymentSheet.Configuration?, isGooglePayReady: Boolean, isLinkReady: Boolean ): FlowControllerInitializer.InitResult { val prefsRepository = prefsRepositoryFactory(customerConfig) + val paymentMethods = if (customerConfig != null) { + retrieveCustomerPaymentMethods( + stripeIntent, + config, + customerConfig + ) + } else { + emptyList() + } + + val savedSelection = retrieveSavedPaymentSelection( + prefsRepository, + isGooglePayReady, + isLinkReady, + paymentMethods + ) + + return FlowControllerInitializer.InitResult.Success( + InitData( + config = config, + clientSecret = clientSecret, + stripeIntent = stripeIntent, + paymentMethods = paymentMethods, + savedSelection = savedSelection, + isGooglePayReady = isGooglePayReady + ) + ) + } + + private suspend fun retrieveCustomerPaymentMethods( + stripeIntent: StripeIntent, + config: PaymentSheet.Configuration?, + customerConfig: PaymentSheet.CustomerConfiguration + ): List { val paymentMethodTypes = getSupportedSavedCustomerPMs( stripeIntent, config, @@ -117,74 +145,43 @@ internal class DefaultFlowControllerInitializer @Inject constructor( paymentMethodTypes ).filter { paymentMethod -> paymentMethod.hasExpectedDetails() - }.let { paymentMethods -> - setLastSavedPaymentMethod( - prefsRepository, - isGooglePayReady, - isLinkReady, - paymentMethods - ) - - FlowControllerInitializer.InitResult.Success( - InitData( - config = config, - clientSecret = clientSecret, - stripeIntent = stripeIntent, - paymentMethods = paymentMethods, - savedSelection = prefsRepository.getSavedSelection( - isGooglePayReady, - isLinkReady - ), - isGooglePayReady = isGooglePayReady - ) - ) } } - private fun createWithoutCustomer( - clientSecret: ClientSecret, - stripeIntent: StripeIntent, - config: PaymentSheet.Configuration?, + private suspend fun retrieveSavedPaymentSelection( + prefsRepository: PrefsRepository, isGooglePayReady: Boolean, - isLinkReady: Boolean - ): FlowControllerInitializer.InitResult { - val savedSelection = if (isLinkReady) { - SavedSelection.Link - } else if (isGooglePayReady) { - SavedSelection.GooglePay - } else { - SavedSelection.None + isLinkReady: Boolean, + paymentMethods: List + ): SavedSelection { + val savedSelection = prefsRepository.getSavedSelection(isGooglePayReady, isLinkReady) + if (savedSelection != SavedSelection.None) { + return savedSelection } - return FlowControllerInitializer.InitResult.Success( - InitData( - config = config, - clientSecret = clientSecret, - stripeIntent = stripeIntent, - paymentMethods = emptyList(), - savedSelection = savedSelection, - isGooglePayReady = isGooglePayReady - ) + // No saved selection has been set yet, so we'll initialize it with a default + // value based on which payment methods are available. + val paymentSelection = determineDefaultPaymentSelection( + isGooglePayReady, + isLinkReady, + paymentMethods ) + + prefsRepository.savePaymentSelection(paymentSelection) + + return prefsRepository.getSavedSelection(isGooglePayReady, isLinkReady) } - private suspend fun setLastSavedPaymentMethod( - prefsRepository: PrefsRepository, + private fun determineDefaultPaymentSelection( isGooglePayReady: Boolean, isLinkReady: Boolean, paymentMethods: List - ) { - if ( - prefsRepository.getSavedSelection(isGooglePayReady, isLinkReady) == SavedSelection.None - ) { - when { - paymentMethods.isNotEmpty() -> PaymentSelection.Saved(paymentMethods.first()) - isLinkReady -> PaymentSelection.Link - isGooglePayReady -> PaymentSelection.GooglePay - else -> null - }?.let { - prefsRepository.savePaymentSelection(it) - } + ): PaymentSelection? { + return when { + paymentMethods.isNotEmpty() -> PaymentSelection.Saved(paymentMethods.first()) + isLinkReady -> PaymentSelection.Link + isGooglePayReady -> PaymentSelection.GooglePay + else -> null } } diff --git a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/injection/FlowControllerModule.kt b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/injection/FlowControllerModule.kt index 8018ab6774e..d9103e5e06f 100644 --- a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/injection/FlowControllerModule.kt +++ b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/injection/FlowControllerModule.kt @@ -4,6 +4,8 @@ import android.content.Context import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelStoreOwner import com.stripe.android.core.injection.IOContext +import com.stripe.android.link.LinkPaymentLauncher +import com.stripe.android.link.injection.LINK_ENABLED import com.stripe.android.payments.core.injection.PRODUCT_USAGE import com.stripe.android.paymentsheet.DefaultPrefsRepository import com.stripe.android.paymentsheet.PaymentSheet @@ -54,13 +56,11 @@ internal abstract class FlowControllerModule { appContext: Context, @IOContext workContext: CoroutineContext ): (PaymentSheet.CustomerConfiguration?) -> PrefsRepository = { customerConfig -> - customerConfig?.let { - DefaultPrefsRepository( - appContext, - it.id, - workContext - ) - } ?: PrefsRepository.Noop() + DefaultPrefsRepository( + appContext, + customerConfig?.id, + workContext + ) } @Provides @@ -72,6 +72,11 @@ internal abstract class FlowControllerModule { @Named(PRODUCT_USAGE) fun provideProductUsageTokens() = setOf("PaymentSheet.FlowController") + @Provides + @Singleton + @Named(LINK_ENABLED) + fun provideLinkEnabled() = LinkPaymentLauncher.LINK_ENABLED + @Provides @Singleton fun provideViewModel(viewModelStoreOwner: ViewModelStoreOwner): FlowControllerViewModel = diff --git a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/injection/PaymentOptionsViewModelModule.kt b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/injection/PaymentOptionsViewModelModule.kt index 70e8dc9e5d1..7f850423ddc 100644 --- a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/injection/PaymentOptionsViewModelModule.kt +++ b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/injection/PaymentOptionsViewModelModule.kt @@ -29,13 +29,11 @@ internal class PaymentOptionsViewModelModule { appContext: Context, @IOContext workContext: CoroutineContext ): (PaymentSheet.CustomerConfiguration?) -> PrefsRepository = { customerConfig -> - customerConfig?.let { - DefaultPrefsRepository( - appContext, - it.id, - workContext - ) - } ?: PrefsRepository.Noop() + DefaultPrefsRepository( + appContext, + customerConfig?.id, + workContext + ) } /** diff --git a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/injection/PaymentSheetViewModelModule.kt b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/injection/PaymentSheetViewModelModule.kt index 1a14ac9580b..7333b4cf148 100644 --- a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/injection/PaymentSheetViewModelModule.kt +++ b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/injection/PaymentSheetViewModelModule.kt @@ -22,12 +22,10 @@ internal class PaymentSheetViewModelModule(private val starterArgs: PaymentSheet appContext: Context, @IOContext workContext: CoroutineContext ): PrefsRepository { - return starterArgs.config?.customer?.let { (id) -> - DefaultPrefsRepository( - appContext, - customerId = id, - workContext = workContext - ) - } ?: PrefsRepository.Noop() + return DefaultPrefsRepository( + appContext, + customerId = starterArgs.config?.customer?.id, + workContext = workContext + ) } } diff --git a/paymentsheet/src/test/java/com/stripe/android/model/PaymentIntentFixtures.kt b/paymentsheet/src/test/java/com/stripe/android/model/PaymentIntentFixtures.kt index 95286ff2722..a9938653cb4 100644 --- a/paymentsheet/src/test/java/com/stripe/android/model/PaymentIntentFixtures.kt +++ b/paymentsheet/src/test/java/com/stripe/android/model/PaymentIntentFixtures.kt @@ -312,7 +312,8 @@ internal object PaymentIntentFixtures { "next_action": null, "payment_method": null, "payment_method_types": [ - "card" + "card", + "link" ], "receipt_email": null, "setup_future_usage": null, diff --git a/paymentsheet/src/test/java/com/stripe/android/paymentsheet/FakePrefsRepository.kt b/paymentsheet/src/test/java/com/stripe/android/paymentsheet/FakePrefsRepository.kt index c9980b4ccc7..fd78b405f65 100644 --- a/paymentsheet/src/test/java/com/stripe/android/paymentsheet/FakePrefsRepository.kt +++ b/paymentsheet/src/test/java/com/stripe/android/paymentsheet/FakePrefsRepository.kt @@ -15,13 +15,20 @@ internal class FakePrefsRepository : PrefsRepository { paymentSelectionArgs.add(paymentSelection) when (paymentSelection) { + is PaymentSelection.Link -> { + SavedSelection.Link + } PaymentSelection.GooglePay -> { SavedSelection.GooglePay } is PaymentSelection.Saved -> { SavedSelection.PaymentMethod(paymentSelection.paymentMethod.id.orEmpty()) } - else -> SavedSelection.None + is PaymentSelection.New.Card, + is PaymentSelection.New.GenericPaymentMethod, + is PaymentSelection.New.LinkInline, + is PaymentSelection.New.USBankAccount, + null -> SavedSelection.None }.let { savedSelection = it } diff --git a/paymentsheet/src/test/java/com/stripe/android/paymentsheet/flowcontroller/DefaultFlowControllerInitializerTest.kt b/paymentsheet/src/test/java/com/stripe/android/paymentsheet/flowcontroller/DefaultFlowControllerInitializerTest.kt index fa69d015ba8..f2db060f9c3 100644 --- a/paymentsheet/src/test/java/com/stripe/android/paymentsheet/flowcontroller/DefaultFlowControllerInitializerTest.kt +++ b/paymentsheet/src/test/java/com/stripe/android/paymentsheet/flowcontroller/DefaultFlowControllerInitializerTest.kt @@ -4,6 +4,7 @@ import android.app.Application import androidx.test.core.app.ApplicationProvider import com.google.common.truth.Truth.assertThat import com.stripe.android.core.Logger +import com.stripe.android.core.model.CountryCode import com.stripe.android.googlepaylauncher.GooglePayRepository import com.stripe.android.model.PaymentIntent import com.stripe.android.model.PaymentIntentFixtures @@ -12,6 +13,7 @@ import com.stripe.android.model.PaymentMethodFixtures import com.stripe.android.model.StripeIntent import com.stripe.android.paymentsheet.FakeCustomerRepository import com.stripe.android.paymentsheet.FakePrefsRepository +import com.stripe.android.paymentsheet.PaymentSheet import com.stripe.android.paymentsheet.PaymentSheetFixtures import com.stripe.android.paymentsheet.analytics.EventReporter import com.stripe.android.paymentsheet.model.PaymentSelection @@ -394,8 +396,61 @@ internal class DefaultFlowControllerInitializerTest { .isInstanceOf(FlowControllerInitializer.InitResult::class.java) } + @Test + fun `Defaults to Google Play for guests if Link is not enabled`() = runTest { + val result = createInitializer( + stripeIntentRepo = StripeIntentRepository.Static( + PaymentIntentFixtures.PI_REQUIRES_PAYMENT_METHOD + ) + ).init( + clientSecret = PaymentSheetFixtures.PAYMENT_INTENT_CLIENT_SECRET, + paymentSheetConfiguration = mockConfiguration() + ) as FlowControllerInitializer.InitResult.Success + + assertThat(result.initData.savedSelection).isEqualTo(SavedSelection.GooglePay) + } + + @Test + fun `Defaults to Link for guests if available`() = runTest { + val result = createInitializer( + stripeIntentRepo = StripeIntentRepository.Static( + PaymentIntentFixtures.PI_REQUIRES_PAYMENT_METHOD + ), + isLinkEnabled = true + ).init( + clientSecret = PaymentSheetFixtures.PAYMENT_INTENT_CLIENT_SECRET, + // The mock configuration is necessary to enable Google Pay. We want to do that, + // so that we can check that Link is preferred. + paymentSheetConfiguration = mockConfiguration() + ) as FlowControllerInitializer.InitResult.Success + + assertThat(result.initData.savedSelection).isEqualTo(SavedSelection.Link) + } + + @Test + fun `Defaults to first existing payment method for known customer`() = runTest { + val result = createInitializer( + stripeIntentRepo = StripeIntentRepository.Static( + PaymentIntentFixtures.PI_REQUIRES_PAYMENT_METHOD + ), + customerRepo = FakeCustomerRepository(paymentMethods = PAYMENT_METHODS) + ).init( + clientSecret = PaymentSheetFixtures.PAYMENT_INTENT_CLIENT_SECRET, + paymentSheetConfiguration = mockConfiguration( + customer = PaymentSheet.CustomerConfiguration( + id = "some_id", + ephemeralKeySecret = "some_key" + ) + ) + ) as FlowControllerInitializer.InitResult.Success + + val expectedPaymentMethodId = requireNotNull(PAYMENT_METHODS.first().id) + assertThat(result.initData.savedSelection).isEqualTo(SavedSelection.PaymentMethod(id = expectedPaymentMethodId)) + } + private fun createInitializer( isGooglePayReady: Boolean = true, + isLinkEnabled: Boolean = false, stripeIntentRepo: StripeIntentRepository = stripeIntentRepository, customerRepo: CustomerRepository = customerRepository ): FlowControllerInitializer { @@ -408,7 +463,22 @@ internal class DefaultFlowControllerInitializerTest { lpmResourceRepository, Logger.noop(), eventReporter, - testDispatcher + testDispatcher, + isLinkEnabled + ) + } + + private fun mockConfiguration( + customer: PaymentSheet.CustomerConfiguration? = null, + isGooglePayEnabled: Boolean = true + ): PaymentSheet.Configuration { + return PaymentSheet.Configuration( + merchantDisplayName = "Merchant", + customer = customer, + googlePay = PaymentSheet.GooglePayConfiguration( + environment = PaymentSheet.GooglePayConfiguration.Environment.Test, + countryCode = CountryCode.US.value + ).takeIf { isGooglePayEnabled } ) }