Skip to content

Commit

Permalink
Add EmbeddedPaymentElement.Configuration to FormContract.Args (#10055)
Browse files Browse the repository at this point in the history
* Pass EmbeddedConfirmationStateHolder.State to EmbeddedSheetLauncher.launchForm
  • Loading branch information
tjclawson-stripe authored Feb 1, 2025
1 parent 1bfd497 commit 045f837
Show file tree
Hide file tree
Showing 7 changed files with 63 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,9 @@ interface ErrorReporter : FraudDetectionErrorReporter {
CUSTOMER_SESSION_ON_CUSTOMER_SHEET_ELEMENTS_SESSION_NO_CUSTOMER_FIELD(
partialEventName = "customersheet.customer_session.elements_session.no_customer_field"
),
EMBEDDED_SHEET_LAUNCHER_EMBEDDED_STATE_IS_NULL(
partialEventName = "embedded.embedded_sheet_launcher.embedded_state_is_null"
),
;

override val eventName: String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import androidx.activity.result.ActivityResultLauncher
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import com.stripe.android.lpmfoundations.paymentmethod.PaymentMethodMetadata
import com.stripe.android.paymentelement.ExperimentalEmbeddedPaymentElementApi
import com.stripe.android.paymentelement.embedded.EmbeddedSelectionHolder
import com.stripe.android.paymentelement.embedded.form.FormContract
import com.stripe.android.paymentelement.embedded.form.FormResult
import com.stripe.android.paymentelement.embedded.manage.ManageContract
import com.stripe.android.paymentelement.embedded.manage.ManageResult
import com.stripe.android.payments.core.analytics.ErrorReporter
import com.stripe.android.paymentsheet.CustomerStateHolder
import com.stripe.android.paymentsheet.model.PaymentSelection
import com.stripe.android.paymentsheet.state.CustomerState
Expand All @@ -19,7 +21,8 @@ internal interface EmbeddedSheetLauncher {
fun launchForm(
code: String,
paymentMethodMetadata: PaymentMethodMetadata,
hasSavedPaymentMethods: Boolean
hasSavedPaymentMethods: Boolean,
embeddedConfirmationState: EmbeddedConfirmationStateHolder.State?
)

fun launchManage(
Expand All @@ -29,13 +32,15 @@ internal interface EmbeddedSheetLauncher {
)
}

@OptIn(ExperimentalEmbeddedPaymentElementApi::class)
@EmbeddedPaymentElementScope
internal class DefaultEmbeddedSheetLauncher @Inject constructor(
activityResultCaller: ActivityResultCaller,
lifecycleOwner: LifecycleOwner,
private val selectionHolder: EmbeddedSelectionHolder,
private val customerStateHolder: CustomerStateHolder,
private val sheetStateHolder: SheetStateHolder,
private val errorReporter: ErrorReporter,
) : EmbeddedSheetLauncher {

init {
Expand Down Expand Up @@ -77,13 +82,21 @@ internal class DefaultEmbeddedSheetLauncher @Inject constructor(
override fun launchForm(
code: String,
paymentMethodMetadata: PaymentMethodMetadata,
hasSavedPaymentMethods: Boolean
hasSavedPaymentMethods: Boolean,
embeddedConfirmationState: EmbeddedConfirmationStateHolder.State?
) {
if (embeddedConfirmationState == null) {
errorReporter.report(
ErrorReporter.UnexpectedErrorEvent.EMBEDDED_SHEET_LAUNCHER_EMBEDDED_STATE_IS_NULL
)
return
}
sheetStateHolder.sheetIsOpen = true
val args = FormContract.Args(
selectedPaymentMethodCode = code,
paymentMethodMetadata = paymentMethodMetadata,
hasSavedPaymentMethods = hasSavedPaymentMethods
hasSavedPaymentMethods = hasSavedPaymentMethods,
configuration = embeddedConfirmationState.configuration
)
formActivityLauncher.launch(args)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,8 @@ internal class DefaultEmbeddedContentHelper @Inject constructor(
paymentMethodMetadata = paymentMethodMetadata,
hasSavedPaymentMethods = customerStateHolder.paymentMethods.value.any {
it.type?.code == code
}
},
embeddedConfirmationState = confirmationStateHolder.state
)
},
paymentMethods = customerStateHolder.paymentMethods,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import com.stripe.android.link.injection.LinkAnalyticsComponent
import com.stripe.android.link.injection.LinkComponent
import com.stripe.android.lpmfoundations.paymentmethod.PaymentMethodMetadata
import com.stripe.android.model.PaymentMethodCode
import com.stripe.android.paymentelement.EmbeddedPaymentElement
import com.stripe.android.paymentelement.ExperimentalEmbeddedPaymentElementApi
import com.stripe.android.paymentelement.embedded.EmbeddedCommonModule
import com.stripe.android.paymentsheet.verticalmode.DefaultVerticalModeFormInteractor
import dagger.Binds
Expand All @@ -30,6 +32,7 @@ import kotlin.coroutines.CoroutineContext
]
)
@Singleton
@OptIn(ExperimentalEmbeddedPaymentElementApi::class)
internal interface FormActivityComponent {

val viewModel: FormActivityViewModel
Expand All @@ -47,6 +50,9 @@ internal interface FormActivityComponent {
@BindsInstance
fun hasSavedPaymentMethods(hasSavedPaymentMethods: Boolean): Builder

@BindsInstance
fun configuration(configuration: EmbeddedPaymentElement.Configuration): Builder

@BindsInstance
fun context(context: Context): Builder

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import androidx.lifecycle.createSavedStateHandle
import androidx.lifecycle.viewmodel.CreationExtras
import com.stripe.android.core.injection.ViewModelScope
import com.stripe.android.core.utils.requireApplication
import com.stripe.android.paymentelement.ExperimentalEmbeddedPaymentElementApi
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.cancel
import javax.inject.Inject
Expand All @@ -18,6 +19,7 @@ internal class FormActivityViewModel @Inject constructor(
customViewModelScope.cancel()
}

@OptIn(ExperimentalEmbeddedPaymentElementApi::class)
class Factory(
private val argSupplier: () -> FormContract.Args
) : ViewModelProvider.Factory {
Expand All @@ -28,6 +30,7 @@ internal class FormActivityViewModel @Inject constructor(
.paymentMethodMetadata(args.paymentMethodMetadata)
.selectedPaymentMethodCode(args.selectedPaymentMethodCode)
.hasSavedPaymentMethods(args.hasSavedPaymentMethods)
.configuration(args.configuration)
.context(extras.requireApplication())
.savedStateHandle(extras.createSavedStateHandle())
.build()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import android.os.Parcelable
import androidx.activity.result.contract.ActivityResultContract
import androidx.core.os.BundleCompat
import com.stripe.android.lpmfoundations.paymentmethod.PaymentMethodMetadata
import com.stripe.android.paymentelement.EmbeddedPaymentElement
import com.stripe.android.paymentelement.ExperimentalEmbeddedPaymentElementApi
import com.stripe.android.paymentsheet.model.PaymentSelection
import com.stripe.android.view.ActivityStarter
import kotlinx.parcelize.Parcelize
Expand Down Expand Up @@ -35,6 +37,7 @@ internal sealed interface FormResult : Parcelable {
}
}

@OptIn(ExperimentalEmbeddedPaymentElementApi::class)
internal object FormContract : ActivityResultContract<FormContract.Args, FormResult>() {
internal const val EXTRA_ARGS: String = "extra_activity_args"

Expand All @@ -51,7 +54,8 @@ internal object FormContract : ActivityResultContract<FormContract.Args, FormRes
internal data class Args(
val selectedPaymentMethodCode: String,
val paymentMethodMetadata: PaymentMethodMetadata,
val hasSavedPaymentMethods: Boolean
val hasSavedPaymentMethods: Boolean,
val configuration: EmbeddedPaymentElement.Configuration
) : Parcelable {
companion object {
fun fromIntent(intent: Intent): Args? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import com.google.common.truth.Truth.assertThat
import com.stripe.android.isInstanceOf
import com.stripe.android.lpmfoundations.paymentmethod.PaymentMethodMetadataFactory
import com.stripe.android.model.PaymentMethodFixtures
import com.stripe.android.paymentelement.ExperimentalEmbeddedPaymentElementApi
import com.stripe.android.paymentelement.confirmation.asCallbackFor
import com.stripe.android.paymentelement.embedded.EmbeddedSelectionHolder
import com.stripe.android.paymentelement.embedded.form.FormContract
Expand All @@ -17,28 +18,49 @@ import com.stripe.android.paymentelement.embedded.manage.ManageResult
import com.stripe.android.paymentsheet.CustomerStateHolder
import com.stripe.android.paymentsheet.PaymentSheetFixtures
import com.stripe.android.paymentsheet.model.PaymentSelection
import com.stripe.android.testing.FakeErrorReporter
import com.stripe.android.utils.DummyActivityResultCaller
import com.stripe.android.utils.DummyActivityResultCaller.RegisterCall
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner

@OptIn(ExperimentalEmbeddedPaymentElementApi::class)
@RunWith(RobolectricTestRunner::class)
internal class DefaultEmbeddedSheetLauncherTest {

@Test
fun `launchForm launches activity with correct parameters`() = testScenario {
val code = "test_code"
val paymentMethodMetadata = PaymentMethodMetadataFactory.create()
val expectedArgs = FormContract.Args(code, paymentMethodMetadata, false)
val state = EmbeddedConfirmationStateFixtures.defaultState()
val expectedArgs = FormContract.Args(
code,
paymentMethodMetadata,
false,
state.configuration
)

sheetLauncher.launchForm(code, paymentMethodMetadata, false)
sheetLauncher.launchForm(code, paymentMethodMetadata, false, state)
val launchCall = dummyActivityResultCallerScenario.awaitLaunchCall()
assertThat(launchCall).isEqualTo(expectedArgs)
assertThat(sheetStateHolder.sheetIsOpen).isTrue()
}

@Test
fun `launchForm logs error and returns if confirmation state is null`() = testScenario {
val code = "test_code"
val paymentMethodMetadata = PaymentMethodMetadataFactory.create()

sheetLauncher.launchForm(code, paymentMethodMetadata, false, null)
val loggedErrors = errorReporter.getLoggedErrors()
assertThat(loggedErrors.size).isEqualTo(1)
assertThat(loggedErrors.first())
.isEqualTo("unexpected_error.embedded.embedded_sheet_launcher.embedded_state_is_null")
assertThat(sheetStateHolder.sheetIsOpen).isFalse()
}

@Test
fun `formActivityLauncher callback updates selection holder on complete result`() = testScenario {
sheetStateHolder.sheetIsOpen = true
Expand Down Expand Up @@ -138,6 +160,7 @@ internal class DefaultEmbeddedSheetLauncherTest {
val selectionHolder = EmbeddedSelectionHolder(savedStateHandle)
val customerStateHolder = CustomerStateHolder(savedStateHandle, selectionHolder.selection)
val sheetStateHolder = SheetStateHolder(savedStateHandle)
val errorReporter = FakeErrorReporter()

DummyActivityResultCaller.test {
val sheetLauncher = DefaultEmbeddedSheetLauncher(
Expand All @@ -146,6 +169,7 @@ internal class DefaultEmbeddedSheetLauncherTest {
selectionHolder = selectionHolder,
customerStateHolder = customerStateHolder,
sheetStateHolder = sheetStateHolder,
errorReporter = errorReporter,
)
val formRegisterCall = awaitRegisterCall()
val manageRegisterCall = awaitRegisterCall()
Expand All @@ -170,6 +194,7 @@ internal class DefaultEmbeddedSheetLauncherTest {
manageLauncher = manageLauncher,
sheetLauncher = sheetLauncher,
sheetStateHolder = sheetStateHolder,
errorReporter = errorReporter
).block()
}
}
Expand All @@ -185,5 +210,6 @@ internal class DefaultEmbeddedSheetLauncherTest {
val manageLauncher: ActivityResultLauncher<*>,
val sheetLauncher: EmbeddedSheetLauncher,
val sheetStateHolder: SheetStateHolder,
val errorReporter: FakeErrorReporter
)
}

0 comments on commit 045f837

Please sign in to comment.