Skip to content
Merged
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 @@ -5,7 +5,7 @@ import com.stripe.android.common.model.PaymentMethodRemovePermission
internal data class CustomerPermissions(
val removePaymentMethod: PaymentMethodRemovePermission,
val canRemoveLastPaymentMethod: Boolean,
val canUpdateFullPaymentMethodDetails: Boolean
val canUpdateCardExpiryAndBillingDetails: Boolean
) {
val canRemovePaymentMethods: Boolean
get() = removePaymentMethod == PaymentMethodRemovePermission.Full ||
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ internal class CustomerSheetViewModel(
permissions = CustomerPermissions(
removePaymentMethod = PaymentMethodRemovePermission.None,
canRemoveLastPaymentMethod = false,
canUpdateFullPaymentMethodDetails = false,
canUpdateCardExpiryAndBillingDetails = false,
),
metadata = null,
)
Expand Down Expand Up @@ -211,7 +211,6 @@ internal class CustomerSheetViewModel(
isEditing = userCanEditAndIsEditing,
isProcessing = selectionConfirmationState.isConfirming,
errorMessage = selectionConfirmationState.error,
isCbcEligible = customerState.cbcEligibility is CardBrandChoiceEligibility.Eligible,
canEdit = customerState.canEdit,
mandateText = paymentSelection?.mandateText(
merchantName = configuration.merchantDisplayName,
Expand Down Expand Up @@ -570,7 +569,8 @@ internal class CustomerSheetViewModel(
updatePaymentMethodInteractor = DefaultUpdatePaymentMethodInteractor(
isLiveMode = isLiveMode,
canRemove = customerState.canRemove,
canUpdateFullPaymentMethodDetails = customerState.canUpdateFullPaymentMethodDetails,
canUpdateCardExpiryAndBillingDetails = customerState.canUpdateCardExpiryAndBillingDetails,
canChangeCbc = customerState.cbcEligibility is CardBrandChoiceEligibility.Eligible,
displayableSavedPaymentMethod = paymentMethod,
addressCollectionMode = configuration.billingDetailsCollectionConfiguration.address,
allowedBillingCountries =
Expand Down Expand Up @@ -1274,12 +1274,15 @@ internal class CustomerSheetViewModel(
else -> permissions.canRemovePaymentMethods
}

val canUpdateFullPaymentMethodDetails = permissions.canUpdateFullPaymentMethodDetails

val canUpdateCardExpiryAndBillingDetails = permissions.canUpdateCardExpiryAndBillingDetails
val cbcEligibility = metadata?.cbcEligibility ?: CardBrandChoiceEligibility.Ineligible

val canEdit = canRemove || paymentMethods.any { method ->
isModifiable(method, cbcEligibility, canUpdateFullPaymentMethodDetails)
isModifiable(
paymentMethod = method,
canUpdateCardExpiryAndBillingDetails = canUpdateCardExpiryAndBillingDetails,
canChangeCbc = cbcEligibility is CardBrandChoiceEligibility.Eligible,
)
}

val canShowSavedPaymentMethods = paymentMethods.isNotEmpty() || shouldShowGooglePay(metadata)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import com.stripe.android.paymentsheet.ui.PaymentSheetTopBarState
import com.stripe.android.paymentsheet.ui.PaymentSheetTopBarStateFactory
import com.stripe.android.paymentsheet.ui.PrimaryButton
import com.stripe.android.paymentsheet.ui.UpdatePaymentMethodInteractor
import com.stripe.android.ui.core.cbc.CardBrandChoiceEligibility
import com.stripe.android.uicore.elements.FormElement

internal sealed class CustomerSheetViewState(
Expand Down Expand Up @@ -67,7 +66,6 @@ internal sealed class CustomerSheetViewState(
val canRemovePaymentMethods: Boolean,
val errorMessage: String? = null,
val mandateText: ResolvableString? = null,
val isCbcEligible: Boolean,
) : CustomerSheetViewState(
isLiveMode = isLiveMode,
isProcessing = isProcessing,
Expand Down Expand Up @@ -143,11 +141,11 @@ internal sealed class CustomerSheetViewState(

internal fun isModifiable(
paymentMethod: PaymentMethod,
cbcEligibility: CardBrandChoiceEligibility,
canUpdateFullPaymentMethodDetails: Boolean
canUpdateCardExpiryAndBillingDetails: Boolean,
canChangeCbc: Boolean,
): Boolean {
return paymentMethod.isModifiable(
canUpdateFullPaymentMethodDetails = canUpdateFullPaymentMethodDetails,
isCbcEligible = cbcEligibility is CardBrandChoiceEligibility.Eligible,
canUpdateCardExpiryAndBillingDetails = canUpdateCardExpiryAndBillingDetails,
canChangeCbc = canChangeCbc,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ internal class CustomerAdapterDataSource @Inject constructor(
// Always `Full` for `Adapter` use case
removePaymentMethod = PaymentMethodRemovePermission.Full,
// Payment Method Update is customer sessions-only feature, so this value is unused.
canUpdateFullPaymentMethodDetails = false,
canUpdateCardExpiryAndBillingDetails = false,
),
// Default payment methods are a customer sessions-only feature, so this value is unused.
defaultPaymentMethodId = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ internal class CustomerSessionInitializationDataSource @Inject constructor(
PaymentMethodRemovePermission.None
},
// Should always be enabled when using `customer_session`
canUpdateFullPaymentMethodDetails = true
canUpdateCardExpiryAndBillingDetails = true
),
defaultPaymentMethodId = customer.defaultPaymentMethod,
customerId = customer.session.customerId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,6 @@ internal fun SelectPaymentMethod(
linkBrand = LinkBrand.Link,
currentSelection = viewState.paymentSelection,
nameProvider = paymentMethodNameProvider,
isCbcEligible = viewState.isCbcEligible,
defaultPaymentMethodId = null
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ internal sealed class CustomerMetadata : Parcelable {
abstract val removePaymentMethod: PaymentMethodRemovePermission
abstract val saveConsent: PaymentMethodSaveConsentBehavior
abstract val canRemoveLastPaymentMethod: Boolean
abstract val canUpdateFullPaymentMethodDetails: Boolean
abstract val canUpdateCardExpiryAndBillingDetails: Boolean
abstract val canUpdateCardBrandChoice: Boolean

val canRemovePaymentMethods: Boolean
get() = removePaymentMethod == PaymentMethodRemovePermission.Full ||
Expand All @@ -27,8 +28,10 @@ internal sealed class CustomerMetadata : Parcelable {
override val removePaymentMethod: PaymentMethodRemovePermission,
override val saveConsent: PaymentMethodSaveConsentBehavior,
override val canRemoveLastPaymentMethod: Boolean,
override val canUpdateFullPaymentMethodDetails: Boolean,
) : CustomerMetadata()
override val canUpdateCardExpiryAndBillingDetails: Boolean,
) : CustomerMetadata() {
override val canUpdateCardBrandChoice: Boolean get() = true
}

@Parcelize
data class CustomerSession(
Expand All @@ -39,8 +42,10 @@ internal sealed class CustomerMetadata : Parcelable {
override val removePaymentMethod: PaymentMethodRemovePermission,
override val saveConsent: PaymentMethodSaveConsentBehavior,
override val canRemoveLastPaymentMethod: Boolean,
override val canUpdateFullPaymentMethodDetails: Boolean,
) : CustomerMetadata()
override val canUpdateCardExpiryAndBillingDetails: Boolean,
) : CustomerMetadata() {
override val canUpdateCardBrandChoice: Boolean get() = true
}

@Parcelize
data class CheckoutSession(
Expand All @@ -55,8 +60,11 @@ internal sealed class CustomerMetadata : Parcelable {
// When removal is permitted, CheckoutSession doesn't restrict removing the last one.
override val canRemoveLastPaymentMethod: Boolean get() = true

// CheckoutSession doesn't support updating full payment method details.
override val canUpdateFullPaymentMethodDetails: Boolean get() = false
// CheckoutSession card detail updates are wired in a separate change.
override val canUpdateCardExpiryAndBillingDetails: Boolean get() = false

// CheckoutSession doesn't support updating preferred card networks.
override val canUpdateCardBrandChoice: Boolean get() = false
}

companion object {
Expand Down Expand Up @@ -112,7 +120,7 @@ internal sealed class CustomerMetadata : Parcelable {
saveConsent = saveConsent,
canRemoveLastPaymentMethod = canRemoveLastPaymentMethod,
// Should always be enabled when using `customer_session`
canUpdateFullPaymentMethodDetails = true,
canUpdateCardExpiryAndBillingDetails = true,
)
}

Expand Down Expand Up @@ -143,7 +151,7 @@ internal sealed class CustomerMetadata : Parcelable {
* sessions.
*/
canRemoveLastPaymentMethod = configuration.allowsRemovalOfLastSavedPaymentMethod,
canUpdateFullPaymentMethodDetails = false,
canUpdateCardExpiryAndBillingDetails = false,
)
}

Expand All @@ -158,8 +166,8 @@ internal sealed class CustomerMetadata : Parcelable {
val removePaymentMethod = customerSheetSession.permissions.removePaymentMethod
val saveConsent = customerSheetSession.paymentMethodSaveConsentBehavior
val canRemoveLastPaymentMethod = configuration.allowsRemovalOfLastSavedPaymentMethod
val canUpdateFullPaymentMethodDetails =
customerSheetSession.permissions.canUpdateFullPaymentMethodDetails
val canUpdateCardExpiryAndBillingDetails =
customerSheetSession.permissions.canUpdateCardExpiryAndBillingDetails

return if (customerSessionClientSecret != null) {
CustomerSession(
Expand All @@ -170,7 +178,7 @@ internal sealed class CustomerMetadata : Parcelable {
removePaymentMethod = removePaymentMethod,
saveConsent = saveConsent,
canRemoveLastPaymentMethod = canRemoveLastPaymentMethod,
canUpdateFullPaymentMethodDetails = canUpdateFullPaymentMethodDetails,
canUpdateCardExpiryAndBillingDetails = canUpdateCardExpiryAndBillingDetails,
)
} else {
LegacyEphemeralKey(
Expand All @@ -180,7 +188,7 @@ internal sealed class CustomerMetadata : Parcelable {
removePaymentMethod = removePaymentMethod,
saveConsent = saveConsent,
canRemoveLastPaymentMethod = canRemoveLastPaymentMethod,
canUpdateFullPaymentMethodDetails = canUpdateFullPaymentMethodDetails,
canUpdateCardExpiryAndBillingDetails = canUpdateCardExpiryAndBillingDetails,
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,8 @@ internal interface EmbeddedCommonModule {
return DefaultCustomerStateHolder(
savedStateHandle = savedStateHandle,
selection = selectionHolder.selection,
customerMetadata = customerMetadata
customerMetadata = customerMetadata,
paymentMethodMetadataFlow = paymentMethodMetadataFlow,
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,8 @@ internal class DefaultEmbeddedContentHelper @Inject constructor(
paymentMethods = customerStateHolder.paymentMethods,
mostRecentlySelectedSavedPaymentMethod = customerStateHolder.mostRecentlySelectedSavedPaymentMethod,
canRemove = customerStateHolder.canRemove,
canUpdateFullPaymentMethodDetails = customerStateHolder.canUpdateFullPaymentMethodDetails,
canUpdateCardExpiryAndBillingDetails = customerStateHolder.canUpdateCardExpiryAndBillingDetails,
canChangeCbc = customerStateHolder.canChangeCbc,
walletsState = walletsState,
updateSelection = { updatedSelection, requiresConfirmation ->
setSelection(updatedSelection)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ internal class DefaultEmbeddedUpdateScreenInteractorFactory @Inject constructor(
return DefaultUpdatePaymentMethodInteractor(
isLiveMode = paymentMethodMetadata.stripeIntent.isLiveMode,
canRemove = customerStateHolder.canRemove.value,
canUpdateFullPaymentMethodDetails = customerStateHolder.canUpdateFullPaymentMethodDetails.value,
canUpdateCardExpiryAndBillingDetails = customerStateHolder.canUpdateCardExpiryAndBillingDetails.value,
canChangeCbc = customerStateHolder.canChangeCbc.value,
displayableSavedPaymentMethod = displayableSavedPaymentMethod,
cardBrandFilter = paymentMethodMetadata.cardBrandFilter,
addressCollectionMode = paymentMethodMetadata.billingDetailsCollectionConfiguration.address,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import com.stripe.android.lpmfoundations.paymentmethod.PaymentMethodMetadata
import com.stripe.android.paymentelement.embedded.sheet.EmbeddedNavigator
import com.stripe.android.paymentsheet.CustomerStateHolder
import com.stripe.android.paymentsheet.DisplayableSavedPaymentMethod
import com.stripe.android.ui.core.cbc.CardBrandChoiceEligibility
import javax.inject.Inject

internal class InitialManageScreenFactory @Inject constructor(
Expand All @@ -24,7 +23,6 @@ internal class InitialManageScreenFactory @Inject constructor(
val displayableSavedPaymentMethod = DisplayableSavedPaymentMethod.create(
displayName = displayName,
paymentMethod = paymentMethod,
isCbcEligible = paymentMethodMetadata.cbcEligibility is CardBrandChoiceEligibility.Eligible,
)
EmbeddedNavigator.Screen.ManageUpdate(
interactor = updateScreenInteractorFactory.createUpdateScreenInteractor(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ internal interface CustomerStateHolder {

val canRemove: StateFlow<Boolean>

val canUpdateFullPaymentMethodDetails: StateFlow<Boolean>
val canUpdateCardExpiryAndBillingDetails: StateFlow<Boolean>

val canChangeCbc: StateFlow<Boolean>

fun setCustomerState(customerState: CustomerState?)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,20 @@ package com.stripe.android.paymentsheet

import androidx.lifecycle.SavedStateHandle
import com.stripe.android.lpmfoundations.paymentmethod.CustomerMetadata
import com.stripe.android.lpmfoundations.paymentmethod.PaymentMethodMetadata
import com.stripe.android.model.PaymentMethod
import com.stripe.android.paymentsheet.CustomerStateHolder.Companion.SAVED_CUSTOMER
import com.stripe.android.paymentsheet.model.PaymentSelection
import com.stripe.android.paymentsheet.state.CustomerState
import com.stripe.android.paymentsheet.viewmodels.BaseSheetViewModel
import com.stripe.android.ui.core.cbc.CardBrandChoiceEligibility
import com.stripe.android.uicore.utils.combineAsStateFlow
import com.stripe.android.uicore.utils.mapAsStateFlow
import kotlinx.coroutines.flow.StateFlow

internal class DefaultCustomerStateHolder(
private val customerMetadata: StateFlow<CustomerMetadata?>,
private val paymentMethodMetadataFlow: StateFlow<PaymentMethodMetadata?>,
private val savedStateHandle: SavedStateHandle,
private val selection: StateFlow<PaymentSelection?>,
) : CustomerStateHolder {
Expand Down Expand Up @@ -48,8 +51,17 @@ internal class DefaultCustomerStateHolder(
} ?: false
}

override val canUpdateFullPaymentMethodDetails: StateFlow<Boolean> = customerMetadata.mapAsStateFlow {
it?.canUpdateFullPaymentMethodDetails ?: false
override val canUpdateCardExpiryAndBillingDetails: StateFlow<Boolean> = customerMetadata.mapAsStateFlow {
it?.canUpdateCardExpiryAndBillingDetails ?: false
}

override val canChangeCbc: StateFlow<Boolean> = combineAsStateFlow(
customerMetadata,
paymentMethodMetadataFlow,
) { metadata, pmMetadata ->
val canUpdateBrandChoice = metadata?.canUpdateCardBrandChoice ?: false
val isCbcEligible = pmMetadata?.cbcEligibility is CardBrandChoiceEligibility.Eligible
canUpdateBrandChoice && isCbcEligible
}

override fun setCustomerState(customerState: CustomerState?) {
Expand Down Expand Up @@ -112,7 +124,8 @@ internal class DefaultCustomerStateHolder(
selection = viewModel.selection,
customerMetadata = viewModel.paymentMethodMetadata.mapAsStateFlow {
it?.customerMetadata
}
},
paymentMethodMetadataFlow = viewModel.paymentMethodMetadata,
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ internal data class DisplayableSavedPaymentMethod private constructor(
val displayName: ResolvableString,
val paymentMethod: PaymentMethod,
val savedPaymentMethod: SavedPaymentMethod,
val isCbcEligible: Boolean = false,
val shouldShowDefaultBadge: Boolean = false,
) {
val isCard: Boolean
Expand Down Expand Up @@ -101,7 +100,6 @@ internal data class DisplayableSavedPaymentMethod private constructor(
fun create(
displayName: ResolvableString,
paymentMethod: PaymentMethod,
isCbcEligible: Boolean = false,
shouldShowDefaultBadge: Boolean = false,
): DisplayableSavedPaymentMethod {
val savedPaymentMethod = when (paymentMethod.type) {
Expand Down Expand Up @@ -130,7 +128,6 @@ internal data class DisplayableSavedPaymentMethod private constructor(
displayName = displayName,
paymentMethod = paymentMethod,
savedPaymentMethod = savedPaymentMethod ?: SavedPaymentMethod.Unexpected,
isCbcEligible = isCbcEligible,
shouldShowDefaultBadge = shouldShowDefaultBadge,
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ internal object PaymentOptionsStateFactory {
showLink: Boolean,
linkBrand: LinkBrand,
nameProvider: (PaymentMethodCode?) -> ResolvableString,
isCbcEligible: Boolean,
defaultPaymentMethodId: String?,
): List<PaymentOptionsItem> {
return listOfNotNull(
Expand All @@ -31,7 +30,6 @@ internal object PaymentOptionsStateFactory {
DisplayableSavedPaymentMethod.create(
displayName = nameProvider(it.type?.code),
paymentMethod = it,
isCbcEligible = isCbcEligible,
shouldShowDefaultBadge = it.id == defaultPaymentMethodId,
),
)
Expand All @@ -54,7 +52,6 @@ internal object PaymentOptionsStateFactory {
linkBrand: LinkBrand,
currentSelection: PaymentSelection?,
nameProvider: (PaymentMethodCode?) -> ResolvableString,
isCbcEligible: Boolean,
defaultPaymentMethodId: String?
): PaymentOptionsState {
val items = createPaymentOptionsList(
Expand All @@ -63,7 +60,6 @@ internal object PaymentOptionsStateFactory {
showLink = showLink,
linkBrand = linkBrand,
nameProvider = nameProvider,
isCbcEligible = isCbcEligible,
defaultPaymentMethodId = defaultPaymentMethodId
)

Expand Down
Loading
Loading