diff --git a/payments-core/api/payments-core.api b/payments-core/api/payments-core.api index c71f7834dce..36fe6cc1a68 100644 --- a/payments-core/api/payments-core.api +++ b/payments-core/api/payments-core.api @@ -5176,11 +5176,11 @@ public abstract interface class com/stripe/android/paymentsheet/PaymentSheetResu } public final class com/stripe/android/paymentsheet/PaymentSheetViewModel_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;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;Ljavax/inject/Provider;Ljavax/inject/Provider;)Lcom/stripe/android/paymentsheet/PaymentSheetViewModel_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;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;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;)Lcom/stripe/android/paymentsheet/PaymentSheetViewModel_Factory; public fun get ()Lcom/stripe/android/paymentsheet/PaymentSheetViewModel; public synthetic fun get ()Ljava/lang/Object; - public static fun newInstance (Landroid/app/Application;Lcom/stripe/android/paymentsheet/PaymentSheetContract$Args;Lcom/stripe/android/paymentsheet/analytics/EventReporter;Ldagger/Lazy;Lcom/stripe/android/paymentsheet/repositories/StripeIntentRepository;Lcom/stripe/android/paymentsheet/repositories/CustomerRepository;Ljavax/inject/Provider;Lcom/stripe/android/paymentsheet/PrefsRepository;Lcom/stripe/android/Logger;Lkotlin/coroutines/CoroutineContext;Lcom/stripe/android/PaymentController;Lcom/stripe/android/googlepaylauncher/GooglePayPaymentMethodLauncherFactory;)Lcom/stripe/android/paymentsheet/PaymentSheetViewModel; + public static fun newInstance (Landroid/app/Application;Lcom/stripe/android/paymentsheet/PaymentSheetContract$Args;Lcom/stripe/android/paymentsheet/analytics/EventReporter;Ldagger/Lazy;Lcom/stripe/android/paymentsheet/repositories/StripeIntentRepository;Lcom/stripe/android/paymentsheet/model/StripeIntentValidator;Lcom/stripe/android/paymentsheet/repositories/CustomerRepository;Ljavax/inject/Provider;Lcom/stripe/android/paymentsheet/PrefsRepository;Lcom/stripe/android/PaymentController;Lcom/stripe/android/googlepaylauncher/GooglePayPaymentMethodLauncherFactory;Lcom/stripe/android/Logger;Lkotlin/coroutines/CoroutineContext;)Lcom/stripe/android/paymentsheet/PaymentSheetViewModel; } public final class com/stripe/android/paymentsheet/analytics/DefaultDeviceIdRepository_Factory : dagger/internal/Factory { @@ -5200,11 +5200,11 @@ public final class com/stripe/android/paymentsheet/analytics/DefaultEventReporte } 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;)V - public static fun create (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;)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;)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/repositories/CustomerRepository;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/Logger;Lkotlin/coroutines/CoroutineContext;)Lcom/stripe/android/paymentsheet/flowcontroller/DefaultFlowControllerInitializer; } public final class com/stripe/android/paymentsheet/flowcontroller/DefaultFlowController_Factory : dagger/internal/Factory { @@ -5343,6 +5343,14 @@ public final class com/stripe/android/paymentsheet/model/PaymentOption { public fun toString ()Ljava/lang/String; } +public final class com/stripe/android/paymentsheet/model/StripeIntentValidator_Factory : dagger/internal/Factory { + public fun ()V + public static fun create ()Lcom/stripe/android/paymentsheet/model/StripeIntentValidator_Factory; + public fun get ()Lcom/stripe/android/paymentsheet/model/StripeIntentValidator; + public synthetic fun get ()Ljava/lang/Object; + public static fun newInstance ()Lcom/stripe/android/paymentsheet/model/StripeIntentValidator; +} + public final class com/stripe/android/paymentsheet/repositories/CustomerApiRepository_Factory : dagger/internal/Factory { public fun (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;)Lcom/stripe/android/paymentsheet/repositories/CustomerApiRepository_Factory; diff --git a/payments-core/src/main/java/com/stripe/android/paymentsheet/PaymentSheetViewModel.kt b/payments-core/src/main/java/com/stripe/android/paymentsheet/PaymentSheetViewModel.kt index 6b55012e97a..ec248f13e71 100644 --- a/payments-core/src/main/java/com/stripe/android/paymentsheet/PaymentSheetViewModel.kt +++ b/payments-core/src/main/java/com/stripe/android/paymentsheet/PaymentSheetViewModel.kt @@ -78,14 +78,15 @@ internal class PaymentSheetViewModel @Inject internal constructor( // Properties provided through injection private val lazyPaymentConfig: Lazy, private val stripeIntentRepository: StripeIntentRepository, + private val stripeIntentValidator: StripeIntentValidator, customerRepository: CustomerRepository, private val paymentFlowResultProcessorProvider: Provider>>, prefsRepository: PrefsRepository, - private val logger: Logger, - @IOContext workContext: CoroutineContext, private val paymentController: PaymentController, - private val googlePayPaymentMethodLauncherFactory: GooglePayPaymentMethodLauncherFactory + private val googlePayPaymentMethodLauncherFactory: GooglePayPaymentMethodLauncherFactory, + private val logger: Logger, + @IOContext workContext: CoroutineContext ) : BaseSheetViewModel( application = application, config = args.config, @@ -132,8 +133,6 @@ internal class PaymentSheetViewModel @Inject internal constructor( override var newCard: PaymentSelection.New.Card? = null - private val stripeIntentValidator = StripeIntentValidator() - @VisibleForTesting internal var googlePayPaymentMethodLauncher: GooglePayPaymentMethodLauncher? = null @@ -216,19 +215,15 @@ internal class PaymentSheetViewModel @Inject internal constructor( } private fun onStripeIntentFetchResponse(stripeIntent: StripeIntent) { - if (stripeIntent.isConfirmed) { - onConfirmedStripeIntent(stripeIntent) - } else { - runCatching { - stripeIntentValidator.requireValid(stripeIntent) - }.fold( - onSuccess = { - updatePaymentMethods(stripeIntent) - resetViewState(stripeIntent) - }, - onFailure = ::onFatal - ) - } + runCatching { + stripeIntentValidator.requireValid(stripeIntent) + }.fold( + onSuccess = { + updatePaymentMethods(stripeIntent) + resetViewState(stripeIntent) + }, + onFailure = ::onFatal + ) } /** @@ -272,22 +267,6 @@ internal class PaymentSheetViewModel @Inject internal constructor( } } - /** - * There's nothing left to be done in payment sheet if the [StripeIntent] is confirmed. - * - * See [How intents work](https://stripe.com/docs/payments/intents) for more details. - */ - private fun onConfirmedStripeIntent(stripeIntent: StripeIntent) { - logger.info( - """ - ${stripeIntent.javaClass.simpleName} with id=${stripeIntent.id} has already been confirmed. - """.trimIndent() - ) - _viewState.value = PaymentSheetViewState.FinishProcessing { - _paymentSheetResult.value = PaymentSheetResult.Completed - } - } - private fun resetViewState(stripeIntent: StripeIntent, @IntegerRes stringResId: Int?) { resetViewState( stripeIntent, @@ -471,6 +450,7 @@ internal class PaymentSheetViewModel @Inject internal constructor( } override fun onFatal(throwable: Throwable) { + logger.error("Payment Sheet error", throwable) _fatal.value = throwable _paymentSheetResult.value = PaymentSheetResult.Failed(throwable) } @@ -503,6 +483,7 @@ internal class PaymentSheetViewModel @Inject internal constructor( private val applicationSupplier: () -> Application, private val starterArgsSupplier: () -> PaymentSheetContract.Args ) : ViewModelProvider.Factory { + @Suppress("UNCHECKED_CAST") override fun create(modelClass: Class): T { return DaggerPaymentSheetViewModelComponent.builder() .application(applicationSupplier()) diff --git a/payments-core/src/main/java/com/stripe/android/paymentsheet/flowcontroller/DefaultFlowControllerInitializer.kt b/payments-core/src/main/java/com/stripe/android/paymentsheet/flowcontroller/DefaultFlowControllerInitializer.kt index 9dd54adc7f1..f2667ce7de2 100644 --- a/payments-core/src/main/java/com/stripe/android/paymentsheet/flowcontroller/DefaultFlowControllerInitializer.kt +++ b/payments-core/src/main/java/com/stripe/android/paymentsheet/flowcontroller/DefaultFlowControllerInitializer.kt @@ -1,5 +1,6 @@ package com.stripe.android.paymentsheet.flowcontroller +import com.stripe.android.Logger import com.stripe.android.googlepaylauncher.GooglePayEnvironment import com.stripe.android.googlepaylauncher.GooglePayRepository import com.stripe.android.model.PaymentMethod @@ -25,10 +26,11 @@ internal class DefaultFlowControllerInitializer @Inject constructor( private val prefsRepositoryFactory: @JvmSuppressWildcards (PaymentSheet.CustomerConfiguration?) -> PrefsRepository, private val googlePayRepositoryFactory: @JvmSuppressWildcards (GooglePayEnvironment) -> GooglePayRepository, private val stripeIntentRepository: StripeIntentRepository, + private val stripeIntentValidator: StripeIntentValidator, private val customerRepository: CustomerRepository, + private val logger: Logger, @IOContext private val workContext: CoroutineContext ) : FlowControllerInitializer { - private val stripeIntentValidator = StripeIntentValidator() override suspend fun init( clientSecret: ClientSecret, @@ -104,6 +106,7 @@ internal class DefaultFlowControllerInitializer @Inject constructor( } }, onFailure = { + logger.error("Failure initializing FlowController", it) FlowControllerInitializer.InitResult.Failure(it) } ) @@ -142,6 +145,7 @@ internal class DefaultFlowControllerInitializer @Inject constructor( ) }, onFailure = { + logger.error("Failure initializing FlowController", it) FlowControllerInitializer.InitResult.Failure(it) } ) diff --git a/payments-core/src/main/java/com/stripe/android/paymentsheet/model/StripeIntentValidator.kt b/payments-core/src/main/java/com/stripe/android/paymentsheet/model/StripeIntentValidator.kt index 3700b47f883..30156955293 100644 --- a/payments-core/src/main/java/com/stripe/android/paymentsheet/model/StripeIntentValidator.kt +++ b/payments-core/src/main/java/com/stripe/android/paymentsheet/model/StripeIntentValidator.kt @@ -3,11 +3,12 @@ package com.stripe.android.paymentsheet.model import com.stripe.android.model.PaymentIntent import com.stripe.android.model.SetupIntent import com.stripe.android.model.StripeIntent +import javax.inject.Inject /** * Validator for [PaymentIntent] or [SetupIntent] instances used in PaymentSheet. */ -internal class StripeIntentValidator { +internal class StripeIntentValidator @Inject constructor() { @JvmSynthetic fun requireValid( stripeIntent: StripeIntent @@ -18,7 +19,7 @@ internal class StripeIntentValidator { error( """ PaymentIntent with confirmation_method='automatic' is required. - The current PaymentIntent has confirmation_method ${stripeIntent.confirmationMethod}. + The current PaymentIntent has confirmation_method '${stripeIntent.confirmationMethod}'. See https://stripe.com/docs/api/payment_intents/object#payment_intent_object-confirmation_method. """.trimIndent() ) @@ -31,7 +32,7 @@ internal class StripeIntentValidator { error( """ A PaymentIntent with status='requires_payment_method' or 'requires_action` is required. - The current PaymentIntent has status ${stripeIntent.status}. + The current PaymentIntent has status '${stripeIntent.status}'. See https://stripe.com/docs/api/payment_intents/object#payment_intent_object-status. """.trimIndent() ) @@ -44,7 +45,7 @@ internal class StripeIntentValidator { error( """ A SetupIntent with status='requires_payment_method' or 'requires_action` is required. - The current SetupIntent has status ${stripeIntent.status}. + The current SetupIntent has status '${stripeIntent.status}'. See https://stripe.com/docs/api/setup_intents/object#setup_intent_object-status """.trimIndent() ) diff --git a/payments-core/src/test/java/com/stripe/android/paymentsheet/PaymentSheetActivityTest.kt b/payments-core/src/test/java/com/stripe/android/paymentsheet/PaymentSheetActivityTest.kt index e317b6664b1..ca6ca9460d8 100644 --- a/payments-core/src/test/java/com/stripe/android/paymentsheet/PaymentSheetActivityTest.kt +++ b/payments-core/src/test/java/com/stripe/android/paymentsheet/PaymentSheetActivityTest.kt @@ -37,6 +37,7 @@ import com.stripe.android.paymentsheet.model.FragmentConfigFixtures import com.stripe.android.paymentsheet.model.PaymentIntentClientSecret import com.stripe.android.paymentsheet.model.PaymentSelection import com.stripe.android.paymentsheet.model.PaymentSheetViewState +import com.stripe.android.paymentsheet.model.StripeIntentValidator import com.stripe.android.paymentsheet.repositories.StripeIntentRepository import com.stripe.android.paymentsheet.ui.PrimaryButtonAnimator import com.stripe.android.paymentsheet.viewmodels.BaseSheetViewModel @@ -658,8 +659,8 @@ internal class PaymentSheetActivityTest { scenario.getResult().resultCode, scenario.getResult().resultData ) - ).isEqualTo( - PaymentSheetResult.Completed + ).isInstanceOf( + PaymentSheetResult.Failed::class.java ) } @@ -739,17 +740,18 @@ internal class PaymentSheetActivityTest { eventReporter, { PaymentConfiguration(ApiKeyFixtures.FAKE_PUBLISHABLE_KEY) }, StripeIntentRepository.Static(paymentIntent), + StripeIntentValidator(), FakeCustomerRepository(paymentMethods), { paymentFlowResultProcessor }, FakePrefsRepository(), - Logger.noop(), - testDispatcher, StripePaymentController( ApplicationProvider.getApplicationContext(), { ApiKeyFixtures.FAKE_PUBLISHABLE_KEY }, mock() ), - googlePayPaymentMethodLauncherFactory + googlePayPaymentMethodLauncherFactory, + Logger.noop(), + testDispatcher ) } diff --git a/payments-core/src/test/java/com/stripe/android/paymentsheet/PaymentSheetViewModelTest.kt b/payments-core/src/test/java/com/stripe/android/paymentsheet/PaymentSheetViewModelTest.kt index 4627bafb0d9..f597de86bcb 100644 --- a/payments-core/src/test/java/com/stripe/android/paymentsheet/PaymentSheetViewModelTest.kt +++ b/payments-core/src/test/java/com/stripe/android/paymentsheet/PaymentSheetViewModelTest.kt @@ -32,6 +32,7 @@ import com.stripe.android.paymentsheet.model.FragmentConfig import com.stripe.android.paymentsheet.model.PaymentSelection import com.stripe.android.paymentsheet.model.PaymentSheetViewState import com.stripe.android.paymentsheet.model.SavedSelection +import com.stripe.android.paymentsheet.model.StripeIntentValidator import com.stripe.android.paymentsheet.repositories.CustomerApiRepository import com.stripe.android.paymentsheet.repositories.CustomerRepository import com.stripe.android.paymentsheet.repositories.StripeIntentRepository @@ -612,7 +613,7 @@ internal class PaymentSheetViewModelTest { assertThat((result as? PaymentSheetResult.Failed)?.error?.message) .isEqualTo( "PaymentIntent with confirmation_method='automatic' is required.\n" + - "The current PaymentIntent has confirmation_method Manual.\n" + + "The current PaymentIntent has confirmation_method 'Manual'.\n" + "See https://stripe.com/docs/api/payment_intents/object#payment_intent_object-confirmation_method." ) } @@ -621,7 +622,7 @@ internal class PaymentSheetViewModelTest { fun `fetchPaymentIntent() should fail if status != requires_payment_method`() { val viewModel = createViewModel( stripeIntentRepository = StripeIntentRepository.Static( - PaymentIntentFixtures.PI_REQUIRES_MASTERCARD_3DS2 + PaymentIntentFixtures.PI_SUCCEEDED ) ) var result: PaymentSheetResult? = null @@ -631,9 +632,9 @@ internal class PaymentSheetViewModelTest { viewModel.fetchStripeIntent() assertThat((result as? PaymentSheetResult.Failed)?.error?.message) .isEqualTo( - "PaymentIntent with confirmation_method='automatic' is required.\n" + - "The current PaymentIntent has confirmation_method Manual.\n" + - "See https://stripe.com/docs/api/payment_intents/object#payment_intent_object-confirmation_method." + "A PaymentIntent with status='requires_payment_method' or 'requires_action` is required.\n" + + "The current PaymentIntent has status 'succeeded'.\n" + + "See https://stripe.com/docs/api/payment_intents/object#payment_intent_object-status." ) } @@ -750,39 +751,6 @@ internal class PaymentSheetViewModelTest { .isFalse() } - @Test - fun `viewState should emit FinishProcessing and ProcessResult if PaymentIntent is confirmed`() { - val viewModel = createViewModel( - stripeIntentRepository = StripeIntentRepository.Static( - PaymentIntentFixtures.PI_SUCCEEDED - ) - ) - - val viewStates = mutableListOf() - viewModel.viewState.observeForever { viewState -> - if (viewState is PaymentSheetViewState.FinishProcessing) { - // force `onComplete` to be called - viewState.onComplete() - } - viewState?.let { - viewStates.add(it) - } - } - - var paymentSheetResult: PaymentSheetResult? = null - viewModel.paymentSheetResult.observeForever { - paymentSheetResult = it - } - - viewModel.fetchStripeIntent() - - assertThat(viewStates) - .hasSize(1) - assertThat(viewStates[0]) - .isInstanceOf(PaymentSheetViewState.FinishProcessing::class.java) - assertThat(paymentSheetResult).isEqualTo(PaymentSheetResult.Completed) - } - @Test fun `When configuration is empty, merchant name should reflect the app name`() { val viewModel = createViewModel( @@ -809,13 +777,14 @@ internal class PaymentSheetViewModelTest { eventReporter, { PaymentConfiguration(ApiKeyFixtures.FAKE_PUBLISHABLE_KEY) }, stripeIntentRepository, + StripeIntentValidator(), customerRepository, { paymentFlowResultProcessor }, prefsRepository, - Logger.noop(), - testDispatcher, mock(), - mock() + mock(), + Logger.noop(), + testDispatcher ) } diff --git a/payments-core/src/test/java/com/stripe/android/paymentsheet/flowcontroller/DefaultFlowControllerInitializerTest.kt b/payments-core/src/test/java/com/stripe/android/paymentsheet/flowcontroller/DefaultFlowControllerInitializerTest.kt index a500e06fe10..8bdf08cfbd5 100644 --- a/payments-core/src/test/java/com/stripe/android/paymentsheet/flowcontroller/DefaultFlowControllerInitializerTest.kt +++ b/payments-core/src/test/java/com/stripe/android/paymentsheet/flowcontroller/DefaultFlowControllerInitializerTest.kt @@ -1,6 +1,7 @@ package com.stripe.android.paymentsheet.flowcontroller import com.google.common.truth.Truth.assertThat +import com.stripe.android.Logger 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.paymentsheet.FakePrefsRepository import com.stripe.android.paymentsheet.PaymentSheetFixtures import com.stripe.android.paymentsheet.model.PaymentSelection import com.stripe.android.paymentsheet.model.SavedSelection +import com.stripe.android.paymentsheet.model.StripeIntentValidator import com.stripe.android.paymentsheet.repositories.CustomerRepository import com.stripe.android.paymentsheet.repositories.StripeIntentRepository import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -368,7 +370,9 @@ internal class DefaultFlowControllerInitializerTest { { prefsRepository }, { if (isGooglePayReady) readyGooglePayRepository else unreadyGooglePayRepository }, stripeIntentRepo, + StripeIntentValidator(), customerRepo, + Logger.noop(), testDispatcher ) }