Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ import com.stripe.android.core.ApiKeyValidator
import dev.drewhamilton.poko.Poko
import kotlinx.parcelize.Parcelize

/**
* Creates a [PaymentConfiguration] for per-instance use (e.g. passing to
* [EmbeddedPaymentElement.Configuration.Builder.paymentConfiguration]).
*
* Unlike [PaymentConfiguration.init], this constructor does not write to SharedPreferences
* and does not affect the global singleton returned by [PaymentConfiguration.getInstance].
*/
@Parcelize
@Poko
class PaymentConfiguration
Expand All @@ -17,6 +24,20 @@ constructor(
val stripeAccountId: String? = null
) : Parcelable {

class Builder(private val publishableKey: String) {
internal var stripeAccountId: String? = null
private set

fun stripeAccountId(stripeAccountId: String?) = apply {
this.stripeAccountId = stripeAccountId
}

fun build() = PaymentConfiguration(
publishableKey = publishableKey,
stripeAccountId = stripeAccountId
)
}

init {
ApiKeyValidator.get().requireValid(publishableKey)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.stripe.android.payments.core.injection

import android.content.Context
import com.stripe.android.PaymentConfiguration
import javax.inject.Inject
import javax.inject.Singleton

/**
* Mutable holder that provides the [PaymentConfiguration] for a single Dagger component scope.
*
* When [paymentConfiguration] is null (the default), [get] falls back to
* [PaymentConfiguration.getInstance] — the same global singleton the SDK has always used.
* Setting a non-null value overrides the singleton for all network requests bound to this
* component (i.e., this [EmbeddedPaymentElement] instance).
*/
@Singleton
class PaymentConfigurationHolder @Inject constructor(
private val context: Context,
) {
@Volatile
var paymentConfiguration: PaymentConfiguration? = null

fun get(): PaymentConfiguration =
paymentConfiguration ?: PaymentConfiguration.getInstance(context)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.stripe.android.payments.core.injection

import android.content.Context
import androidx.annotation.RestrictTo
import com.stripe.android.PaymentConfiguration
import com.stripe.android.core.injection.PUBLISHABLE_KEY
Expand All @@ -14,8 +13,10 @@ import javax.inject.Provider
@Module
class PaymentConfigurationModule {
@Provides
fun providePaymentConfiguration(context: Context): PaymentConfiguration {
return PaymentConfiguration.getInstance(context)
fun providePaymentConfiguration(
holder: PaymentConfigurationHolder,
): PaymentConfiguration {
return holder.get()
}

@Provides
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelStoreOwner
import com.stripe.android.ExperimentalAllowsRemovalOfLastSavedPaymentMethodApi
import com.stripe.android.PaymentConfiguration
import com.stripe.android.SharedPaymentTokenSessionPreview
import com.stripe.android.payments.core.injection.PaymentConfigurationHolder
import com.stripe.android.checkout.Checkout
import com.stripe.android.checkout.CheckoutConfigurationMerger
import com.stripe.android.checkout.CheckoutInstances
Expand Down Expand Up @@ -59,6 +61,7 @@ class EmbeddedPaymentElement @Inject internal constructor(
paymentOptionDisplayDataHolder: PaymentOptionDisplayDataHolder,
private val configurationCoordinator: EmbeddedConfigurationCoordinator,
stateHelper: EmbeddedStateHelper,
private val paymentConfigurationHolder: PaymentConfigurationHolder,
) {

/**
Expand All @@ -85,6 +88,7 @@ class EmbeddedPaymentElement @Inject internal constructor(
intentConfiguration: PaymentSheet.IntentConfiguration,
configuration: Configuration,
): ConfigureResult {
paymentConfigurationHolder.paymentConfiguration = configuration.paymentConfiguration
val initializationMode = PaymentElementLoader.InitializationMode.DeferredIntent(intentConfiguration)
return configurationCoordinator.configure(configuration, initializationMode)
}
Expand All @@ -102,6 +106,7 @@ class EmbeddedPaymentElement @Inject internal constructor(
checkout: Checkout,
configuration: Configuration,
): ConfigureResult {
paymentConfigurationHolder.paymentConfiguration = configuration.paymentConfiguration
CheckoutInstances.ensureNoMutationInFlight(checkout.internalState.key)
return configurationCoordinator.configure(
configuration = CheckoutConfigurationMerger.EmbeddedConfiguration(configuration)
Expand Down Expand Up @@ -305,6 +310,7 @@ class EmbeddedPaymentElement @Inject internal constructor(
internal val termsDisplay: Map<PaymentMethod.Type, TermsDisplay> = emptyMap(),
internal val opensCardScannerAutomatically: Boolean = ConfigurationDefaults.opensCardScannerAutomatically,
internal val userOverrideCountry: String? = ConfigurationDefaults.userOverrideCountry,
internal val paymentConfiguration: PaymentConfiguration? = null,
) : Parcelable {
@Suppress("TooManyFunctions")
class Builder(
Expand Down Expand Up @@ -342,6 +348,7 @@ class EmbeddedPaymentElement @Inject internal constructor(
private var opensCardScannerAutomatically: Boolean =
ConfigurationDefaults.opensCardScannerAutomatically
private var userOverrideCountry: String? = ConfigurationDefaults.userOverrideCountry
private var paymentConfiguration: PaymentConfiguration? = null

/**
* If set, the customer can select a previously saved payment method.
Expand Down Expand Up @@ -578,6 +585,16 @@ class EmbeddedPaymentElement @Inject internal constructor(
this.userOverrideCountry = userOverrideCountry
}

/**
* An optional [PaymentConfiguration] for this payment session. When set, overrides the
* global [PaymentConfiguration] singleton for all network requests made by this
* [EmbeddedPaymentElement] instance. When not set, defaults to the value from
* [PaymentConfiguration.getInstance].
*/
fun paymentConfiguration(paymentConfiguration: PaymentConfiguration) = apply {
this.paymentConfiguration = paymentConfiguration
}

fun build() = Configuration(
merchantDisplayName = merchantDisplayName,
customer = customer,
Expand All @@ -602,6 +619,7 @@ class EmbeddedPaymentElement @Inject internal constructor(
termsDisplay = termsDisplay,
opensCardScannerAutomatically = opensCardScannerAutomatically,
userOverrideCountry = userOverrideCountry,
paymentConfiguration = paymentConfiguration,
)
}

Expand Down Expand Up @@ -633,6 +651,7 @@ class EmbeddedPaymentElement @Inject internal constructor(
.userOverrideCountry(userOverrideCountry)
.apply {
primaryButtonLabel?.let { primaryButtonLabel(it) }
paymentConfiguration?.let { paymentConfiguration(it) }
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@ internal class DefaultEmbeddedConfigurationHandler @Inject constructor(
return coalescingOrchestrator.get()
}

// TODO: paymentConfiguration is not included in the Arguments cache key (CommonConfiguration
// doesn't carry it). Calling configure() twice with the same initializationMode/configuration
// but a different PaymentConfiguration will return the cached result without re-loading.
// Fix by adding paymentConfiguration: PaymentConfiguration? to Arguments.
@Parcelize
data class Arguments(
val initializationMode: PaymentElementLoader.InitializationMode,
Expand Down
Loading