From 4db01fa394721cab9b7b3d209e751eb0993f90ff Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 26 May 2021 20:35:13 -0700 Subject: [PATCH 1/2] Bump truth from 1.1.2 to 1.1.3 (#3763) --- example/build.gradle | 2 +- payments-core/build.gradle | 2 +- paymentsheet/build.gradle | 2 +- stripe-test-e2e/build.gradle | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/example/build.gradle b/example/build.gradle index fd9c147bf68..b1e8e810c50 100644 --- a/example/build.gradle +++ b/example/build.gradle @@ -93,7 +93,7 @@ dependencies { androidTestImplementation 'androidx.test.ext:junit:1.1.2' androidTestImplementation 'androidx.test.ext:junit-ktx:1.1.2' androidTestImplementation "androidx.test.ext:truth:$androidTestVersion" - androidTestImplementation 'com.google.truth:truth:1.1.2' + androidTestImplementation 'com.google.truth:truth:1.1.3' // Espresso dependencies androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVersion" diff --git a/payments-core/build.gradle b/payments-core/build.gradle index b47dd0acd5a..3b7b6c9d63c 100644 --- a/payments-core/build.gradle +++ b/payments-core/build.gradle @@ -50,7 +50,7 @@ dependencies { testImplementation "org.jetbrains.kotlin:kotlin-test-junit:$kotlinVersion" testImplementation "org.jetbrains.kotlin:kotlin-test-annotations-common:$kotlinVersion" testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$kotlinCoroutinesVersion" - testImplementation 'com.google.truth:truth:1.1.2' + testImplementation 'com.google.truth:truth:1.1.3' testImplementation "androidx.arch.core:core-testing:2.1.0" testImplementation "androidx.fragment:fragment-testing:$fragmentVersion" diff --git a/paymentsheet/build.gradle b/paymentsheet/build.gradle index c6ab4543554..971e4015731 100644 --- a/paymentsheet/build.gradle +++ b/paymentsheet/build.gradle @@ -79,7 +79,7 @@ dependencies { testImplementation "org.jetbrains.kotlin:kotlin-test-junit:$kotlinVersion" testImplementation "org.jetbrains.kotlin:kotlin-test-annotations-common:$kotlinVersion" testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$kotlinCoroutinesVersion" - testImplementation 'com.google.truth:truth:1.1.2' + testImplementation 'com.google.truth:truth:1.1.3' testImplementation "androidx.arch.core:core-testing:2.1.0" testImplementation "androidx.fragment:fragment-testing:$fragmentVersion" diff --git a/stripe-test-e2e/build.gradle b/stripe-test-e2e/build.gradle index 0f0ee10d70d..e3c25c2966f 100644 --- a/stripe-test-e2e/build.gradle +++ b/stripe-test-e2e/build.gradle @@ -60,7 +60,7 @@ dependencies { testImplementation 'org.robolectric:robolectric:4.5.1' testImplementation 'junit:junit:4.13.2' - testImplementation 'com.google.truth:truth:1.1.2' + testImplementation 'com.google.truth:truth:1.1.3' testImplementation "androidx.arch.core:core-testing:2.1.0" testImplementation "androidx.test:core:$androidTestVersion" testImplementation 'androidx.test.ext:junit-ktx:1.1.2' From 001166fbfe0a776274011c58b4541f6b428816e8 Mon Sep 17 00:00:00 2001 From: "Bruno R. Nunes" <77990083+brnunes-stripe@users.noreply.github.com> Date: Wed, 26 May 2021 21:23:45 -0700 Subject: [PATCH 2/2] Break up DefaultPaymentFlowResultProcessor into PaymentIntent and SetupIntent implementations (#3748) --- .../stripe/android/StripePaymentController.kt | 16 +- .../DefaultPaymentFlowResultProcessor.kt | 144 ------------- .../android/payments/PaymentFlowResult.kt | 2 +- .../payments/PaymentFlowResultProcessor.kt | 195 +++++++++++++++++- .../paymentsheet/PaymentSheetViewModel.kt | 11 +- .../flowcontroller/DefaultFlowController.kt | 10 +- .../DefaultFlowControllerInitializer.kt | 2 + .../flowcontroller/FlowControllerFactory.kt | 4 +- .../paymentsheet/flowcontroller/InitData.kt | 2 + .../DefaultPaymentFlowResultProcessorTest.kt | 102 --------- .../FakePaymentFlowResultProcessor.kt | 37 ---- .../PaymentIntentFlowResultProcessorTest.kt | 70 +++++++ .../SetupIntentFlowResultProcessorTest.kt | 70 +++++++ .../paymentsheet/PaymentSheetActivityTest.kt | 14 +- .../paymentsheet/PaymentSheetViewModelTest.kt | 94 +++++---- .../DefaultFlowControllerInitializerTest.kt | 7 + .../DefaultFlowControllerTest.kt | 111 +++++----- .../LaunchPaymentSheetCompleteActivity.kt | 3 +- 18 files changed, 490 insertions(+), 404 deletions(-) delete mode 100644 payments-core/src/main/java/com/stripe/android/payments/DefaultPaymentFlowResultProcessor.kt delete mode 100644 payments-core/src/test/java/com/stripe/android/payments/DefaultPaymentFlowResultProcessorTest.kt delete mode 100644 payments-core/src/test/java/com/stripe/android/payments/FakePaymentFlowResultProcessor.kt create mode 100644 payments-core/src/test/java/com/stripe/android/payments/PaymentIntentFlowResultProcessorTest.kt create mode 100644 payments-core/src/test/java/com/stripe/android/payments/SetupIntentFlowResultProcessorTest.kt diff --git a/payments-core/src/main/java/com/stripe/android/StripePaymentController.kt b/payments-core/src/main/java/com/stripe/android/StripePaymentController.kt index bb7f145447f..3a8e1edb773 100644 --- a/payments-core/src/main/java/com/stripe/android/StripePaymentController.kt +++ b/payments-core/src/main/java/com/stripe/android/StripePaymentController.kt @@ -30,11 +30,12 @@ import com.stripe.android.networking.DefaultAlipayRepository import com.stripe.android.networking.StripeRepository import com.stripe.android.payments.BrowserCapabilities import com.stripe.android.payments.BrowserCapabilitiesSupplier -import com.stripe.android.payments.DefaultPaymentFlowResultProcessor import com.stripe.android.payments.DefaultReturnUrl import com.stripe.android.payments.DefaultStripeChallengeStatusReceiver import com.stripe.android.payments.PaymentFlowFailureMessageFactory import com.stripe.android.payments.PaymentFlowResult +import com.stripe.android.payments.PaymentIntentFlowResultProcessor +import com.stripe.android.payments.SetupIntentFlowResultProcessor import com.stripe.android.payments.Stripe3ds2CompletionStarter import com.stripe.android.stripe3ds2.init.ui.StripeUiCustomization import com.stripe.android.stripe3ds2.service.StripeThreeDs2Service @@ -85,7 +86,14 @@ internal class StripePaymentController internal constructor( private val uiContext: CoroutineContext = Dispatchers.Main ) : PaymentController { private val failureMessageFactory = PaymentFlowFailureMessageFactory(context) - private val paymentFlowResultProcessor = DefaultPaymentFlowResultProcessor( + private val paymentIntentFlowResultProcessor = PaymentIntentFlowResultProcessor( + context, + publishableKey, + stripeRepository, + enableLogging, + workContext + ) + private val setupIntentFlowResultProcessor = SetupIntentFlowResultProcessor( context, publishableKey, stripeRepository, @@ -404,7 +412,7 @@ internal class StripePaymentController internal constructor( IllegalArgumentException::class ) override suspend fun getPaymentIntentResult(data: Intent) = - paymentFlowResultProcessor.processPaymentIntent( + paymentIntentFlowResultProcessor.processResult( PaymentFlowResult.Unvalidated.fromIntent(data) ) @@ -429,7 +437,7 @@ internal class StripePaymentController internal constructor( IllegalArgumentException::class ) override suspend fun getSetupIntentResult(data: Intent) = - paymentFlowResultProcessor.processSetupIntent( + setupIntentFlowResultProcessor.processResult( PaymentFlowResult.Unvalidated.fromIntent(data) ) diff --git a/payments-core/src/main/java/com/stripe/android/payments/DefaultPaymentFlowResultProcessor.kt b/payments-core/src/main/java/com/stripe/android/payments/DefaultPaymentFlowResultProcessor.kt deleted file mode 100644 index 660527bc458..00000000000 --- a/payments-core/src/main/java/com/stripe/android/payments/DefaultPaymentFlowResultProcessor.kt +++ /dev/null @@ -1,144 +0,0 @@ -package com.stripe.android.payments - -import android.content.Context -import com.stripe.android.Logger -import com.stripe.android.PaymentIntentResult -import com.stripe.android.SetupIntentResult -import com.stripe.android.model.PaymentIntent -import com.stripe.android.model.SetupIntent -import com.stripe.android.model.StripeIntent -import com.stripe.android.networking.ApiRequest -import com.stripe.android.networking.StripeRepository -import kotlinx.coroutines.withContext -import kotlin.coroutines.CoroutineContext - -internal class DefaultPaymentFlowResultProcessor( - context: Context, - private val publishableKey: String, - private val stripeRepository: StripeRepository, - enableLogging: Boolean, - private val workContext: CoroutineContext -) : PaymentFlowResultProcessor { - private val logger = Logger.getInstance(enableLogging) - private val failureMessageFactory = PaymentFlowFailureMessageFactory(context) - - override suspend fun processPaymentIntent( - unvalidatedResult: PaymentFlowResult.Unvalidated - ): PaymentIntentResult = withContext(workContext) { - val result = unvalidatedResult.validate() - - val requestOptions = ApiRequest.Options( - apiKey = publishableKey, - stripeAccount = result.stripeAccountId - ) - - requireNotNull( - stripeRepository.retrievePaymentIntent( - result.clientSecret, - requestOptions, - expandFields = EXPAND_PAYMENT_METHOD - ) - ).let { paymentIntent -> - if (shouldCancelIntent(paymentIntent, result.canCancelSource)) { - cancelPaymentIntent( - paymentIntent, - requestOptions, - result.sourceId.orEmpty(), - ) - } else { - paymentIntent - } - }.let { paymentIntent -> - PaymentIntentResult( - paymentIntent, - result.flowOutcome, - failureMessageFactory.create(paymentIntent, result.flowOutcome) - ) - } - } - - override suspend fun processSetupIntent( - unvalidatedResult: PaymentFlowResult.Unvalidated - ): SetupIntentResult = withContext(workContext) { - val result = unvalidatedResult.validate() - - val requestOptions = ApiRequest.Options( - apiKey = publishableKey, - stripeAccount = result.stripeAccountId - ) - - requireNotNull( - stripeRepository.retrieveSetupIntent( - result.clientSecret, - requestOptions, - expandFields = EXPAND_PAYMENT_METHOD - ) - ).let { setupIntent -> - if (shouldCancelIntent(setupIntent, result.canCancelSource)) { - cancelSetupIntent( - setupIntent, - requestOptions, - result.sourceId.orEmpty(), - ) - } else { - setupIntent - } - }.let { setupIntent -> - SetupIntentResult( - setupIntent, - result.flowOutcome, - failureMessageFactory.create(setupIntent, result.flowOutcome) - ) - } - } - - /** - * It is very important to check `requiresAction()` because we can't always tell what - * action the customer took during payment authentication (e.g. when using Custom Tabs). - * - * We don't want to cancel if required actions have been resolved and the payment is ready - * for capture. - */ - private fun shouldCancelIntent( - stripeIntent: StripeIntent, - shouldCancelSource: Boolean - ): Boolean { - return shouldCancelSource && stripeIntent.requiresAction() - } - - private suspend fun cancelPaymentIntent( - paymentIntent: PaymentIntent, - requestOptions: ApiRequest.Options, - sourceId: String - ): PaymentIntent { - logger.debug("Canceling source '$sourceId' for PaymentIntent") - - return requireNotNull( - stripeRepository.cancelPaymentIntentSource( - paymentIntent.id.orEmpty(), - sourceId, - requestOptions, - ) - ) - } - - private suspend fun cancelSetupIntent( - setupIntent: SetupIntent, - requestOptions: ApiRequest.Options, - sourceId: String - ): SetupIntent { - logger.debug("Canceling source '$sourceId' for SetupIntent") - - return requireNotNull( - stripeRepository.cancelSetupIntentSource( - setupIntent.id.orEmpty(), - sourceId, - requestOptions, - ) - ) - } - - private companion object { - private val EXPAND_PAYMENT_METHOD = listOf("payment_method") - } -} diff --git a/payments-core/src/main/java/com/stripe/android/payments/PaymentFlowResult.kt b/payments-core/src/main/java/com/stripe/android/payments/PaymentFlowResult.kt index f7156e39e29..ee521ac1d70 100644 --- a/payments-core/src/main/java/com/stripe/android/payments/PaymentFlowResult.kt +++ b/payments-core/src/main/java/com/stripe/android/payments/PaymentFlowResult.kt @@ -93,7 +93,7 @@ sealed class PaymentFlowResult { /** * The Source is eligible for cancellation. - * See [DefaultPaymentFlowResultProcessor.shouldCancelSource] for usage. + * See [PaymentFlowResultProcessor.shouldCancelIntent] for usage. */ internal val canCancelSource: Boolean = false, diff --git a/payments-core/src/main/java/com/stripe/android/payments/PaymentFlowResultProcessor.kt b/payments-core/src/main/java/com/stripe/android/payments/PaymentFlowResultProcessor.kt index 875c3c06587..7f069a37206 100644 --- a/payments-core/src/main/java/com/stripe/android/payments/PaymentFlowResultProcessor.kt +++ b/payments-core/src/main/java/com/stripe/android/payments/PaymentFlowResultProcessor.kt @@ -1,14 +1,197 @@ package com.stripe.android.payments +import android.content.Context +import com.stripe.android.Logger +import com.stripe.android.PaymentController import com.stripe.android.PaymentIntentResult import com.stripe.android.SetupIntentResult +import com.stripe.android.StripeIntentResult +import com.stripe.android.model.PaymentIntent +import com.stripe.android.model.SetupIntent +import com.stripe.android.model.StripeIntent +import com.stripe.android.networking.ApiRequest +import com.stripe.android.networking.StripeRepository +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import kotlin.coroutines.CoroutineContext -internal interface PaymentFlowResultProcessor { - suspend fun processPaymentIntent( - unvalidatedResult: PaymentFlowResult.Unvalidated - ): PaymentIntentResult +/** + * Class responsible for processing the result of a [PaymentController] confirm operation. + */ +internal sealed class PaymentFlowResultProcessor>( + context: Context, + private val publishableKey: String, + protected val stripeRepository: StripeRepository, + enableLogging: Boolean, + private val workContext: CoroutineContext = Dispatchers.IO +) { + private val logger = Logger.getInstance(enableLogging) + private val failureMessageFactory = PaymentFlowFailureMessageFactory(context) - suspend fun processSetupIntent( + suspend fun processResult( unvalidatedResult: PaymentFlowResult.Unvalidated - ): SetupIntentResult + ): S = withContext(workContext) { + val result = unvalidatedResult.validate() + + val requestOptions = ApiRequest.Options( + apiKey = publishableKey, + stripeAccount = result.stripeAccountId + ) + + requireNotNull( + retrieveStripeIntent( + result.clientSecret, + requestOptions, + expandFields = EXPAND_PAYMENT_METHOD + ) + ).let { stripeIntent -> + if (shouldCancelIntent(stripeIntent, result.canCancelSource)) { + val sourceId = result.sourceId.orEmpty() + logger.debug( + "Canceling source '$sourceId' for '${stripeIntent.javaClass.simpleName}'" + ) + + requireNotNull( + cancelStripeIntent( + stripeIntent, + requestOptions, + sourceId, + ) + ) + } else { + stripeIntent + } + }.let { stripeIntent -> + createStripeIntentResult( + stripeIntent, + result.flowOutcome, + failureMessageFactory.create(stripeIntent, result.flowOutcome) + ) + } + } + + private fun shouldCancelIntent( + stripeIntent: StripeIntent, + shouldCancelSource: Boolean + ): Boolean { + // It is very important to check `requiresAction()` because we can't always tell what + // action the customer took during payment authentication (e.g. when using Custom Tabs). + // We don't want to cancel if required actions have been resolved and the payment is ready + // for capture. + return shouldCancelSource && stripeIntent.requiresAction() + } + + protected abstract suspend fun retrieveStripeIntent( + clientSecret: String, + requestOptions: ApiRequest.Options, + expandFields: List + ): T? + + protected abstract suspend fun cancelStripeIntent( + stripeIntent: T, + requestOptions: ApiRequest.Options, + sourceId: String + ): T? + + protected abstract fun createStripeIntentResult( + stripeIntent: T, + @StripeIntentResult.Outcome outcomeFromFlow: Int, + failureMessage: String? + ): S + + private companion object { + val EXPAND_PAYMENT_METHOD = listOf("payment_method") + } +} + +/** + * Processes the result of a [PaymentIntent] confirmation. + */ +internal class PaymentIntentFlowResultProcessor( + context: Context, + publishableKey: String, + stripeRepository: StripeRepository, + enableLogging: Boolean, + workContext: CoroutineContext = Dispatchers.IO +) : PaymentFlowResultProcessor( + context, publishableKey, stripeRepository, enableLogging, workContext +) { + override suspend fun retrieveStripeIntent( + clientSecret: String, + requestOptions: ApiRequest.Options, + expandFields: List + ): PaymentIntent? = + stripeRepository.retrievePaymentIntent( + clientSecret, + requestOptions, + expandFields + ) + + override suspend fun cancelStripeIntent( + stripeIntent: PaymentIntent, + requestOptions: ApiRequest.Options, + sourceId: String + ): PaymentIntent? = + stripeRepository.cancelPaymentIntentSource( + stripeIntent.id.orEmpty(), + sourceId, + requestOptions, + ) + + override fun createStripeIntentResult( + stripeIntent: PaymentIntent, + outcomeFromFlow: Int, + failureMessage: String? + ): PaymentIntentResult = + PaymentIntentResult( + stripeIntent, + outcomeFromFlow, + failureMessage + ) +} + +/** + * Processes the result of a [SetupIntent] confirmation. + */ +internal class SetupIntentFlowResultProcessor( + context: Context, + publishableKey: String, + stripeRepository: StripeRepository, + enableLogging: Boolean, + workContext: CoroutineContext = Dispatchers.IO +) : PaymentFlowResultProcessor( + context, publishableKey, stripeRepository, enableLogging, workContext +) { + override suspend fun retrieveStripeIntent( + clientSecret: String, + requestOptions: ApiRequest.Options, + expandFields: List + ): SetupIntent? = + stripeRepository.retrieveSetupIntent( + clientSecret, + requestOptions, + expandFields + ) + + override suspend fun cancelStripeIntent( + stripeIntent: SetupIntent, + requestOptions: ApiRequest.Options, + sourceId: String + ): SetupIntent? = + stripeRepository.cancelSetupIntentSource( + stripeIntent.id.orEmpty(), + sourceId, + requestOptions, + ) + + override fun createStripeIntentResult( + stripeIntent: SetupIntent, + outcomeFromFlow: Int, + failureMessage: String? + ): SetupIntentResult = + SetupIntentResult( + stripeIntent, + outcomeFromFlow, + failureMessage + ) } 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 e2fdb9e002e..bd18ce20e4b 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 @@ -24,9 +24,8 @@ import com.stripe.android.model.PaymentIntent import com.stripe.android.model.PaymentMethod import com.stripe.android.networking.ApiRequest import com.stripe.android.networking.StripeApiRepository -import com.stripe.android.payments.DefaultPaymentFlowResultProcessor import com.stripe.android.payments.PaymentFlowResult -import com.stripe.android.payments.PaymentFlowResultProcessor +import com.stripe.android.payments.PaymentIntentFlowResultProcessor import com.stripe.android.paymentsheet.analytics.DefaultEventReporter import com.stripe.android.paymentsheet.analytics.EventReporter import com.stripe.android.paymentsheet.model.FragmentConfig @@ -62,7 +61,7 @@ internal fun PaymentSheetViewState.convert(): PrimaryButton.State { internal class PaymentSheetViewModel internal constructor( private val stripeIntentRepository: StripeIntentRepository, private val paymentMethodsRepository: PaymentMethodsRepository, - private val paymentFlowResultProcessor: PaymentFlowResultProcessor, + private val paymentFlowResultProcessor: PaymentIntentFlowResultProcessor, private val googlePayRepository: GooglePayRepository, prefsRepository: PrefsRepository, private val eventReporter: EventReporter, @@ -353,7 +352,9 @@ internal class PaymentSheetViewModel internal constructor( viewModelScope.launch { val result = runCatching { withContext(workContext) { - paymentFlowResultProcessor.processPaymentIntent(paymentFlowResult) + paymentFlowResultProcessor.processResult( + paymentFlowResult + ) } } @@ -449,7 +450,7 @@ internal class PaymentSheetViewModel internal constructor( return PaymentSheetViewModel( stripeIntentRepository, paymentMethodsRepository, - DefaultPaymentFlowResultProcessor( + PaymentIntentFlowResultProcessor( application, publishableKey, stripeRepository, diff --git a/payments-core/src/main/java/com/stripe/android/paymentsheet/flowcontroller/DefaultFlowController.kt b/payments-core/src/main/java/com/stripe/android/paymentsheet/flowcontroller/DefaultFlowController.kt index 0fff3aae82e..61d4a11428a 100644 --- a/payments-core/src/main/java/com/stripe/android/paymentsheet/flowcontroller/DefaultFlowController.kt +++ b/payments-core/src/main/java/com/stripe/android/paymentsheet/flowcontroller/DefaultFlowController.kt @@ -19,7 +19,7 @@ import com.stripe.android.networking.ApiRequest import com.stripe.android.networking.StripeApiRepository import com.stripe.android.payments.DefaultReturnUrl import com.stripe.android.payments.PaymentFlowResult -import com.stripe.android.payments.PaymentFlowResultProcessor +import com.stripe.android.payments.PaymentIntentFlowResultProcessor import com.stripe.android.payments.Stripe3ds2CompletionContract import com.stripe.android.paymentsheet.PaymentOptionCallback import com.stripe.android.paymentsheet.PaymentOptionContract @@ -56,7 +56,7 @@ internal class DefaultFlowController internal constructor( private val paymentOptionFactory: PaymentOptionFactory, private val flowControllerInitializer: FlowControllerInitializer, paymentControllerFactory: PaymentControllerFactory, - paymentFlowResultProcessorFactory: (String, StripeApiRepository) -> PaymentFlowResultProcessor, + paymentFlowResultProcessorFactory: (String, StripeApiRepository) -> PaymentIntentFlowResultProcessor, private val eventReporter: EventReporter, private val sessionId: SessionId, defaultReturnUrl: DefaultReturnUrl, @@ -120,7 +120,7 @@ internal class DefaultFlowController internal constructor( ) } - private val paymentFlowResultProcessor: PaymentFlowResultProcessor by lazy { + private val paymentFlowResultProcessor by lazy { paymentFlowResultProcessorFactory(paymentConfiguration.publishableKey, stripeApiRepository) } @@ -410,7 +410,9 @@ internal class DefaultFlowController internal constructor( ) { lifecycleScope.launch { runCatching { - paymentFlowResultProcessor.processPaymentIntent(paymentFlowResult) + paymentFlowResultProcessor.processResult( + paymentFlowResult + ) }.fold( onSuccess = { withContext(Dispatchers.Main) { 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 7c5c6409727..424dcd4f456 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 @@ -77,6 +77,7 @@ internal class DefaultFlowControllerInitializer( FlowControllerInitializer.InitResult.Success( InitData( config = config, + clientSecret = clientSecret, paymentIntent = paymentIntent, paymentMethodTypes = paymentMethodTypes, paymentMethods = paymentMethods, @@ -115,6 +116,7 @@ internal class DefaultFlowControllerInitializer( FlowControllerInitializer.InitResult.Success( InitData( config = config, + clientSecret = clientSecret, paymentIntent = paymentIntent, paymentMethodTypes = paymentMethodTypes, paymentMethods = emptyList(), diff --git a/payments-core/src/main/java/com/stripe/android/paymentsheet/flowcontroller/FlowControllerFactory.kt b/payments-core/src/main/java/com/stripe/android/paymentsheet/flowcontroller/FlowControllerFactory.kt index f3fe2b6608d..59e5c29abad 100644 --- a/payments-core/src/main/java/com/stripe/android/paymentsheet/flowcontroller/FlowControllerFactory.kt +++ b/payments-core/src/main/java/com/stripe/android/paymentsheet/flowcontroller/FlowControllerFactory.kt @@ -6,8 +6,8 @@ import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelStoreOwner import androidx.lifecycle.lifecycleScope import com.stripe.android.StripePaymentController -import com.stripe.android.payments.DefaultPaymentFlowResultProcessor import com.stripe.android.payments.DefaultReturnUrl +import com.stripe.android.payments.PaymentIntentFlowResultProcessor import com.stripe.android.paymentsheet.DefaultGooglePayRepository import com.stripe.android.paymentsheet.DefaultPrefsRepository import com.stripe.android.paymentsheet.GooglePayRepository @@ -119,7 +119,7 @@ internal class FlowControllerFactory( ), paymentControllerFactory = paymentControllerFactory, paymentFlowResultProcessorFactory = { publishableKey, stripeApiRepository -> - DefaultPaymentFlowResultProcessor( + PaymentIntentFlowResultProcessor( appContext, publishableKey, stripeApiRepository, diff --git a/payments-core/src/main/java/com/stripe/android/paymentsheet/flowcontroller/InitData.kt b/payments-core/src/main/java/com/stripe/android/paymentsheet/flowcontroller/InitData.kt index 7a017c885d1..8671f07de1d 100644 --- a/payments-core/src/main/java/com/stripe/android/paymentsheet/flowcontroller/InitData.kt +++ b/payments-core/src/main/java/com/stripe/android/paymentsheet/flowcontroller/InitData.kt @@ -4,12 +4,14 @@ import android.os.Parcelable import com.stripe.android.model.PaymentIntent import com.stripe.android.model.PaymentMethod import com.stripe.android.paymentsheet.PaymentSheet +import com.stripe.android.paymentsheet.model.ClientSecret import com.stripe.android.paymentsheet.model.SavedSelection import kotlinx.parcelize.Parcelize @Parcelize internal data class InitData( val config: PaymentSheet.Configuration?, + val clientSecret: ClientSecret, val paymentIntent: PaymentIntent, // the allowed payment method types val paymentMethodTypes: List, diff --git a/payments-core/src/test/java/com/stripe/android/payments/DefaultPaymentFlowResultProcessorTest.kt b/payments-core/src/test/java/com/stripe/android/payments/DefaultPaymentFlowResultProcessorTest.kt deleted file mode 100644 index be350e1f54c..00000000000 --- a/payments-core/src/test/java/com/stripe/android/payments/DefaultPaymentFlowResultProcessorTest.kt +++ /dev/null @@ -1,102 +0,0 @@ -package com.stripe.android.payments - -import androidx.test.core.app.ApplicationProvider -import com.google.common.truth.Truth.assertThat -import com.stripe.android.ApiKeyFixtures -import com.stripe.android.PaymentIntentResult -import com.stripe.android.SetupIntentResult -import com.stripe.android.StripeIntentResult -import com.stripe.android.model.PaymentIntentFixtures -import com.stripe.android.model.SetupIntentFixtures -import com.stripe.android.networking.AbsFakeStripeRepository -import com.stripe.android.networking.ApiRequest -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.TestCoroutineDispatcher -import kotlinx.coroutines.test.runBlockingTest -import org.junit.Test -import org.junit.runner.RunWith -import org.robolectric.RobolectricTestRunner -import kotlin.test.AfterTest - -@RunWith(RobolectricTestRunner::class) -@ExperimentalCoroutinesApi -internal class DefaultPaymentFlowResultProcessorTest { - private val testDispatcher = TestCoroutineDispatcher() - - private val processor = DefaultPaymentFlowResultProcessor( - ApplicationProvider.getApplicationContext(), - ApiKeyFixtures.FAKE_PUBLISHABLE_KEY, - FakeStripeRepository(), - false, - testDispatcher - ) - - @AfterTest - fun after() { - testDispatcher.cleanupTestCoroutines() - } - - @Test - fun `processPaymentIntent() when shouldCancelSource=true should return canceled PaymentIntent`() = testDispatcher.runBlockingTest { - val paymentIntentResult = processor.processPaymentIntent( - PaymentFlowResult.Unvalidated( - clientSecret = "client_secret", - flowOutcome = StripeIntentResult.Outcome.CANCELED, - canCancelSource = true - ) - ) - - assertThat(paymentIntentResult) - .isEqualTo( - PaymentIntentResult( - intent = PaymentIntentFixtures.CANCELLED, - outcomeFromFlow = StripeIntentResult.Outcome.CANCELED, - ) - ) - } - - @Test - fun `processSetupIntent() when shouldCancelSource=true should return canceled SetupIntent`() = testDispatcher.runBlockingTest { - val setupIntentResult = processor.processSetupIntent( - PaymentFlowResult.Unvalidated( - clientSecret = "client_secret", - flowOutcome = StripeIntentResult.Outcome.CANCELED, - canCancelSource = true - ) - ) - - assertThat(setupIntentResult) - .isEqualTo( - SetupIntentResult( - intent = SetupIntentFixtures.CANCELLED, - outcomeFromFlow = StripeIntentResult.Outcome.CANCELED, - ) - ) - } - - private class FakeStripeRepository : AbsFakeStripeRepository() { - override suspend fun retrieveSetupIntent( - clientSecret: String, - options: ApiRequest.Options, - expandFields: List - ) = SetupIntentFixtures.SI_NEXT_ACTION_REDIRECT - - override suspend fun retrievePaymentIntent( - clientSecret: String, - options: ApiRequest.Options, - expandFields: List - ) = PaymentIntentFixtures.PI_REQUIRES_REDIRECT - - override suspend fun cancelPaymentIntentSource( - paymentIntentId: String, - sourceId: String, - options: ApiRequest.Options - ) = PaymentIntentFixtures.CANCELLED - - override suspend fun cancelSetupIntentSource( - setupIntentId: String, - sourceId: String, - options: ApiRequest.Options - ) = SetupIntentFixtures.CANCELLED - } -} diff --git a/payments-core/src/test/java/com/stripe/android/payments/FakePaymentFlowResultProcessor.kt b/payments-core/src/test/java/com/stripe/android/payments/FakePaymentFlowResultProcessor.kt deleted file mode 100644 index b68378be87e..00000000000 --- a/payments-core/src/test/java/com/stripe/android/payments/FakePaymentFlowResultProcessor.kt +++ /dev/null @@ -1,37 +0,0 @@ -package com.stripe.android.payments - -import com.stripe.android.PaymentIntentResult -import com.stripe.android.SetupIntentResult -import com.stripe.android.StripeIntentResult -import com.stripe.android.model.PaymentIntentFixtures -import com.stripe.android.model.SetupIntentFixtures - -internal class FakePaymentFlowResultProcessor : PaymentFlowResultProcessor { - var error: Throwable? = null - - var paymentIntentResult = PaymentIntentResult( - PaymentIntentFixtures.PI_WITH_LAST_PAYMENT_ERROR, - StripeIntentResult.Outcome.FAILED - ) - - var setupIntentResult = SetupIntentResult( - SetupIntentFixtures.SI_WITH_LAST_PAYMENT_ERROR, - StripeIntentResult.Outcome.FAILED - ) - - override suspend fun processPaymentIntent( - unvalidatedResult: PaymentFlowResult.Unvalidated - ): PaymentIntentResult { - return error?.let { - throw it - } ?: paymentIntentResult - } - - override suspend fun processSetupIntent( - unvalidatedResult: PaymentFlowResult.Unvalidated - ): SetupIntentResult { - return error?.let { - throw it - } ?: setupIntentResult - } -} diff --git a/payments-core/src/test/java/com/stripe/android/payments/PaymentIntentFlowResultProcessorTest.kt b/payments-core/src/test/java/com/stripe/android/payments/PaymentIntentFlowResultProcessorTest.kt new file mode 100644 index 00000000000..ba8077a238f --- /dev/null +++ b/payments-core/src/test/java/com/stripe/android/payments/PaymentIntentFlowResultProcessorTest.kt @@ -0,0 +1,70 @@ +package com.stripe.android.payments + +import androidx.test.core.app.ApplicationProvider +import com.google.common.truth.Truth.assertThat +import com.stripe.android.ApiKeyFixtures +import com.stripe.android.PaymentIntentResult +import com.stripe.android.StripeIntentResult +import com.stripe.android.model.PaymentIntentFixtures +import com.stripe.android.networking.AbsFakeStripeRepository +import com.stripe.android.networking.ApiRequest +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestCoroutineDispatcher +import kotlinx.coroutines.test.runBlockingTest +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import kotlin.test.AfterTest + +@RunWith(RobolectricTestRunner::class) +@ExperimentalCoroutinesApi +internal class PaymentIntentFlowResultProcessorTest { + private val testDispatcher = TestCoroutineDispatcher() + + private val processor = PaymentIntentFlowResultProcessor( + ApplicationProvider.getApplicationContext(), + ApiKeyFixtures.FAKE_PUBLISHABLE_KEY, + FakeStripeRepository(), + false, + testDispatcher + ) + + @AfterTest + fun after() { + testDispatcher.cleanupTestCoroutines() + } + + @Test + fun `processPaymentIntent() when shouldCancelSource=true should return canceled PaymentIntent`() = + testDispatcher.runBlockingTest { + val paymentIntentResult = processor.processResult( + PaymentFlowResult.Unvalidated( + clientSecret = "client_secret", + flowOutcome = StripeIntentResult.Outcome.CANCELED, + canCancelSource = true + ) + ) + + assertThat(paymentIntentResult) + .isEqualTo( + PaymentIntentResult( + intent = PaymentIntentFixtures.CANCELLED, + outcomeFromFlow = StripeIntentResult.Outcome.CANCELED, + ) + ) + } + + private class FakeStripeRepository : AbsFakeStripeRepository() { + override suspend fun retrievePaymentIntent( + clientSecret: String, + options: ApiRequest.Options, + expandFields: List + ) = PaymentIntentFixtures.PI_REQUIRES_REDIRECT + + override suspend fun cancelPaymentIntentSource( + paymentIntentId: String, + sourceId: String, + options: ApiRequest.Options + ) = PaymentIntentFixtures.CANCELLED + } +} diff --git a/payments-core/src/test/java/com/stripe/android/payments/SetupIntentFlowResultProcessorTest.kt b/payments-core/src/test/java/com/stripe/android/payments/SetupIntentFlowResultProcessorTest.kt new file mode 100644 index 00000000000..00bbf25d73f --- /dev/null +++ b/payments-core/src/test/java/com/stripe/android/payments/SetupIntentFlowResultProcessorTest.kt @@ -0,0 +1,70 @@ +package com.stripe.android.payments + +import androidx.test.core.app.ApplicationProvider +import com.google.common.truth.Truth.assertThat +import com.stripe.android.ApiKeyFixtures +import com.stripe.android.SetupIntentResult +import com.stripe.android.StripeIntentResult +import com.stripe.android.model.SetupIntentFixtures +import com.stripe.android.networking.AbsFakeStripeRepository +import com.stripe.android.networking.ApiRequest +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestCoroutineDispatcher +import kotlinx.coroutines.test.runBlockingTest +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import kotlin.test.AfterTest + +@RunWith(RobolectricTestRunner::class) +@ExperimentalCoroutinesApi +internal class SetupIntentFlowResultProcessorTest { + private val testDispatcher = TestCoroutineDispatcher() + + private val processor = SetupIntentFlowResultProcessor( + ApplicationProvider.getApplicationContext(), + ApiKeyFixtures.FAKE_PUBLISHABLE_KEY, + FakeStripeRepository(), + false, + testDispatcher + ) + + @AfterTest + fun after() { + testDispatcher.cleanupTestCoroutines() + } + + @Test + fun `processResult() when shouldCancelSource=true should return canceled SetupIntent`() = + testDispatcher.runBlockingTest { + val setupIntentResult = processor.processResult( + PaymentFlowResult.Unvalidated( + clientSecret = "client_secret", + flowOutcome = StripeIntentResult.Outcome.CANCELED, + canCancelSource = true + ) + ) + + assertThat(setupIntentResult) + .isEqualTo( + SetupIntentResult( + intent = SetupIntentFixtures.CANCELLED, + outcomeFromFlow = StripeIntentResult.Outcome.CANCELED, + ) + ) + } + + private class FakeStripeRepository : AbsFakeStripeRepository() { + override suspend fun retrieveSetupIntent( + clientSecret: String, + options: ApiRequest.Options, + expandFields: List + ) = SetupIntentFixtures.SI_NEXT_ACTION_REDIRECT + + override suspend fun cancelSetupIntentSource( + setupIntentId: String, + sourceId: String, + options: ApiRequest.Options + ) = SetupIntentFixtures.CANCELLED + } +} 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 7aa2d2cee7c..89133402b3a 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 @@ -8,7 +8,9 @@ import androidx.lifecycle.Lifecycle import androidx.test.core.app.ApplicationProvider import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.common.truth.Truth.assertThat +import com.nhaarman.mockitokotlin2.any import com.nhaarman.mockitokotlin2.mock +import com.nhaarman.mockitokotlin2.whenever import com.stripe.android.ApiKeyFixtures import com.stripe.android.PaymentConfiguration import com.stripe.android.PaymentIntentResult @@ -25,8 +27,8 @@ import com.stripe.android.model.PaymentMethod import com.stripe.android.model.PaymentMethodCreateParams import com.stripe.android.model.PaymentMethodFixtures import com.stripe.android.payments.DefaultReturnUrl -import com.stripe.android.payments.FakePaymentFlowResultProcessor import com.stripe.android.payments.PaymentFlowResult +import com.stripe.android.payments.PaymentIntentFlowResultProcessor import com.stripe.android.paymentsheet.PaymentSheetViewModel.CheckoutIdentifier import com.stripe.android.paymentsheet.analytics.EventReporter import com.stripe.android.paymentsheet.analytics.SessionId @@ -42,6 +44,7 @@ import com.stripe.android.utils.TestUtils.viewModelFactoryFor import com.stripe.android.utils.injectableActivityScenario import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.TestCoroutineDispatcher import kotlinx.coroutines.test.resetMain import kotlinx.coroutines.test.setMain @@ -676,12 +679,11 @@ internal class PaymentSheetActivityTest { paymentIntent: PaymentIntent = PAYMENT_INTENT, paymentMethods: List = PAYMENT_METHODS, paymentIntentResult: PaymentIntentResult = PaymentIntentResult(paymentIntent) - ): PaymentSheetViewModel { - val paymentFlowResultProcessor = FakePaymentFlowResultProcessor().also { - it.paymentIntentResult = paymentIntentResult - } + ): PaymentSheetViewModel = runBlocking { + val paymentFlowResultProcessor = mock() + whenever(paymentFlowResultProcessor.processResult(any())).thenReturn(paymentIntentResult) - return PaymentSheetViewModel( + PaymentSheetViewModel( stripeIntentRepository = StripeIntentRepository.Static(paymentIntent), paymentMethodsRepository = FakePaymentMethodsRepository(paymentMethods), paymentFlowResultProcessor = paymentFlowResultProcessor, 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 fe6d344bfaf..942149bfd92 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 @@ -5,8 +5,10 @@ import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.test.core.app.ApplicationProvider import com.google.android.gms.common.api.Status import com.google.common.truth.Truth.assertThat +import com.nhaarman.mockitokotlin2.any import com.nhaarman.mockitokotlin2.mock import com.nhaarman.mockitokotlin2.verify +import com.nhaarman.mockitokotlin2.whenever import com.stripe.android.ApiKeyFixtures import com.stripe.android.PaymentIntentResult import com.stripe.android.StripeIntentResult @@ -22,8 +24,8 @@ import com.stripe.android.model.PaymentMethodCreateParamsFixtures import com.stripe.android.model.PaymentMethodFixtures import com.stripe.android.networking.AbsFakeStripeRepository import com.stripe.android.networking.ApiRequest -import com.stripe.android.payments.FakePaymentFlowResultProcessor import com.stripe.android.payments.PaymentFlowResult +import com.stripe.android.payments.PaymentIntentFlowResultProcessor import com.stripe.android.paymentsheet.PaymentSheetViewModel.CheckoutIdentifier import com.stripe.android.paymentsheet.analytics.EventReporter import com.stripe.android.paymentsheet.model.FragmentConfig @@ -60,7 +62,7 @@ internal class PaymentSheetViewModelTest { private val prefsRepository = FakePrefsRepository() private val eventReporter = mock() private val viewModel: PaymentSheetViewModel by lazy { createViewModel() } - private val paymentFlowResultProcessor = FakePaymentFlowResultProcessor() + private val paymentFlowResultProcessor = mock() private val application = ApplicationProvider.getApplicationContext() @AfterTest @@ -265,7 +267,9 @@ internal class PaymentSheetViewModelTest { @Test fun `onPaymentFlowResult() should update ViewState and save preferences`() = testDispatcher.runBlockingTest { - paymentFlowResultProcessor.paymentIntentResult = PAYMENT_INTENT_RESULT + whenever(paymentFlowResultProcessor.processResult(any())).thenReturn( + PAYMENT_INTENT_RESULT + ) val selection = PaymentSelection.Saved(PaymentMethodFixtures.CARD_PAYMENT_METHOD) viewModel.updateSelection(selection) @@ -307,7 +311,9 @@ internal class PaymentSheetViewModelTest { @Test fun `onPaymentFlowResult() should update ViewState and save new payment method`() = testDispatcher.runBlockingTest { - paymentFlowResultProcessor.paymentIntentResult = PAYMENT_INTENT_RESULT_WITH_PM + whenever(paymentFlowResultProcessor.processResult(any())).thenReturn( + PAYMENT_INTENT_RESULT_WITH_PM + ) val selection = PaymentSelection.New.Card( PaymentMethodCreateParamsFixtures.DEFAULT_CARD, @@ -357,53 +363,59 @@ internal class PaymentSheetViewModelTest { } @Test - fun `onPaymentFlowResult() with non-success outcome should report failure event`() { - paymentFlowResultProcessor.paymentIntentResult = PAYMENT_INTENT_RESULT.copy( - outcomeFromFlow = StripeIntentResult.Outcome.FAILED - ) + fun `onPaymentFlowResult() with non-success outcome should report failure event`() = + testDispatcher.runBlockingTest { + whenever(paymentFlowResultProcessor.processResult(any())).thenReturn( + PAYMENT_INTENT_RESULT.copy( + outcomeFromFlow = StripeIntentResult.Outcome.FAILED + ) + ) - val selection = PaymentSelection.Saved(PaymentMethodFixtures.CARD_PAYMENT_METHOD) - viewModel.updateSelection(selection) + val selection = PaymentSelection.Saved(PaymentMethodFixtures.CARD_PAYMENT_METHOD) + viewModel.updateSelection(selection) - var paymentIntent: PaymentIntent? = null - viewModel.paymentIntent.observeForever { - paymentIntent = it - } + var paymentIntent: PaymentIntent? = null + viewModel.paymentIntent.observeForever { + paymentIntent = it + } - viewModel.onPaymentFlowResult( - PaymentFlowResult.Unvalidated() - ) - verify(eventReporter) - .onPaymentFailure(selection) + viewModel.onPaymentFlowResult( + PaymentFlowResult.Unvalidated() + ) + verify(eventReporter) + .onPaymentFailure(selection) - assertThat(paymentIntent).isNull() - } + assertThat(paymentIntent).isNull() + } @Test - fun `onPaymentFlowResult() should update emit API errors`() { - paymentFlowResultProcessor.error = RuntimeException("Your card was declined.") - - viewModel.fetchStripeIntent() + fun `onPaymentFlowResult() should update emit API errors`() = + testDispatcher.runBlockingTest { + whenever(paymentFlowResultProcessor.processResult(any())).thenThrow( + RuntimeException("Your card was declined.") + ) - val viewStateList = mutableListOf() - viewModel.viewState.observeForever { - viewStateList.add(it) - } - viewModel.onPaymentFlowResult( - PaymentFlowResult.Unvalidated() - ) + viewModel.fetchStripeIntent() - assertThat(viewStateList[0]) - .isEqualTo( - PaymentSheetViewState.Reset(null) + val viewStateList = mutableListOf() + viewModel.viewState.observeForever { + viewStateList.add(it) + } + viewModel.onPaymentFlowResult( + PaymentFlowResult.Unvalidated() ) - assertThat(viewStateList[1]) - .isEqualTo( - PaymentSheetViewState.Reset( - UserErrorMessage("Your card was declined.") + + assertThat(viewStateList[0]) + .isEqualTo( + PaymentSheetViewState.Reset(null) ) - ) - } + assertThat(viewStateList[1]) + .isEqualTo( + PaymentSheetViewState.Reset( + UserErrorMessage("Your card was declined.") + ) + ) + } @Test fun `fetchPaymentIntent() should update ViewState LiveData`() { 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 470e65618f2..173db6a7ea5 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 @@ -50,6 +50,7 @@ internal class DefaultFlowControllerInitializerTest { FlowControllerInitializer.InitResult.Success( InitData( null, + PaymentSheetFixtures.PAYMENT_INTENT_CLIENT_SECRET, PaymentIntentFixtures.PI_REQUIRES_PAYMENT_METHOD, listOf(PaymentMethod.Type.Card), emptyList(), @@ -77,6 +78,7 @@ internal class DefaultFlowControllerInitializerTest { FlowControllerInitializer.InitResult.Success( InitData( PaymentSheetFixtures.CONFIG_CUSTOMER_WITH_GOOGLEPAY, + PaymentSheetFixtures.PAYMENT_INTENT_CLIENT_SECRET, PaymentIntentFixtures.PI_REQUIRES_PAYMENT_METHOD, listOf(PaymentMethod.Type.Card), PAYMENT_METHODS, @@ -106,6 +108,7 @@ internal class DefaultFlowControllerInitializerTest { FlowControllerInitializer.InitResult.Success( InitData( PaymentSheetFixtures.CONFIG_GOOGLEPAY, + PaymentSheetFixtures.PAYMENT_INTENT_CLIENT_SECRET, PaymentIntentFixtures.PI_REQUIRES_PAYMENT_METHOD, listOf(PaymentMethod.Type.Card), emptyList(), @@ -133,6 +136,7 @@ internal class DefaultFlowControllerInitializerTest { FlowControllerInitializer.InitResult.Success( InitData( PaymentSheetFixtures.CONFIG_GOOGLEPAY, + PaymentSheetFixtures.PAYMENT_INTENT_CLIENT_SECRET, PaymentIntentFixtures.PI_REQUIRES_PAYMENT_METHOD, listOf(PaymentMethod.Type.Card), emptyList(), @@ -160,6 +164,7 @@ internal class DefaultFlowControllerInitializerTest { FlowControllerInitializer.InitResult.Success( InitData( PaymentSheetFixtures.CONFIG_CUSTOMER_WITH_GOOGLEPAY, + PaymentSheetFixtures.PAYMENT_INTENT_CLIENT_SECRET, PaymentIntentFixtures.PI_REQUIRES_PAYMENT_METHOD, listOf(PaymentMethod.Type.Card), PAYMENT_METHODS, @@ -195,6 +200,7 @@ internal class DefaultFlowControllerInitializerTest { FlowControllerInitializer.InitResult.Success( InitData( PaymentSheetFixtures.CONFIG_CUSTOMER_WITH_GOOGLEPAY, + PaymentSheetFixtures.PAYMENT_INTENT_CLIENT_SECRET, PaymentIntentFixtures.PI_REQUIRES_PAYMENT_METHOD, listOf(PaymentMethod.Type.Card), emptyList(), @@ -224,6 +230,7 @@ internal class DefaultFlowControllerInitializerTest { FlowControllerInitializer.InitResult.Success( InitData( PaymentSheetFixtures.CONFIG_CUSTOMER_WITH_GOOGLEPAY, + PaymentSheetFixtures.PAYMENT_INTENT_CLIENT_SECRET, PaymentIntentFixtures.PI_REQUIRES_PAYMENT_METHOD, listOf(PaymentMethod.Type.Card), emptyList(), diff --git a/payments-core/src/test/java/com/stripe/android/paymentsheet/flowcontroller/DefaultFlowControllerTest.kt b/payments-core/src/test/java/com/stripe/android/paymentsheet/flowcontroller/DefaultFlowControllerTest.kt index 0f80e7365c9..29d00b58c0f 100644 --- a/payments-core/src/test/java/com/stripe/android/paymentsheet/flowcontroller/DefaultFlowControllerTest.kt +++ b/payments-core/src/test/java/com/stripe/android/paymentsheet/flowcontroller/DefaultFlowControllerTest.kt @@ -13,6 +13,7 @@ import com.nhaarman.mockitokotlin2.mock import com.nhaarman.mockitokotlin2.verify import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions import com.nhaarman.mockitokotlin2.verifyZeroInteractions +import com.nhaarman.mockitokotlin2.whenever import com.stripe.android.ApiKeyFixtures import com.stripe.android.PaymentConfiguration import com.stripe.android.PaymentController @@ -28,8 +29,8 @@ import com.stripe.android.model.PaymentMethod import com.stripe.android.model.PaymentMethodCreateParamsFixtures import com.stripe.android.model.PaymentMethodFixtures import com.stripe.android.payments.DefaultReturnUrl -import com.stripe.android.payments.FakePaymentFlowResultProcessor import com.stripe.android.payments.PaymentFlowResult +import com.stripe.android.payments.PaymentIntentFlowResultProcessor import com.stripe.android.paymentsheet.PaymentOptionCallback import com.stripe.android.paymentsheet.PaymentOptionContract import com.stripe.android.paymentsheet.PaymentOptionResult @@ -73,7 +74,7 @@ internal class DefaultFlowControllerTest { private val paymentController = mock() private val eventReporter = mock() - private val paymentFlowResultProcessor = FakePaymentFlowResultProcessor() + private val paymentFlowResultProcessor = mock() private val flowController: DefaultFlowController by lazy { createFlowController() } @@ -481,67 +482,76 @@ internal class DefaultFlowControllerTest { } @Test - fun `onPaymentFlowResult when succeeded should invoke callback with Completed`() { - paymentFlowResultProcessor.paymentIntentResult = PaymentIntentResult( - PaymentIntentFixtures.PI_WITH_SHIPPING, - StripeIntentResult.Outcome.SUCCEEDED - ) + fun `onPaymentFlowResult when succeeded should invoke callback with Completed`() = + testDispatcher.runBlockingTest { + whenever(paymentFlowResultProcessor.processResult(any())).thenReturn( + PaymentIntentResult( + PaymentIntentFixtures.PI_WITH_SHIPPING, + StripeIntentResult.Outcome.SUCCEEDED + ) + ) - flowController.onPaymentFlowResult( - PaymentFlowResult.Unvalidated( - clientSecret = PaymentSheetFixtures.CLIENT_SECRET, - flowOutcome = StripeIntentResult.Outcome.CANCELED + flowController.onPaymentFlowResult( + PaymentFlowResult.Unvalidated( + clientSecret = PaymentSheetFixtures.CLIENT_SECRET, + flowOutcome = StripeIntentResult.Outcome.CANCELED + ) ) - ) - verify(paymentResultCallback).onPaymentSheetResult( - argWhere { paymentResult -> - paymentResult is PaymentSheetResult.Completed - } - ) - } + verify(paymentResultCallback).onPaymentSheetResult( + argWhere { paymentResult -> + paymentResult is PaymentSheetResult.Completed + } + ) + } @Test - fun `onPaymentFlowResult when canceled should invoke callback with Cancelled`() { - paymentFlowResultProcessor.paymentIntentResult = PaymentIntentResult( - PaymentIntentFixtures.CANCELLED, - StripeIntentResult.Outcome.CANCELED - ) + fun `onPaymentFlowResult when canceled should invoke callback with Cancelled`() = + testDispatcher.runBlockingTest { + whenever(paymentFlowResultProcessor.processResult(any())).thenReturn( + PaymentIntentResult( + PaymentIntentFixtures.CANCELLED, + StripeIntentResult.Outcome.CANCELED + ) + ) - flowController.onPaymentFlowResult( - PaymentFlowResult.Unvalidated( - clientSecret = PaymentSheetFixtures.CLIENT_SECRET, - flowOutcome = StripeIntentResult.Outcome.CANCELED + flowController.onPaymentFlowResult( + PaymentFlowResult.Unvalidated( + clientSecret = PaymentSheetFixtures.CLIENT_SECRET, + flowOutcome = StripeIntentResult.Outcome.CANCELED + ) ) - ) - verify(paymentResultCallback).onPaymentSheetResult( - argWhere { paymentResult -> - paymentResult is PaymentSheetResult.Canceled - } - ) - } + verify(paymentResultCallback).onPaymentSheetResult( + argWhere { paymentResult -> + paymentResult is PaymentSheetResult.Canceled + } + ) + } @Test - fun `onPaymentFlowResult when error should invoke callback with Failed`() { - paymentFlowResultProcessor.paymentIntentResult = PaymentIntentResult( - PaymentIntentFixtures.PI_WITH_LAST_PAYMENT_ERROR, - StripeIntentResult.Outcome.FAILED - ) + fun `onPaymentFlowResult when error should invoke callback with Failed`() = + testDispatcher.runBlockingTest { + whenever(paymentFlowResultProcessor.processResult(any())).thenReturn( + PaymentIntentResult( + PaymentIntentFixtures.PI_WITH_LAST_PAYMENT_ERROR, + StripeIntentResult.Outcome.FAILED + ) + ) - flowController.onPaymentFlowResult( - PaymentFlowResult.Unvalidated( - clientSecret = PaymentSheetFixtures.CLIENT_SECRET, - flowOutcome = StripeIntentResult.Outcome.CANCELED + flowController.onPaymentFlowResult( + PaymentFlowResult.Unvalidated( + clientSecret = PaymentSheetFixtures.CLIENT_SECRET, + flowOutcome = StripeIntentResult.Outcome.CANCELED + ) ) - ) - verify(paymentResultCallback).onPaymentSheetResult( - argWhere { paymentResult -> - paymentResult is PaymentSheetResult.Failed - } - ) - } + verify(paymentResultCallback).onPaymentSheetResult( + argWhere { paymentResult -> + paymentResult is PaymentSheetResult.Failed + } + ) + } private fun createFlowController( paymentMethods: List = emptyList(), @@ -592,6 +602,7 @@ internal class DefaultFlowControllerTest { return FlowControllerInitializer.InitResult.Success( InitData( paymentSheetConfiguration, + clientSecret, PaymentIntentFixtures.PI_REQUIRES_PAYMENT_METHOD, listOf(PaymentMethod.Type.Card), paymentMethods, diff --git a/paymentsheet-example/src/main/java/com/stripe/android/paymentsheet/example/activity/LaunchPaymentSheetCompleteActivity.kt b/paymentsheet-example/src/main/java/com/stripe/android/paymentsheet/example/activity/LaunchPaymentSheetCompleteActivity.kt index 25bb6191307..8c095f8bf99 100644 --- a/paymentsheet-example/src/main/java/com/stripe/android/paymentsheet/example/activity/LaunchPaymentSheetCompleteActivity.kt +++ b/paymentsheet-example/src/main/java/com/stripe/android/paymentsheet/example/activity/LaunchPaymentSheetCompleteActivity.kt @@ -150,7 +150,6 @@ fun ProductRow( } } - @Composable fun ReceiptRow( description: String, @@ -209,4 +208,4 @@ fun TotalLine(modifier: Modifier) { .height(1.dp) .background(Color.LightGray) ) -} \ No newline at end of file +}