Skip to content

Commit

Permalink
Move Bacs to a ConfirmationDefinition type (#9688)
Browse files Browse the repository at this point in the history
* Move `Bacs` to a `ConfirmationDefinition` type

* Create test scenario for `BacsMandateConfirmationLauncherFactory`
  • Loading branch information
samer-stripe authored Nov 22, 2024
1 parent 69d4f5d commit 5c60031
Show file tree
Hide file tree
Showing 15 changed files with 643 additions and 112 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ internal interface ConfirmationDefinition<
val deferredIntentConfirmationType: DeferredIntentConfirmationType?,
) : Result

data class NextStep(
val intent: StripeIntent,
val confirmationOption: ConfirmationHandler.Option,
) : Result

data class Failed(
val cause: Throwable,
val message: ResolvableString,
Expand All @@ -74,6 +79,7 @@ internal interface ConfirmationDefinition<

data class Launch<TLauncherArgs>(
val launcherArguments: TLauncherArgs,
val receivesResultInProcess: Boolean,
val deferredIntentConfirmationType: DeferredIntentConfirmationType?,
) : Action<TLauncherArgs>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ internal class ConfirmationMediator<
intent = intent,
)
},
receivesResultInProcess = action.receivesResultInProcess,
)
} ?: run {
val exception = IllegalStateException(
Expand Down Expand Up @@ -133,6 +134,7 @@ internal class ConfirmationMediator<
sealed interface Action {
class Launch(
val launch: () -> Unit,
val receivesResultInProcess: Boolean,
) : Action

data class Fail(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import com.stripe.android.googlepaylauncher.injection.GooglePayPaymentMethodLaun
import com.stripe.android.model.PaymentIntent
import com.stripe.android.model.SetupIntent
import com.stripe.android.model.StripeIntent
import com.stripe.android.paymentelement.confirmation.bacs.BacsConfirmationOption
import com.stripe.android.paymentelement.confirmation.bacs.BacsConfirmationDefinition
import com.stripe.android.paymentelement.confirmation.epms.ExternalPaymentMethodConfirmationDefinition
import com.stripe.android.paymentelement.confirmation.gpay.GooglePayConfirmationOption
import com.stripe.android.paymentelement.confirmation.intent.IntentConfirmationDefinition
Expand All @@ -31,11 +31,7 @@ import com.stripe.android.payments.paymentlauncher.StripePaymentLauncherAssisted
import com.stripe.android.paymentsheet.ExternalPaymentMethodInterceptor
import com.stripe.android.paymentsheet.PaymentSheet
import com.stripe.android.paymentsheet.R
import com.stripe.android.paymentsheet.paymentdatacollection.bacs.BacsMandateConfirmationContract
import com.stripe.android.paymentsheet.paymentdatacollection.bacs.BacsMandateConfirmationLauncher
import com.stripe.android.paymentsheet.paymentdatacollection.bacs.BacsMandateConfirmationLauncherFactory
import com.stripe.android.paymentsheet.paymentdatacollection.bacs.BacsMandateConfirmationResult
import com.stripe.android.paymentsheet.paymentdatacollection.bacs.BacsMandateData
import com.stripe.android.paymentsheet.state.PaymentElementLoader
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
Expand Down Expand Up @@ -68,21 +64,23 @@ internal class DefaultConfirmationHandler(
private val intentConfirmationRegistry = ConfirmationRegistry(
confirmationDefinitions = listOf(
IntentConfirmationDefinition(
intentConfirmationInterceptor,
paymentLauncherFactory,
intentConfirmationInterceptor = intentConfirmationInterceptor,
paymentLauncherFactory = paymentLauncherFactory,
),
ExternalPaymentMethodConfirmationDefinition(
externalPaymentMethodConfirmHandlerProvider = {
ExternalPaymentMethodInterceptor.externalPaymentMethodConfirmHandler
},
errorReporter = errorReporter,
),
BacsConfirmationDefinition(
bacsMandateConfirmationLauncherFactory = bacsMandateConfirmationLauncherFactory,
)
)
)

private val confirmationMediators = intentConfirmationRegistry.createConfirmationMediators(savedStateHandle)

private var bacsMandateConfirmationLauncher: BacsMandateConfirmationLauncher? = null
private var googlePayPaymentMethodLauncher:
ActivityResultLauncher<GooglePayPaymentMethodLauncherContractV2.Args>? = null

Expand Down Expand Up @@ -137,13 +135,6 @@ internal class DefaultConfirmationHandler(
mediator.register(activityResultCaller, ::onResult)
}

val bacsActivityResultLauncher = activityResultCaller.registerForActivityResult(
BacsMandateConfirmationContract(),
::onBacsMandateResult
)

bacsMandateConfirmationLauncher = bacsMandateConfirmationLauncherFactory.create(bacsActivityResultLauncher)

googlePayPaymentMethodLauncher = activityResultCaller.registerForActivityResult(
GooglePayPaymentMethodLauncherContractV2(),
::onGooglePayResult
Expand All @@ -155,9 +146,7 @@ internal class DefaultConfirmationHandler(
confirmationMediators.forEach { mediator ->
mediator.unregister()
}
bacsMandateConfirmationLauncher = null
googlePayPaymentMethodLauncher = null
bacsActivityResultLauncher.unregister()
super.onDestroy(owner)
}
}
Expand Down Expand Up @@ -219,7 +208,6 @@ internal class DefaultConfirmationHandler(
googlePay = confirmationOption,
intent = arguments.intent,
)
is BacsConfirmationOption -> launchBacsMandate(confirmationOption)
else -> confirm(confirmationOption, arguments.intent)
}
}
Expand Down Expand Up @@ -259,7 +247,7 @@ internal class DefaultConfirmationHandler(
is ConfirmationMediator.Action.Launch -> {
storeIsAwaitingForResult(
option = confirmationOption,
receivesResultInProcess = false,
receivesResultInProcess = action.receivesResultInProcess,
)

action.launch()
Expand Down Expand Up @@ -384,81 +372,6 @@ internal class DefaultConfirmationHandler(
)
}

private fun launchBacsMandate(
confirmationOption: BacsConfirmationOption,
) {
BacsMandateData.fromConfirmationOption(confirmationOption)?.let { data ->
runCatching {
requireNotNull(bacsMandateConfirmationLauncher)
}.onSuccess { launcher ->
storeIsAwaitingForResult(
option = confirmationOption,
receivesResultInProcess = true,
)

launcher.launch(
data = data,
appearance = confirmationOption.appearance
)
}.onFailure { cause ->
onIntentResult(
ConfirmationHandler.Result.Failed(
cause = cause,
message = R.string.stripe_something_went_wrong.resolvableString,
type = ConfirmationHandler.Result.Failed.ErrorType.Internal
)
)
}
} ?: run {
onIntentResult(
ConfirmationHandler.Result.Failed(
cause = IllegalArgumentException(
"Given payment selection could not be converted to Bacs data!"
),
message = R.string.stripe_something_went_wrong.resolvableString,
type = ConfirmationHandler.Result.Failed.ErrorType.Internal
)
)
}
}

private fun onBacsMandateResult(result: BacsMandateConfirmationResult) {
coroutineScope.launch {
removeIsAwaitingForResult()

when (result) {
is BacsMandateConfirmationResult.Confirmed -> {
val arguments = currentArguments
val bacs = arguments?.confirmationOption as? BacsConfirmationOption

bacs?.let { bacsPaymentMethod ->
confirm(
arguments.copy(
confirmationOption = PaymentMethodConfirmationOption.New(
initializationMode = bacsPaymentMethod.initializationMode,
shippingDetails = bacsPaymentMethod.shippingDetails,
createParams = bacsPaymentMethod.createParams,
optionsParams = null,
shouldSave = false,
)
)
)
}
}
is BacsMandateConfirmationResult.ModifyDetails -> onIntentResult(
ConfirmationHandler.Result.Canceled(
action = ConfirmationHandler.Result.Canceled.Action.ModifyPaymentDetails,
)
)
is BacsMandateConfirmationResult.Cancelled -> onIntentResult(
ConfirmationHandler.Result.Canceled(
action = ConfirmationHandler.Result.Canceled.Action.None,
)
)
}
}
}

private fun onGooglePayResult(result: GooglePayPaymentMethodLauncher.Result) {
coroutineScope.launch {
removeIsAwaitingForResult()
Expand Down Expand Up @@ -509,6 +422,18 @@ internal class DefaultConfirmationHandler(

private fun onResult(result: ConfirmationDefinition.Result) {
val confirmationResult = when (result) {
is ConfirmationDefinition.Result.NextStep -> {
coroutineScope.launch {
confirm(
arguments = ConfirmationHandler.Args(
intent = result.intent,
confirmationOption = result.confirmationOption,
)
)
}

return
}
is ConfirmationDefinition.Result.Succeeded -> ConfirmationHandler.Result.Succeeded(
intent = result.intent,
deferredIntentConfirmationType = result.deferredIntentConfirmationType,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package com.stripe.android.paymentelement.confirmation.bacs
import androidx.activity.result.ActivityResultCaller
import com.stripe.android.core.strings.resolvableString
import com.stripe.android.model.StripeIntent
import com.stripe.android.paymentelement.confirmation.ConfirmationDefinition
import com.stripe.android.paymentelement.confirmation.ConfirmationHandler
import com.stripe.android.paymentelement.confirmation.PaymentMethodConfirmationOption
import com.stripe.android.paymentelement.confirmation.intent.DeferredIntentConfirmationType
import com.stripe.android.paymentsheet.R
import com.stripe.android.paymentsheet.paymentdatacollection.bacs.BacsMandateConfirmationContract
import com.stripe.android.paymentsheet.paymentdatacollection.bacs.BacsMandateConfirmationLauncher
import com.stripe.android.paymentsheet.paymentdatacollection.bacs.BacsMandateConfirmationLauncherFactory
import com.stripe.android.paymentsheet.paymentdatacollection.bacs.BacsMandateConfirmationResult
import com.stripe.android.paymentsheet.paymentdatacollection.bacs.BacsMandateData

internal class BacsConfirmationDefinition(
private val bacsMandateConfirmationLauncherFactory: BacsMandateConfirmationLauncherFactory,
) : ConfirmationDefinition<
BacsConfirmationOption,
BacsMandateConfirmationLauncher,
BacsMandateData,
BacsMandateConfirmationResult,
> {
override val key: String = "Bacs"

override fun option(confirmationOption: ConfirmationHandler.Option): BacsConfirmationOption? {
return confirmationOption as? BacsConfirmationOption
}

override suspend fun action(
confirmationOption: BacsConfirmationOption,
intent: StripeIntent
): ConfirmationDefinition.Action<BacsMandateData> {
return BacsMandateData.fromConfirmationOption(confirmationOption)?.let { data ->
ConfirmationDefinition.Action.Launch(
launcherArguments = data,
deferredIntentConfirmationType = null,
receivesResultInProcess = true,
)
} ?: run {
ConfirmationDefinition.Action.Fail(
cause = IllegalArgumentException(
"Given confirmation option does not have expected Bacs data!"
),
message = R.string.stripe_something_went_wrong.resolvableString,
errorType = ConfirmationHandler.Result.Failed.ErrorType.Internal
)
}
}

override fun createLauncher(
activityResultCaller: ActivityResultCaller,
onResult: (BacsMandateConfirmationResult) -> Unit
): BacsMandateConfirmationLauncher {
return bacsMandateConfirmationLauncherFactory.create(
activityResultCaller.registerForActivityResult(
BacsMandateConfirmationContract(),
onResult,
)
)
}

override fun launch(
launcher: BacsMandateConfirmationLauncher,
arguments: BacsMandateData,
confirmationOption: BacsConfirmationOption,
intent: StripeIntent,
) {
launcher.launch(
data = arguments,
appearance = confirmationOption.appearance
)
}

override fun toResult(
confirmationOption: BacsConfirmationOption,
deferredIntentConfirmationType: DeferredIntentConfirmationType?,
intent: StripeIntent,
result: BacsMandateConfirmationResult,
): ConfirmationDefinition.Result {
return when (result) {
is BacsMandateConfirmationResult.Confirmed -> {
val nextConfirmationOption = PaymentMethodConfirmationOption.New(
initializationMode = confirmationOption.initializationMode,
shippingDetails = confirmationOption.shippingDetails,
createParams = confirmationOption.createParams,
optionsParams = null,
shouldSave = false,
)

ConfirmationDefinition.Result.NextStep(
intent = intent,
confirmationOption = nextConfirmationOption,
)
}
is BacsMandateConfirmationResult.ModifyDetails -> ConfirmationDefinition.Result.Canceled(
action = ConfirmationHandler.Result.Canceled.Action.ModifyPaymentDetails,
)
is BacsMandateConfirmationResult.Cancelled -> ConfirmationDefinition.Result.Canceled(
action = ConfirmationHandler.Result.Canceled.Action.None,
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ internal class ExternalPaymentMethodConfirmationDefinition(
ConfirmationDefinition.Action.Launch(
launcherArguments = Unit,
deferredIntentConfirmationType = null,
receivesResultInProcess = false,
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,14 @@ internal class IntentConfirmationDefinition(
ConfirmationDefinition.Action.Launch(
launcherArguments = Args.NextAction(nextStep.clientSecret),
deferredIntentConfirmationType = deferredIntentConfirmationType,
receivesResultInProcess = false,
)
}
is IntentConfirmationInterceptor.NextStep.Confirm -> {
ConfirmationDefinition.Action.Launch(
launcherArguments = Args.Confirm(nextStep.confirmParams),
deferredIntentConfirmationType = deferredIntentConfirmationType,
receivesResultInProcess = false,
)
}
is IntentConfirmationInterceptor.NextStep.Fail -> {
Expand Down
Loading

0 comments on commit 5c60031

Please sign in to comment.