diff --git a/paymentsheet-example/src/main/java/com/stripe/android/paymentsheet/example/playground/embedded/EmbeddedPlaygroundActivity.kt b/paymentsheet-example/src/main/java/com/stripe/android/paymentsheet/example/playground/embedded/EmbeddedPlaygroundActivity.kt index 50dc7ba98d3..848a864749b 100644 --- a/paymentsheet-example/src/main/java/com/stripe/android/paymentsheet/example/playground/embedded/EmbeddedPlaygroundActivity.kt +++ b/paymentsheet-example/src/main/java/com/stripe/android/paymentsheet/example/playground/embedded/EmbeddedPlaygroundActivity.kt @@ -8,6 +8,7 @@ import android.widget.Toast import androidx.activity.compose.setContent import androidx.appcompat.app.AppCompatActivity import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -15,6 +16,7 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.Button import androidx.compose.material.Text +import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier @@ -27,6 +29,7 @@ import com.stripe.android.paymentsheet.CreateIntentResult import com.stripe.android.paymentsheet.ExternalPaymentMethodConfirmHandler import com.stripe.android.paymentsheet.example.playground.PlaygroundState import com.stripe.android.paymentsheet.example.playground.activity.FawryActivity +import com.stripe.android.paymentsheet.example.playground.settings.EmbeddedViewDisplaysMandateSettingDefinition import com.stripe.android.paymentsheet.example.playground.settings.PlaygroundConfigurationData import com.stripe.android.paymentsheet.example.samples.ui.shared.BuyButton import com.stripe.android.paymentsheet.example.samples.ui.shared.PaymentMethodSelector @@ -60,6 +63,7 @@ internal class EmbeddedPlaygroundActivity : AppCompatActivity(), ExternalPayment }, resultCallback = ::handleEmbeddedResult, ).externalPaymentMethodConfirmHandler(this) + val embeddedViewDisplaysMandateText = playgroundState.snapshot[EmbeddedViewDisplaysMandateSettingDefinition] setContent { val embeddedPaymentElement = rememberEmbeddedPaymentElement(embeddedBuilder) @@ -82,29 +86,48 @@ internal class EmbeddedPlaygroundActivity : AppCompatActivity(), ExternalPayment val selectedPaymentOption by embeddedPaymentElement.paymentOption.collectAsState() selectedPaymentOption?.let { selectedPaymentOption -> - PaymentMethodSelector( - isEnabled = true, - paymentMethodLabel = selectedPaymentOption.label, - paymentMethodPainter = selectedPaymentOption.iconPainter, - clickable = false, - onClick = { }, + EmbeddedContentWithSelectedPaymentOption( + embeddedPaymentElement = embeddedPaymentElement, + selectedPaymentOption = selectedPaymentOption, + embeddedViewDisplaysMandateText = embeddedViewDisplaysMandateText, ) + } + } + } + } - BuyButton(buyButtonEnabled = true) { - embeddedPaymentElement.confirm() - } + @Composable + private fun ColumnScope.EmbeddedContentWithSelectedPaymentOption( + embeddedPaymentElement: EmbeddedPaymentElement, + selectedPaymentOption: EmbeddedPaymentElement.PaymentOptionDisplayData, + embeddedViewDisplaysMandateText: Boolean, + ) { + PaymentMethodSelector( + isEnabled = true, + paymentMethodLabel = selectedPaymentOption.label, + paymentMethodPainter = selectedPaymentOption.iconPainter, + clickable = false, + onClick = { }, + ) - Button( - onClick = { - embeddedPaymentElement.clearPaymentOption() - }, - Modifier.fillMaxWidth(), - ) { - Text("Clear selected") - } - } + if (!embeddedViewDisplaysMandateText) { + selectedPaymentOption.mandateText?.let { mandateText -> + Text(mandateText) } } + + BuyButton(buyButtonEnabled = true) { + embeddedPaymentElement.confirm() + } + + Button( + onClick = { + embeddedPaymentElement.clearPaymentOption() + }, + Modifier.fillMaxWidth(), + ) { + Text("Clear selected") + } } override fun confirmExternalPaymentMethod( diff --git a/paymentsheet-example/src/main/java/com/stripe/android/paymentsheet/example/playground/settings/EmbeddedViewDisplaysMandateSettingDefinition.kt b/paymentsheet-example/src/main/java/com/stripe/android/paymentsheet/example/playground/settings/EmbeddedViewDisplaysMandateSettingDefinition.kt new file mode 100644 index 00000000000..525d43a3f85 --- /dev/null +++ b/paymentsheet-example/src/main/java/com/stripe/android/paymentsheet/example/playground/settings/EmbeddedViewDisplaysMandateSettingDefinition.kt @@ -0,0 +1,25 @@ +package com.stripe.android.paymentsheet.example.playground.settings + +import com.stripe.android.paymentelement.EmbeddedPaymentElement +import com.stripe.android.paymentelement.ExperimentalEmbeddedPaymentElementApi +import com.stripe.android.paymentsheet.example.playground.PlaygroundState + +@OptIn(ExperimentalEmbeddedPaymentElementApi::class) +internal object EmbeddedViewDisplaysMandateSettingDefinition : BooleanSettingsDefinition( + key = "embeddedViewDisplaysMandate", + displayName = "Embedded View Displays Mandate", + defaultValue = true, +) { + override fun applicable(configurationData: PlaygroundConfigurationData): Boolean { + return configurationData.integrationType == PlaygroundConfigurationData.IntegrationType.Embedded + } + + override fun configure( + value: Boolean, + configurationBuilder: EmbeddedPaymentElement.Configuration.Builder, + playgroundState: PlaygroundState.Payment, + configurationData: PlaygroundSettingDefinition.EmbeddedConfigurationData + ) { + configurationBuilder.embeddedViewDisplaysMandateText(value) + } +} diff --git a/paymentsheet-example/src/main/java/com/stripe/android/paymentsheet/example/playground/settings/PlaygroundSettings.kt b/paymentsheet-example/src/main/java/com/stripe/android/paymentsheet/example/playground/settings/PlaygroundSettings.kt index 49772f8f43c..e76e09d5f86 100644 --- a/paymentsheet-example/src/main/java/com/stripe/android/paymentsheet/example/playground/settings/PlaygroundSettings.kt +++ b/paymentsheet-example/src/main/java/com/stripe/android/paymentsheet/example/playground/settings/PlaygroundSettings.kt @@ -450,6 +450,7 @@ internal class PlaygroundSettings private constructor( CardBrandAcceptanceSettingsDefinition, FeatureFlagSettingsDefinition(FeatureFlags.instantDebitsIncentives), FeatureFlagSettingsDefinition(FeatureFlags.enableDefaultPaymentMethods), + EmbeddedViewDisplaysMandateSettingDefinition, ) private val nonUiSettingDefinitions: List> = listOf( diff --git a/paymentsheet/src/main/java/com/stripe/android/paymentelement/embedded/EmbeddedContentHelper.kt b/paymentsheet/src/main/java/com/stripe/android/paymentelement/embedded/EmbeddedContentHelper.kt index 3f264883753..799361f59c5 100644 --- a/paymentsheet/src/main/java/com/stripe/android/paymentelement/embedded/EmbeddedContentHelper.kt +++ b/paymentsheet/src/main/java/com/stripe/android/paymentelement/embedded/EmbeddedContentHelper.kt @@ -41,7 +41,11 @@ import kotlin.coroutines.CoroutineContext internal interface EmbeddedContentHelper { val embeddedContent: StateFlow - fun dataLoaded(paymentMethodMetadata: PaymentMethodMetadata, rowStyle: Embedded.RowStyle) + fun dataLoaded( + paymentMethodMetadata: PaymentMethodMetadata, + rowStyle: Embedded.RowStyle, + embeddedViewDisplaysMandateText: Boolean, + ) } internal fun interface EmbeddedContentHelperFactory { @@ -94,17 +98,24 @@ internal class DefaultEmbeddedContentHelper @AssistedInject constructor( } coroutineScope.launch { mandate.collect { mandate -> - _embeddedContent.update { originalEmbeddedContent -> - originalEmbeddedContent?.copy(mandate = mandate) + if (state.value?.embeddedViewDisplaysMandateText == true) { + _embeddedContent.update { originalEmbeddedContent -> + originalEmbeddedContent?.copy(mandate = mandate) + } } } } } - override fun dataLoaded(paymentMethodMetadata: PaymentMethodMetadata, rowStyle: Embedded.RowStyle) { + override fun dataLoaded( + paymentMethodMetadata: PaymentMethodMetadata, + rowStyle: Embedded.RowStyle, + embeddedViewDisplaysMandateText: Boolean, + ) { savedStateHandle[STATE_KEY_EMBEDDED_CONTENT] = State( paymentMethodMetadata = paymentMethodMetadata, rowStyle = rowStyle, + embeddedViewDisplaysMandateText = embeddedViewDisplaysMandateText, ) } @@ -262,6 +273,7 @@ internal class DefaultEmbeddedContentHelper @AssistedInject constructor( class State( val paymentMethodMetadata: PaymentMethodMetadata, val rowStyle: Embedded.RowStyle, + val embeddedViewDisplaysMandateText: Boolean, ) : Parcelable companion object { diff --git a/paymentsheet/src/main/java/com/stripe/android/paymentelement/embedded/SharedPaymentElementViewModel.kt b/paymentsheet/src/main/java/com/stripe/android/paymentelement/embedded/SharedPaymentElementViewModel.kt index 6a7f90cab7b..6c324519681 100644 --- a/paymentsheet/src/main/java/com/stripe/android/paymentelement/embedded/SharedPaymentElementViewModel.kt +++ b/paymentsheet/src/main/java/com/stripe/android/paymentelement/embedded/SharedPaymentElementViewModel.kt @@ -128,7 +128,8 @@ internal class SharedPaymentElementViewModel @Inject constructor( selectionHolder.set(state.paymentSelection) embeddedContentHelper.dataLoaded( paymentMethodMetadata = state.paymentMethodMetadata, - rowStyle = configuration.appearance.embeddedAppearance.style + rowStyle = configuration.appearance.embeddedAppearance.style, + embeddedViewDisplaysMandateText = configuration.embeddedViewDisplaysMandateText, ) ConfigureResult.Succeeded() }, diff --git a/paymentsheet/src/test/java/com/stripe/android/paymentelement/embedded/DefaultEmbeddedContentHelperTest.kt b/paymentsheet/src/test/java/com/stripe/android/paymentelement/embedded/DefaultEmbeddedContentHelperTest.kt index 0476d255acd..e0f835e9352 100644 --- a/paymentsheet/src/test/java/com/stripe/android/paymentelement/embedded/DefaultEmbeddedContentHelperTest.kt +++ b/paymentsheet/src/test/java/com/stripe/android/paymentelement/embedded/DefaultEmbeddedContentHelperTest.kt @@ -27,7 +27,7 @@ internal class DefaultEmbeddedContentHelperTest { .isNull() val paymentMethodMetadata = PaymentMethodMetadataFactory.create() val rowStyle = Embedded.RowStyle.FlatWithRadio.defaultLight - embeddedContentHelper.dataLoaded(paymentMethodMetadata, rowStyle) + embeddedContentHelper.dataLoaded(paymentMethodMetadata, rowStyle, embeddedViewDisplaysMandateText = true) val state = savedStateHandle.get(STATE_KEY_EMBEDDED_CONTENT) assertThat(state?.paymentMethodMetadata).isEqualTo(paymentMethodMetadata) assertThat(state?.rowStyle).isEqualTo(rowStyle) @@ -39,7 +39,8 @@ internal class DefaultEmbeddedContentHelperTest { assertThat(awaitItem()).isNull() embeddedContentHelper.dataLoaded( PaymentMethodMetadataFactory.create(), - Embedded.RowStyle.FlatWithRadio.defaultLight + Embedded.RowStyle.FlatWithRadio.defaultLight, + embeddedViewDisplaysMandateText = true, ) assertThat(awaitItem()).isNotNull() } @@ -51,7 +52,8 @@ internal class DefaultEmbeddedContentHelperTest { assertThat(awaitItem()).isNull() embeddedContentHelper.dataLoaded( PaymentMethodMetadataFactory.create(), - Embedded.RowStyle.FlatWithRadio.defaultLight + Embedded.RowStyle.FlatWithRadio.defaultLight, + embeddedViewDisplaysMandateText = true, ) awaitItem().run { assertThat(this).isNotNull() @@ -65,6 +67,24 @@ internal class DefaultEmbeddedContentHelperTest { } } + @Test + fun `setting mandate when embeddedViewDisplaysMandateText does not emit event with mandate`() = testScenario { + embeddedContentHelper.embeddedContent.test { + assertThat(awaitItem()).isNull() + embeddedContentHelper.dataLoaded( + PaymentMethodMetadataFactory.create(), + Embedded.RowStyle.FlatWithRadio.defaultLight, + embeddedViewDisplaysMandateText = false, + ) + awaitItem().run { + assertThat(this).isNotNull() + assertThat(this?.mandate).isNull() + } + savedStateHandle[MANDATE_KEY_EMBEDDED_CONTENT] = "Hi".resolvableString + ensureAllEventsConsumed() // Updating the mandate shouldn't emit any more events. + } + } + @Test fun `initializing embeddedContentHelper with paymentMethodMetadata emits correct initial event`() = testScenario( setup = { @@ -72,7 +92,8 @@ internal class DefaultEmbeddedContentHelperTest { STATE_KEY_EMBEDDED_CONTENT, DefaultEmbeddedContentHelper.State( PaymentMethodMetadataFactory.create(), - Embedded.RowStyle.FloatingButton.default + Embedded.RowStyle.FloatingButton.default, + embeddedViewDisplaysMandateText = true, ) ) } @@ -89,7 +110,8 @@ internal class DefaultEmbeddedContentHelperTest { STATE_KEY_EMBEDDED_CONTENT, DefaultEmbeddedContentHelper.State( PaymentMethodMetadataFactory.create(), - Embedded.RowStyle.FloatingButton.default + Embedded.RowStyle.FloatingButton.default, + embeddedViewDisplaysMandateText = true ) ) set(MANDATE_KEY_EMBEDDED_CONTENT, "Hi".resolvableString) diff --git a/paymentsheet/src/test/java/com/stripe/android/paymentelement/embedded/FakeEmbeddedContentHelper.kt b/paymentsheet/src/test/java/com/stripe/android/paymentelement/embedded/FakeEmbeddedContentHelper.kt index a798a44a880..96bd337665e 100644 --- a/paymentsheet/src/test/java/com/stripe/android/paymentelement/embedded/FakeEmbeddedContentHelper.kt +++ b/paymentsheet/src/test/java/com/stripe/android/paymentelement/embedded/FakeEmbeddedContentHelper.kt @@ -14,8 +14,18 @@ internal class FakeEmbeddedContentHelper( private val _dataLoadedTurbine = Turbine() val dataLoadedTurbine: ReceiveTurbine = _dataLoadedTurbine - override fun dataLoaded(paymentMethodMetadata: PaymentMethodMetadata, rowStyle: Embedded.RowStyle) { - _dataLoadedTurbine.add(DefaultEmbeddedContentHelper.State(paymentMethodMetadata, rowStyle)) + override fun dataLoaded( + paymentMethodMetadata: PaymentMethodMetadata, + rowStyle: Embedded.RowStyle, + embeddedViewDisplaysMandateText: Boolean, + ) { + _dataLoadedTurbine.add( + DefaultEmbeddedContentHelper.State( + paymentMethodMetadata = paymentMethodMetadata, + rowStyle = rowStyle, + embeddedViewDisplaysMandateText = embeddedViewDisplaysMandateText, + ) + ) } fun validate() {