Skip to content

Commit

Permalink
Migrate PaymentController to PaymentLauncher within PaymentSheet comp…
Browse files Browse the repository at this point in the history
…onents. (#4159)

* inject payment launcher into model

* process paymentResult rather than StripeIntentResult

* add correct error messages to payment sheet

* fix klint

* update unit tests

* fix api check failure

* fix klint again

* some clean up

* use throwable message instead of removal internal from message factory class

* use already retrieved stripe intent instead of fetching

* close payment launcher again

* linter fixes

* continue refetching stripe intent

* update unit tests to reflect new api

* fix apicheck

* added some null checks
  • Loading branch information
skyler-stripe authored Sep 3, 2021
1 parent 892e369 commit 0def576
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 181 deletions.
6 changes: 3 additions & 3 deletions paymentsheet/api/paymentsheet.api
Original file line number Diff line number Diff line change
Expand Up @@ -271,11 +271,11 @@ public abstract interface class com/stripe/android/paymentsheet/PaymentSheetResu
}

public final class com/stripe/android/paymentsheet/PaymentSheetViewModel_Factory : dagger/internal/Factory {
public fun <init> (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 <init> (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 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/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 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;Lcom/stripe/android/paymentsheet/PrefsRepository;Lcom/stripe/android/payments/paymentlauncher/StripePaymentLauncherAssistedFactory;Lcom/stripe/android/googlepaylauncher/GooglePayPaymentMethodLauncherFactory;Lcom/stripe/android/Logger;Lkotlin/coroutines/CoroutineContext;)Lcom/stripe/android/paymentsheet/PaymentSheetViewModel;
}

public final class com/stripe/android/paymentsheet/address/AddressFieldElementRepository_Factory : dagger/internal/Factory {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import com.stripe.android.paymentsheet.model.PaymentSheetViewState
import com.stripe.android.paymentsheet.ui.AnimationConstants
import com.stripe.android.paymentsheet.ui.BaseSheetActivity
import com.stripe.android.paymentsheet.viewmodels.BaseSheetViewModel
import com.stripe.android.view.AuthActivityStarterHost
import kotlinx.coroutines.launch

internal class PaymentSheetActivity : BaseSheetActivity<PaymentSheetResult>() {
Expand Down Expand Up @@ -145,10 +144,7 @@ internal class PaymentSheetActivity : BaseSheetActivity<PaymentSheetResult>() {
val confirmParams = event.getContentIfNotHandled()
if (confirmParams != null) {
lifecycleScope.launch {
viewModel.confirmStripeIntent(
AuthActivityStarterHost.create(this@PaymentSheetActivity),
confirmParams
)
viewModel.confirmStripeIntent(confirmParams)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,21 @@ import androidx.lifecycle.distinctUntilChanged
import androidx.lifecycle.viewModelScope
import com.stripe.android.Logger
import com.stripe.android.PaymentConfiguration
import com.stripe.android.PaymentController
import com.stripe.android.StripeIntentResult
import com.stripe.android.exception.APIConnectionException
import com.stripe.android.googlepaylauncher.GooglePayEnvironment
import com.stripe.android.googlepaylauncher.GooglePayPaymentMethodLauncher
import com.stripe.android.googlepaylauncher.GooglePayPaymentMethodLauncherContract
import com.stripe.android.googlepaylauncher.GooglePayPaymentMethodLauncherFactory
import com.stripe.android.model.ConfirmPaymentIntentParams
import com.stripe.android.model.ConfirmSetupIntentParams
import com.stripe.android.model.ConfirmStripeIntentParams
import com.stripe.android.model.PaymentIntent
import com.stripe.android.model.PaymentMethod
import com.stripe.android.model.StripeIntent
import com.stripe.android.networking.ApiRequest
import com.stripe.android.payments.PaymentFlowResult
import com.stripe.android.payments.PaymentFlowResultProcessor
import com.stripe.android.payments.core.injection.IOContext
import com.stripe.android.payments.paymentlauncher.PaymentLauncher
import com.stripe.android.payments.paymentlauncher.PaymentLauncherContract
import com.stripe.android.payments.paymentlauncher.PaymentResult
import com.stripe.android.payments.paymentlauncher.StripePaymentLauncherAssistedFactory
import com.stripe.android.paymentsheet.analytics.EventReporter
import com.stripe.android.paymentsheet.injection.DaggerPaymentSheetViewModelComponent
import com.stripe.android.paymentsheet.model.ConfirmStripeIntentParamsFactory
Expand All @@ -41,13 +41,10 @@ import com.stripe.android.paymentsheet.repositories.CustomerRepository
import com.stripe.android.paymentsheet.repositories.StripeIntentRepository
import com.stripe.android.paymentsheet.ui.PrimaryButton
import com.stripe.android.paymentsheet.viewmodels.BaseSheetViewModel
import com.stripe.android.view.AuthActivityStarterHost
import dagger.Lazy
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import javax.inject.Inject
import javax.inject.Provider
import javax.inject.Singleton
import kotlin.coroutines.CoroutineContext

Expand Down Expand Up @@ -77,10 +74,8 @@ internal class PaymentSheetViewModel @Inject internal constructor(
private val stripeIntentRepository: StripeIntentRepository,
private val stripeIntentValidator: StripeIntentValidator,
customerRepository: CustomerRepository,
private val paymentFlowResultProcessorProvider:
Provider<PaymentFlowResultProcessor<out StripeIntent, StripeIntentResult<StripeIntent>>>,
prefsRepository: PrefsRepository,
private val paymentController: PaymentController,
private val paymentLauncherFactory: StripePaymentLauncherAssistedFactory,
private val googlePayPaymentMethodLauncherFactory: GooglePayPaymentMethodLauncherFactory,
private val logger: Logger,
@IOContext workContext: CoroutineContext
Expand Down Expand Up @@ -155,26 +150,15 @@ internal class PaymentSheetViewModel @Inject internal constructor(
}
}

internal var paymentLauncher: PaymentLauncher? = null

init {
eventReporter.onInit(config)
if (googlePayLauncherConfig == null) {
_isGooglePayReady.value = false
}
}

private fun apiThrowableToString(throwable: Throwable): String? {
return when (throwable) {
is APIConnectionException -> {
getApplication<Application>().resources.getString(
R.string.stripe_failure_connection_error
)
}
else -> {
throwable.localizedMessage
}
}
}

fun setupGooglePay(
lifecycleScope: CoroutineScope,
activityResultLauncher: ActivityResultLauncher<GooglePayPaymentMethodLauncherContract.Args>
Expand Down Expand Up @@ -301,29 +285,37 @@ internal class PaymentSheetViewModel @Inject internal constructor(
}
}

suspend fun confirmStripeIntent(
authActivityStarterHost: AuthActivityStarterHost,
confirmStripeIntentParams: ConfirmStripeIntentParams
) {
paymentController.startConfirmAndAuth(
authActivityStarterHost,
confirmStripeIntentParams,
ApiRequest.Options(
lazyPaymentConfig.get().publishableKey,
lazyPaymentConfig.get().stripeAccountId
)
fun confirmStripeIntent(confirmStripeIntentParams: ConfirmStripeIntentParams) {
runCatching {
requireNotNull(paymentLauncher)
}.fold(
onSuccess = {
when (confirmStripeIntentParams) {
is ConfirmPaymentIntentParams -> {
it.confirm(confirmStripeIntentParams)
}
is ConfirmSetupIntentParams -> {
it.confirm(confirmStripeIntentParams)
}
}
},
onFailure = ::onFatal
)
}

fun registerFromActivity(activityResultCaller: ActivityResultCaller) {
paymentController.registerLaunchersWithActivityResultCaller(
activityResultCaller,
::onPaymentFlowResult
paymentLauncher = paymentLauncherFactory.create(
{ lazyPaymentConfig.get().publishableKey },
{ lazyPaymentConfig.get().stripeAccountId },
activityResultCaller.registerForActivityResult(
PaymentLauncherContract(),
::onPaymentResult
)
)
}

fun unregisterFromActivity() {
paymentController.unregisterLaunchers()
paymentLauncher = null
}

private fun confirmPaymentSelection(paymentSelection: PaymentSelection?) {
Expand All @@ -340,16 +332,28 @@ internal class PaymentSheetViewModel @Inject internal constructor(
}
}

private fun onStripeIntentResult(
stripeIntentResult: StripeIntentResult<StripeIntent>
) {
when (stripeIntentResult.outcome) {
StripeIntentResult.Outcome.SUCCEEDED -> {
@VisibleForTesting
fun onPaymentResult(paymentResult: PaymentResult) {
viewModelScope.launch {
runCatching {
stripeIntentRepository.get(args.clientSecret)
}.fold(
onSuccess = {
processPayment(it, paymentResult)
},
onFailure = ::onFatal
)
}
}

private fun processPayment(stripeIntent: StripeIntent, paymentResult: PaymentResult) {
when (paymentResult) {
PaymentResult.Completed -> {
eventReporter.onPaymentSuccess(selection.value)

// SavedSelection needs to happen after new cards have been saved.
when (selection.value) {
is PaymentSelection.New -> stripeIntentResult.intent.paymentMethod?.let {
is PaymentSelection.New -> stripeIntent.paymentMethod?.let {
PaymentSelection.Saved(it)
}
PaymentSelection.GooglePay -> selection.value
Expand All @@ -367,11 +371,14 @@ internal class PaymentSheetViewModel @Inject internal constructor(
eventReporter.onPaymentFailure(selection.value)

runCatching {
stripeIntentValidator.requireValid(stripeIntentResult.intent)
stripeIntentValidator.requireValid(stripeIntent)
}.fold(
onSuccess = {
resetViewState(
stripeIntentResult.failureMessage
when (paymentResult) {
is PaymentResult.Failed -> paymentResult.throwable.message
else -> null // indicates canceled payment
}
)
},
onFailure = ::onFatal
Expand Down Expand Up @@ -399,29 +406,6 @@ internal class PaymentSheetViewModel @Inject internal constructor(
}
}

fun onPaymentFlowResult(paymentFlowResult: PaymentFlowResult.Unvalidated) {
viewModelScope.launch {
runCatching {
withContext(workContext) {
paymentFlowResultProcessorProvider.get().processResult(
paymentFlowResult
)
}
}.fold(
onSuccess = {
onStripeIntentResult(it)
},
onFailure = { error ->
selection.value?.let {
eventReporter.onPaymentFailure(it)
}

resetViewState(apiThrowableToString(error))
}
)
}
}

override fun onFatal(throwable: Throwable) {
logger.error("Payment Sheet error", throwable)
_fatal.value = throwable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import com.stripe.android.Logger
import com.stripe.android.PaymentConfiguration
import com.stripe.android.PaymentController
import com.stripe.android.PaymentIntentResult
import com.stripe.android.StripeIntentResult
import com.stripe.android.googlepaylauncher.GooglePayPaymentMethodLauncher
import com.stripe.android.googlepaylauncher.GooglePayPaymentMethodLauncherContract
import com.stripe.android.googlepaylauncher.GooglePayPaymentMethodLauncherFactory
Expand All @@ -25,8 +24,9 @@ import com.stripe.android.model.PaymentIntentFixtures
import com.stripe.android.model.PaymentMethod
import com.stripe.android.model.PaymentMethodCreateParamsFixtures
import com.stripe.android.model.PaymentMethodFixtures
import com.stripe.android.payments.PaymentFlowResult
import com.stripe.android.payments.PaymentFlowResultProcessor
import com.stripe.android.payments.paymentlauncher.PaymentResult
import com.stripe.android.payments.paymentlauncher.StripePaymentLauncherAssistedFactory
import com.stripe.android.paymentsheet.PaymentSheetViewModel.CheckoutIdentifier
import com.stripe.android.paymentsheet.analytics.EventReporter
import com.stripe.android.paymentsheet.databinding.PrimaryButtonBinding
Expand Down Expand Up @@ -73,6 +73,8 @@ internal class PaymentSheetActivityTest {
private val eventReporter = mock<EventReporter>()
private val googlePayPaymentMethodLauncherFactory =
createGooglePayPaymentMethodLauncherFactory()
private val stripePaymentLauncherAssistedFactory =
mock<StripePaymentLauncherAssistedFactory>()

private val viewModel = createViewModel()

Expand Down Expand Up @@ -508,13 +510,8 @@ internal class PaymentSheetActivityTest {
idleLooper()

viewModel.checkoutIdentifier = CheckoutIdentifier.SheetBottomBuy
viewModel.onPaymentResult(PaymentResult.Completed)

viewModel.onPaymentFlowResult(
PaymentFlowResult.Unvalidated(
"client_secret",
StripeIntentResult.Outcome.SUCCEEDED
)
)
idleLooper()

assertThat(activity.bottomSheetBehavior.state)
Expand Down Expand Up @@ -723,7 +720,7 @@ internal class PaymentSheetActivityTest {
mock<PaymentFlowResultProcessor<PaymentIntent, PaymentIntentResult>>()
whenever(paymentFlowResultProcessor.processResult(any())).thenReturn(paymentIntentResult)

val mockPaymentController: PaymentController = mock()
val mockPaymentLauncher: PaymentController = mock()
PaymentSheetViewModel(
ApplicationProvider.getApplicationContext(),
PaymentSheetFixtures.ARGS_CUSTOMER_WITH_GOOGLEPAY,
Expand All @@ -732,9 +729,8 @@ internal class PaymentSheetActivityTest {
StripeIntentRepository.Static(paymentIntent),
StripeIntentValidator(),
FakeCustomerRepository(paymentMethods),
{ paymentFlowResultProcessor },
FakePrefsRepository(),
mockPaymentController,
stripePaymentLauncherAssistedFactory,
googlePayPaymentMethodLauncherFactory,
Logger.noop(),
testDispatcher
Expand Down
Loading

0 comments on commit 0def576

Please sign in to comment.