Skip to content

Commit

Permalink
Update containsVolatileDifferences. (#9933)
Browse files Browse the repository at this point in the history
  • Loading branch information
jaynewstrom-stripe authored Jan 17, 2025
1 parent 6fa293e commit 1d4d941
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 153 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,26 @@ private fun String.isEKClientSecretValid(): Boolean {
}

private const val EK_CLIENT_SECRET_VALID_REGEX_PATTERN = "^ek_[^_](.)+$"

internal fun CommonConfiguration.containsVolatileDifferences(
other: CommonConfiguration
): Boolean {
return toVolatileConfiguration() != other.toVolatileConfiguration()
}

/**
* Creates a subset of the [CommonConfiguration] values that affect the behavior of [PaymentSelection].
*/
private fun CommonConfiguration.toVolatileConfiguration(): VolatileCommonConfiguration {
return VolatileCommonConfiguration(
defaultBillingDetails = defaultBillingDetails,
billingDetailsCollectionConfiguration = billingDetailsCollectionConfiguration,
cardBrandAcceptance = cardBrandAcceptance,
)
}

private data class VolatileCommonConfiguration(
val defaultBillingDetails: PaymentSheet.BillingDetails?,
val billingDetailsCollectionConfiguration: PaymentSheet.BillingDetailsCollectionConfiguration,
val cardBrandAcceptance: PaymentSheet.CardBrandAcceptance,
)
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,12 @@ import androidx.compose.material.darkColors
import androidx.compose.material.lightColors
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.sp
import com.stripe.android.model.CardBrand
import com.stripe.android.paymentsheet.addresselement.AddressDetails
import com.stripe.android.uicore.PrimaryButtonColors
import com.stripe.android.uicore.PrimaryButtonShape
import com.stripe.android.uicore.PrimaryButtonTypography
import com.stripe.android.uicore.StripeTheme
import com.stripe.android.uicore.StripeThemeDefaults

internal fun PaymentSheet.Configuration.containsVolatileDifferences(
other: PaymentSheet.Configuration
): Boolean {
return toVolatileConfiguration() != other.toVolatileConfiguration()
}

internal fun PaymentSheet.Appearance.parseAppearance() {
StripeTheme.colorsLightMutable = StripeThemeDefaults.colorsLight.copy(
component = Color(colorsLight.component),
Expand Down Expand Up @@ -88,57 +80,3 @@ internal fun PaymentSheet.Appearance.parseAppearance() {
)
)
}

/**
* Creates a subset of the [PaymentSheet.Configuration] of values that affect the functional behavior of
* [PaymentSheet]. The items not included mainly affect how [PaymentSheet] will look but not affect what
* payment options are available to the customer:
* - UI elements in [PaymentSheet.GooglePayConfiguration]:
* - [PaymentSheet.GooglePayConfiguration.amount]
* - [PaymentSheet.GooglePayConfiguration.label]
* - [PaymentSheet.GooglePayConfiguration.buttonType]
* - [PaymentSheet.Configuration.merchantDisplayName]
* - [PaymentSheet.Configuration.primaryButtonColor]
* - [PaymentSheet.Configuration.appearance]
* - [PaymentSheet.Configuration.primaryButtonLabel]
*/
private fun PaymentSheet.Configuration.toVolatileConfiguration(): VolatilePaymentSheetConfiguration {
return VolatilePaymentSheetConfiguration(
customer = customer,
googlePay = googlePay?.toVolatileConfiguration(),
defaultBillingDetails = defaultBillingDetails,
shippingDetails = shippingDetails,
allowsDelayedPaymentMethods = allowsDelayedPaymentMethods,
allowsPaymentMethodsRequiringShippingAddress = allowsPaymentMethodsRequiringShippingAddress,
billingDetailsCollectionConfiguration = billingDetailsCollectionConfiguration,
preferredNetworks = preferredNetworks,
allowsRemovalOfLastSavedPaymentMethod = allowsRemovalOfLastSavedPaymentMethod,
)
}

private fun PaymentSheet.GooglePayConfiguration.toVolatileConfiguration():
VolatilePaymentSheetConfiguration.GooglePayConfiguration {
return VolatilePaymentSheetConfiguration.GooglePayConfiguration(
environment = environment,
countryCode = countryCode,
currencyCode = currencyCode,
)
}

private data class VolatilePaymentSheetConfiguration(
val customer: PaymentSheet.CustomerConfiguration?,
val googlePay: GooglePayConfiguration?,
val defaultBillingDetails: PaymentSheet.BillingDetails?,
val shippingDetails: AddressDetails?,
val allowsDelayedPaymentMethods: Boolean,
val allowsPaymentMethodsRequiringShippingAddress: Boolean,
val billingDetailsCollectionConfiguration: PaymentSheet.BillingDetailsCollectionConfiguration,
val preferredNetworks: List<CardBrand>,
val allowsRemovalOfLastSavedPaymentMethod: Boolean,
) {
data class GooglePayConfiguration(
val environment: PaymentSheet.GooglePayConfiguration.Environment,
val countryCode: String,
val currencyCode: String? = null,
)
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package com.stripe.android.paymentsheet.flowcontroller

import com.stripe.android.common.model.asCommonConfiguration
import com.stripe.android.common.model.containsVolatileDifferences
import com.stripe.android.lpmfoundations.paymentmethod.PaymentMethodMetadata
import com.stripe.android.paymentsheet.PaymentSheet
import com.stripe.android.paymentsheet.containsVolatileDifferences
import com.stripe.android.paymentsheet.model.PaymentSelection
import com.stripe.android.paymentsheet.state.PaymentSheetState
import javax.inject.Inject
Expand All @@ -26,8 +27,8 @@ internal class DefaultPaymentSelectionUpdater @Inject constructor() : PaymentSel
): PaymentSelection? {
return currentSelection?.takeIf { selection ->
canUseSelection(selection, newState) && previousConfig?.let { previousConfig ->
!previousConfig.containsVolatileDifferences(newConfig)
} ?: true
!previousConfig.asCommonConfiguration().containsVolatileDifferences(newConfig.asCommonConfiguration())
} != false
} ?: newState.paymentSelection
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.stripe.android.common.model

import com.stripe.android.ExperimentalCardBrandFilteringApi
import com.stripe.android.model.CardBrand
import com.stripe.android.paymentsheet.PaymentSheet
import com.stripe.android.paymentsheet.addresselement.AddressDetails

internal object CommonConfigurationFactory {
@OptIn(ExperimentalCardBrandFilteringApi::class)
fun create(
merchantDisplayName: String = "Example, Inc.",
customer: PaymentSheet.CustomerConfiguration? = null,
googlePay: PaymentSheet.GooglePayConfiguration? = null,
defaultBillingDetails: PaymentSheet.BillingDetails? = null,
shippingDetails: AddressDetails? = null,
allowsDelayedPaymentMethods: Boolean = true,
allowsPaymentMethodsRequiringShippingAddress: Boolean = true,
billingDetailsCollectionConfiguration: PaymentSheet.BillingDetailsCollectionConfiguration =
PaymentSheet.BillingDetailsCollectionConfiguration(),
preferredNetworks: List<CardBrand> = emptyList(),
allowsRemovalOfLastSavedPaymentMethod: Boolean = true,
paymentMethodOrder: List<String> = emptyList(),
externalPaymentMethods: List<String> = emptyList(),
cardBrandAcceptance: PaymentSheet.CardBrandAcceptance = PaymentSheet.CardBrandAcceptance.all(),
): CommonConfiguration = CommonConfiguration(
merchantDisplayName = merchantDisplayName,
customer = customer,
googlePay = googlePay,
defaultBillingDetails = defaultBillingDetails,
shippingDetails = shippingDetails,
allowsDelayedPaymentMethods = allowsDelayedPaymentMethods,
allowsPaymentMethodsRequiringShippingAddress = allowsPaymentMethodsRequiringShippingAddress,
billingDetailsCollectionConfiguration = billingDetailsCollectionConfiguration,
preferredNetworks = preferredNetworks,
allowsRemovalOfLastSavedPaymentMethod = allowsRemovalOfLastSavedPaymentMethod,
paymentMethodOrder = paymentMethodOrder,
externalPaymentMethods = externalPaymentMethods,
cardBrandAcceptance = cardBrandAcceptance,
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.stripe.android.common.model

import com.google.common.truth.Truth.assertThat
import com.stripe.android.ExperimentalCardBrandFilteringApi
import com.stripe.android.paymentsheet.PaymentSheet
import org.junit.Test

@OptIn(ExperimentalCardBrandFilteringApi::class)
internal class CommonConfigurationTest {
private val configuration = CommonConfigurationFactory.create()

@Test
fun `'containVolatileDifferences' should return false when no volatile differences are found`() {
val changedConfiguration = configuration.copy(
merchantDisplayName = "New merchant, Inc.",
)

assertThat(configuration.containsVolatileDifferences(changedConfiguration)).isFalse()
}

@Test
fun `'containVolatileDifferences' should return true when volatile differences are found`() {
val configWithCardBrandAcceptanceChanges = configuration.copy(
cardBrandAcceptance = PaymentSheet.CardBrandAcceptance.disallowed(
listOf(PaymentSheet.CardBrandAcceptance.BrandCategory.Visa)
)
)

assertThat(configuration.containsVolatileDifferences(configWithCardBrandAcceptanceChanges)).isTrue()

val configWithBillingDetailsChanges = configuration.copy(
defaultBillingDetails = PaymentSheet.BillingDetails(
name = "Jenny Richards",
),
)

assertThat(configuration.containsVolatileDifferences(configWithBillingDetailsChanges)).isTrue()

val configWithBillingConfigChanges = configuration.copy(
billingDetailsCollectionConfiguration = PaymentSheet.BillingDetailsCollectionConfiguration(
name = PaymentSheet.BillingDetailsCollectionConfiguration.CollectionMode.Never,
),
)

assertThat(configuration.containsVolatileDifferences(configWithBillingConfigChanges)).isTrue()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package com.stripe.android.paymentsheet

import android.content.res.ColorStateList
import android.graphics.Color
import com.google.common.truth.Truth.assertThat
import com.stripe.android.common.model.CommonConfiguration
import com.stripe.android.common.model.asCommonConfiguration
import com.stripe.android.paymentsheet.addresselement.AddressDetails
Expand All @@ -11,89 +10,6 @@ import java.lang.IllegalArgumentException
import kotlin.test.assertFailsWith

class PaymentSheetConfigurationKtxTest {
@Test
fun `'containVolatileDifferences' should return false when no volatile differences are found`() {
val changedConfiguration = configuration.copy(
merchantDisplayName = "New merchant, Inc.",
primaryButtonColor = ColorStateList.valueOf(Color.GREEN),
googlePay = PaymentSheet.GooglePayConfiguration(
environment = PaymentSheet.GooglePayConfiguration.Environment.Test,
countryCode = "CA",
currencyCode = "CAD",
amount = 6899,
label = "New merchant, Inc.",
buttonType = PaymentSheet.GooglePayConfiguration.ButtonType.Plain,
),
primaryButtonLabel = "Pay",
)

assertThat(configuration.containsVolatileDifferences(changedConfiguration)).isFalse()
}

@Test
fun `'containVolatileDifferences' should return true when volatile differences are found`() {
val configWithGooglePayChanges = configuration.copy(
googlePay = PaymentSheet.GooglePayConfiguration(
environment = PaymentSheet.GooglePayConfiguration.Environment.Production,
countryCode = "US",
currencyCode = "USD",
amount = 6899,
label = "New merchant, Inc.",
buttonType = PaymentSheet.GooglePayConfiguration.ButtonType.Plain,
),
)

assertThat(configuration.containsVolatileDifferences(configWithGooglePayChanges)).isTrue()

val configWithBillingDetailsChanges = configuration.copy(
defaultBillingDetails = PaymentSheet.BillingDetails(
name = "Jenny Richards",
),
)

assertThat(configuration.containsVolatileDifferences(configWithBillingDetailsChanges)).isTrue()

val configWithShippingDetailsChanges = configuration.copy(
shippingDetails = AddressDetails(
name = "Jenny Richards",
),
)

assertThat(configuration.containsVolatileDifferences(configWithShippingDetailsChanges)).isTrue()

val configWithBillingConfigChanges = configuration.copy(
billingDetailsCollectionConfiguration = PaymentSheet.BillingDetailsCollectionConfiguration(
name = PaymentSheet.BillingDetailsCollectionConfiguration.CollectionMode.Never,
),
)

assertThat(configuration.containsVolatileDifferences(configWithBillingConfigChanges)).isTrue()

val configWithAllowsDelayedPaymentMethodChanges = configuration.copy(
allowsDelayedPaymentMethods = true,
)

assertThat(
configuration.containsVolatileDifferences(configWithAllowsDelayedPaymentMethodChanges)
).isTrue()

val configWithAllowsPaymentMethodsRequiringShippingAddressChanges = configuration.copy(
allowsPaymentMethodsRequiringShippingAddress = true,
)

assertThat(
configuration.containsVolatileDifferences(configWithAllowsPaymentMethodsRequiringShippingAddressChanges)
).isTrue()

val configWithAllowRemovalOfLastPaymentMethodChanges = configuration.copy(
allowsRemovalOfLastSavedPaymentMethod = false,
)

assertThat(
configuration.containsVolatileDifferences(configWithAllowRemovalOfLastPaymentMethodChanges)
).isTrue()
}

@Test
fun `'validate' should fail when ephemeral key secret is blank`() {
val configWithBlankEphemeralKeySecret = configuration.copy(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -326,10 +326,7 @@ class PaymentSelectionUpdaterTest {
val existingSelection = PaymentSelection.GooglePay

val newConfig = defaultPaymentSheetConfiguration.copy(
customer = PaymentSheet.CustomerConfiguration(
id = "id1",
ephemeralKeySecret = "ek_123",
)
defaultBillingDetails = PaymentSheet.BillingDetails(email = "hi-jay@example.com")
)
val newState = mockPaymentSheetStateWithPaymentIntent(
config = newConfig
Expand Down

0 comments on commit 1d4d941

Please sign in to comment.