From 2cf0a35c6ad1cfd020967fb8b05e125cafbb8e58 Mon Sep 17 00:00:00 2001 From: Samer Alabi Date: Mon, 20 Nov 2023 00:09:23 -0500 Subject: [PATCH 1/3] Support updating payment method in `CustomerSheet`. --- paymentsheet/api/paymentsheet.api | 1 + .../android/customersheet/CustomerAdapter.kt | 8 +++ .../customersheet/CustomerSheetViewModel.kt | 51 +++++++++++++-- .../customersheet/StripeCustomerAdapter.kt | 20 ++++++ .../customersheet/CustomerAdapterTest.kt | 65 +++++++++++++++++++ .../CustomerSheetViewModelTest.kt | 64 ++++++++++++++++++ .../customersheet/FakeCustomerAdapter.kt | 14 ++++ 7 files changed, 217 insertions(+), 6 deletions(-) diff --git a/paymentsheet/api/paymentsheet.api b/paymentsheet/api/paymentsheet.api index 558fd76713d..de364b0891c 100644 --- a/paymentsheet/api/paymentsheet.api +++ b/paymentsheet/api/paymentsheet.api @@ -15,6 +15,7 @@ public abstract interface class com/stripe/android/customersheet/CustomerAdapter public abstract fun retrieveSelectedPaymentOption (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public abstract fun setSelectedPaymentOption (Lcom/stripe/android/customersheet/CustomerAdapter$PaymentOption;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public abstract fun setupIntentClientSecretForCustomerAttach (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun updatePaymentMethod (Lcom/stripe/android/model/PaymentMethodUpdateParams;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } public final class com/stripe/android/customersheet/CustomerAdapter$Companion { diff --git a/paymentsheet/src/main/java/com/stripe/android/customersheet/CustomerAdapter.kt b/paymentsheet/src/main/java/com/stripe/android/customersheet/CustomerAdapter.kt index 5056382f629..ca947e696aa 100644 --- a/paymentsheet/src/main/java/com/stripe/android/customersheet/CustomerAdapter.kt +++ b/paymentsheet/src/main/java/com/stripe/android/customersheet/CustomerAdapter.kt @@ -3,6 +3,7 @@ package com.stripe.android.customersheet import android.content.Context import com.stripe.android.customersheet.injection.DaggerStripeCustomerAdapterComponent import com.stripe.android.model.PaymentMethod +import com.stripe.android.model.PaymentMethodUpdateParams import com.stripe.android.paymentsheet.model.PaymentSelection import com.stripe.android.paymentsheet.model.SavedSelection @@ -49,6 +50,13 @@ interface CustomerAdapter { */ suspend fun detachPaymentMethod(paymentMethodId: String): Result + /** + * Detaches the given payment method from a customer + * @param params, parameters for updating a payment method + * @return the modified [PaymentMethod]. + */ + suspend fun updatePaymentMethod(params: PaymentMethodUpdateParams): Result + /** * Saves the payment option to a data store. * @param paymentOption the [PaymentOption] to save to the data store. If null, the selected diff --git a/paymentsheet/src/main/java/com/stripe/android/customersheet/CustomerSheetViewModel.kt b/paymentsheet/src/main/java/com/stripe/android/customersheet/CustomerSheetViewModel.kt index 9e7f56e294f..fff878bb5f9 100644 --- a/paymentsheet/src/main/java/com/stripe/android/customersheet/CustomerSheetViewModel.kt +++ b/paymentsheet/src/main/java/com/stripe/android/customersheet/CustomerSheetViewModel.kt @@ -18,11 +18,13 @@ import com.stripe.android.customersheet.CustomerAdapter.PaymentOption.Companion. import com.stripe.android.customersheet.analytics.CustomerSheetEventReporter import com.stripe.android.customersheet.injection.CustomerSheetViewModelScope import com.stripe.android.customersheet.util.isUnverifiedUSBankAccount +import com.stripe.android.model.CardBrand import com.stripe.android.model.ConfirmSetupIntentParams import com.stripe.android.model.ConfirmStripeIntentParams import com.stripe.android.model.PaymentMethod import com.stripe.android.model.PaymentMethodCode import com.stripe.android.model.PaymentMethodCreateParams +import com.stripe.android.model.PaymentMethodUpdateParams import com.stripe.android.model.SetupIntent import com.stripe.android.model.StripeIntent import com.stripe.android.networking.StripeRepository @@ -67,8 +69,6 @@ import javax.inject.Provider import kotlin.coroutines.CoroutineContext import com.stripe.android.ui.core.R as UiCoreR -private const val TempDelay = 2000L - @OptIn(ExperimentalCustomerSheetApi::class) @CustomerSheetViewModelScope internal class CustomerSheetViewModel @Inject constructor( @@ -429,6 +429,25 @@ internal class CustomerSheetViewModel @Inject constructor( } } + private suspend fun modifyCardPaymentMethod( + paymentMethod: PaymentMethod, + brand: CardBrand + ): CustomerAdapter.Result { + val paymentMethodId = paymentMethod.id + + return customerAdapter.updatePaymentMethod( + params = PaymentMethodUpdateParams.createCard( + paymentMethodId = paymentMethodId!!, + networks = PaymentMethodUpdateParams.Card.Networks( + preferred = brand.code + ) + ) + ).onSuccess { updatedMethod -> + onBackPressed() + updatePaymentMethodInState(updatedMethod) + } + } + private fun handlePaymentMethodRemoved(paymentMethod: PaymentMethod) { val currentViewState = viewState.value val newSavedPaymentMethods = currentViewState.savedPaymentMethods.filter { it.id != paymentMethod.id!! } @@ -484,10 +503,11 @@ internal class CustomerSheetViewModel @Inject constructor( } result is CustomerAdapter.Result.Success }, - updateExecutor = { _, _ -> - // TODO(tillh-stripe): Replace with update operation - delay(TempDelay) - Result.success(paymentMethod) + updateExecutor = { method, brand -> + when (val result = modifyCardPaymentMethod(method, brand)) { + is CustomerAdapter.Result.Success -> Result.success(result.value) + is CustomerAdapter.Result.Failure -> Result.failure(result.cause) + } }, ), isLiveMode = currentViewState.isLiveMode, @@ -513,6 +533,25 @@ internal class CustomerSheetViewModel @Inject constructor( } } + private fun updatePaymentMethodInState(updatedMethod: PaymentMethod) { + viewModelScope.launch { + val newSavedPaymentMethods = viewState.value.savedPaymentMethods.map { savedMethod -> + val savedId = savedMethod.id + val updatedId = updatedMethod.id + + if (updatedId != null && savedId != null && updatedId == savedId) { + updatedMethod + } else { + savedMethod + } + } + + updateViewState { + it.copy(savedPaymentMethods = newSavedPaymentMethods) + } + } + } + private fun onItemSelected(paymentSelection: PaymentSelection?) { // TODO (jameswoo) consider clearing the error message onItemSelected, currently the only // error source is when the payment methods cannot be loaded diff --git a/paymentsheet/src/main/java/com/stripe/android/customersheet/StripeCustomerAdapter.kt b/paymentsheet/src/main/java/com/stripe/android/customersheet/StripeCustomerAdapter.kt index db23b24e381..dfa898c8517 100644 --- a/paymentsheet/src/main/java/com/stripe/android/customersheet/StripeCustomerAdapter.kt +++ b/paymentsheet/src/main/java/com/stripe/android/customersheet/StripeCustomerAdapter.kt @@ -5,6 +5,7 @@ import com.stripe.android.common.exception.stripeErrorMessage import com.stripe.android.core.injection.IOContext import com.stripe.android.customersheet.CustomerAdapter.PaymentOption.Companion.toPaymentOption import com.stripe.android.model.PaymentMethod +import com.stripe.android.model.PaymentMethodUpdateParams import com.stripe.android.paymentsheet.PaymentSheet import com.stripe.android.paymentsheet.PrefsRepository import com.stripe.android.paymentsheet.R @@ -101,6 +102,25 @@ internal class StripeCustomerAdapter @Inject constructor( } } + override suspend fun updatePaymentMethod( + params: PaymentMethodUpdateParams + ): CustomerAdapter.Result { + return getCustomerEphemeralKey().mapCatching { customerEphemeralKey -> + customerRepository.updatePaymentMethod( + customerConfig = PaymentSheet.CustomerConfiguration( + id = customerEphemeralKey.customerId, + ephemeralKeySecret = customerEphemeralKey.ephemeralKey + ), + params = params + ).getOrElse { + return CustomerAdapter.Result.failure( + cause = it, + displayMessage = it.stripeErrorMessage(context), + ) + } + } + } + override suspend fun setSelectedPaymentOption( paymentOption: CustomerAdapter.PaymentOption? ): CustomerAdapter.Result { diff --git a/paymentsheet/src/test/java/com/stripe/android/customersheet/CustomerAdapterTest.kt b/paymentsheet/src/test/java/com/stripe/android/customersheet/CustomerAdapterTest.kt index 56682d6ed58..03efab5096a 100644 --- a/paymentsheet/src/test/java/com/stripe/android/customersheet/CustomerAdapterTest.kt +++ b/paymentsheet/src/test/java/com/stripe/android/customersheet/CustomerAdapterTest.kt @@ -10,6 +10,7 @@ import com.stripe.android.customersheet.CustomerAdapter.PaymentOption.Companion. import com.stripe.android.customersheet.StripeCustomerAdapter.Companion.CACHED_CUSTOMER_MAX_AGE_MILLIS import com.stripe.android.model.PaymentMethod import com.stripe.android.model.PaymentMethodFixtures +import com.stripe.android.model.PaymentMethodUpdateParams import com.stripe.android.paymentsheet.DefaultPrefsRepository import com.stripe.android.paymentsheet.FakePrefsRepository import com.stripe.android.paymentsheet.PrefsRepository @@ -288,6 +289,70 @@ class CustomerAdapterTest { .isEqualTo("Unable to detach payment method") } + @Test + fun `updatePaymentMethod succeeds when the payment method is update`() = runTest { + val adapter = createAdapter( + customerRepository = FakeCustomerRepository( + onUpdatePaymentMethod = { + Result.success(PaymentMethodFixtures.CARD_PAYMENT_METHOD) + } + ) + ) + val result = adapter.updatePaymentMethod( + PaymentMethodUpdateParams.createCard( + paymentMethodId = "pm_1234" + ) + ) + assertThat(result.getOrNull()).isEqualTo( + PaymentMethodFixtures.CARD_PAYMENT_METHOD + ) + } + + @Test + fun `updatePaymentMethod fails with default message when the payment method couldn't be updated`() = runTest { + val adapter = createAdapter( + customerRepository = FakeCustomerRepository( + onUpdatePaymentMethod = { + Result.failure( + APIException( + message = "could not update payment method", + ) + ) + } + ) + ) + val result = adapter.updatePaymentMethod( + PaymentMethodUpdateParams.createCard( + paymentMethodId = "pm_1234" + ) + ) + assertThat(result.failureOrNull()?.displayMessage) + .isEqualTo("Something went wrong") + } + + @Test + fun `updatePaymentMethod fails with Stripe message when the payment method couldn't be updated`() = runTest { + val adapter = createAdapter( + customerRepository = FakeCustomerRepository( + onUpdatePaymentMethod = { + Result.failure( + APIException( + message = "could not update payment method", + stripeError = StripeError(message = "Unable to update payment method") + ) + ) + } + ) + ) + val result = adapter.updatePaymentMethod( + PaymentMethodUpdateParams.createCard( + paymentMethodId = "pm_1234" + ) + ) + assertThat(result.failureOrNull()?.displayMessage) + .isEqualTo("Unable to update payment method") + } + @Test fun `setSelectedPaymentMethodOption saves the selected payment method`() = runTest { val adapter = createAdapter( diff --git a/paymentsheet/src/test/java/com/stripe/android/customersheet/CustomerSheetViewModelTest.kt b/paymentsheet/src/test/java/com/stripe/android/customersheet/CustomerSheetViewModelTest.kt index 8bd12cb7e26..6adf83dc7ca 100644 --- a/paymentsheet/src/test/java/com/stripe/android/customersheet/CustomerSheetViewModelTest.kt +++ b/paymentsheet/src/test/java/com/stripe/android/customersheet/CustomerSheetViewModelTest.kt @@ -19,6 +19,7 @@ import com.stripe.android.customersheet.utils.FakeCustomerSheetLoader import com.stripe.android.financialconnections.model.FinancialConnectionsAccount import com.stripe.android.financialconnections.model.FinancialConnectionsSession import com.stripe.android.financialconnections.model.PaymentAccount +import com.stripe.android.model.CardBrand import com.stripe.android.model.PaymentMethod import com.stripe.android.model.PaymentMethodFixtures.CARD_PAYMENT_METHOD import com.stripe.android.model.PaymentMethodFixtures.US_BANK_ACCOUNT @@ -31,7 +32,10 @@ import com.stripe.android.paymentsheet.forms.FormFieldValues import com.stripe.android.paymentsheet.forms.FormViewModel import com.stripe.android.paymentsheet.model.PaymentSelection import com.stripe.android.paymentsheet.paymentdatacollection.ach.USBankAccountFormScreenState +import com.stripe.android.paymentsheet.ui.EditPaymentMethodViewAction.OnBrandChoiceChanged import com.stripe.android.paymentsheet.ui.EditPaymentMethodViewAction.OnRemovePressed +import com.stripe.android.paymentsheet.ui.EditPaymentMethodViewAction.OnUpdatePressed +import com.stripe.android.paymentsheet.ui.EditPaymentMethodViewState import com.stripe.android.paymentsheet.ui.PrimaryButton import com.stripe.android.testing.CoroutineTestRule import com.stripe.android.testing.FeatureFlagTestRule @@ -2483,6 +2487,66 @@ class CustomerSheetViewModelTest { } } + @Test + fun `Updating payment method in edit screen goes through expected states`() = runTest(testDispatcher) { + val paymentMethods = PaymentMethodFactory.cards(size = 1) + + val firstMethod = paymentMethods.single() + + val updatedMethod = firstMethod.copy( + card = firstMethod.card?.copy( + networks = PaymentMethod.Card.Networks( + available = setOf("visa", "cartes_bancaires"), + preferred = "visa" + ) + ) + ) + + val customerAdapter = FakeCustomerAdapter( + paymentMethods = CustomerAdapter.Result.Success(paymentMethods), + onUpdatePaymentMethod = { + CustomerAdapter.Result.Success(updatedMethod) + } + ) + + val viewModel = createViewModel( + workContext = testDispatcher, + initialBackStack = listOf( + selectPaymentMethodViewState.copy( + savedPaymentMethods = paymentMethods, + ) + ), + customerPaymentMethods = paymentMethods, + customerAdapter = customerAdapter, + ) + + viewModel.viewState.test { + assertThat(awaitItem()).isInstanceOf(SelectPaymentMethod::class.java) + viewModel.handleViewAction(CustomerSheetViewAction.OnModifyItem(firstMethod)) + + val editViewState = awaitViewState() + editViewState.editPaymentMethodInteractor.handleViewAction( + OnBrandChoiceChanged( + EditPaymentMethodViewState.CardBrandChoice( + brand = CardBrand.Visa + ) + ) + ) + editViewState.editPaymentMethodInteractor.handleViewAction(OnUpdatePressed) + + // Confirm that nothing has changed yet. We're waiting to update the payment method + // once we return to the SPM screen. + val updatedViewState = awaitViewState() + assertThat(updatedViewState.savedPaymentMethods).containsExactlyElementsIn(paymentMethods) + + // Simulate the delay + testDispatcher.scheduler.advanceUntilIdle() + + val finalViewState = awaitViewState() + assertThat(finalViewState.savedPaymentMethods).containsExactlyElementsIn(listOf(updatedMethod)) + } + } + private fun mockUSBankAccountResult( isVerified: Boolean ): CollectBankAccountResultInternal.Completed { diff --git a/paymentsheet/src/test/java/com/stripe/android/customersheet/FakeCustomerAdapter.kt b/paymentsheet/src/test/java/com/stripe/android/customersheet/FakeCustomerAdapter.kt index d0709acc361..b3540fe91c5 100644 --- a/paymentsheet/src/test/java/com/stripe/android/customersheet/FakeCustomerAdapter.kt +++ b/paymentsheet/src/test/java/com/stripe/android/customersheet/FakeCustomerAdapter.kt @@ -2,6 +2,7 @@ package com.stripe.android.customersheet import com.stripe.android.model.PaymentMethod import com.stripe.android.model.PaymentMethodFixtures.CARD_PAYMENT_METHOD +import com.stripe.android.model.PaymentMethodUpdateParams @OptIn(ExperimentalCustomerSheetApi::class) internal class FakeCustomerAdapter( @@ -14,6 +15,8 @@ internal class FakeCustomerAdapter( ((paymentOption: CustomerAdapter.PaymentOption?) -> CustomerAdapter.Result)? = null, private val onAttachPaymentMethod: ((paymentMethodId: String) -> CustomerAdapter.Result)? = null, private val onDetachPaymentMethod: ((paymentMethodId: String) -> CustomerAdapter.Result)? = null, + private val onUpdatePaymentMethod: + ((params: PaymentMethodUpdateParams) -> CustomerAdapter.Result)? = null, private val onSetupIntentClientSecretForCustomerAttach: (() -> CustomerAdapter.Result)? = null ) : CustomerAdapter { @@ -31,6 +34,17 @@ internal class FakeCustomerAdapter( ?: CustomerAdapter.Result.success(paymentMethods.getOrNull()?.find { it.id!! == paymentMethodId }!!) } + override suspend fun updatePaymentMethod( + params: PaymentMethodUpdateParams + ): CustomerAdapter.Result { + return onUpdatePaymentMethod?.invoke(params) + ?: CustomerAdapter.Result.success( + paymentMethods.getOrNull()?.find { + it.id!! == params.paymentMethodId + }!! + ) + } + override suspend fun setSelectedPaymentOption( paymentOption: CustomerAdapter.PaymentOption? ): CustomerAdapter.Result { From 39392fd3cdc06179d81bea80d57f5be0e6bd4e8d Mon Sep 17 00:00:00 2001 From: Samer Alabi Date: Mon, 20 Nov 2023 14:41:58 -0500 Subject: [PATCH 2/3] Use updated method from `StripeRepository` in `CustomerAdapter`. --- paymentsheet/api/paymentsheet.api | 2 +- .../android/customersheet/CustomerAdapter.kt | 5 +++-- .../customersheet/CustomerSheetViewModel.kt | 4 +--- .../customersheet/StripeCustomerAdapter.kt | 2 ++ .../android/customersheet/CustomerAdapterTest.kt | 15 ++++++--------- .../customersheet/CustomerSheetViewModelTest.kt | 2 +- .../android/customersheet/FakeCustomerAdapter.kt | 7 ++++--- 7 files changed, 18 insertions(+), 19 deletions(-) diff --git a/paymentsheet/api/paymentsheet.api b/paymentsheet/api/paymentsheet.api index de364b0891c..3a5cc9c9dd4 100644 --- a/paymentsheet/api/paymentsheet.api +++ b/paymentsheet/api/paymentsheet.api @@ -15,7 +15,7 @@ public abstract interface class com/stripe/android/customersheet/CustomerAdapter public abstract fun retrieveSelectedPaymentOption (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public abstract fun setSelectedPaymentOption (Lcom/stripe/android/customersheet/CustomerAdapter$PaymentOption;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public abstract fun setupIntentClientSecretForCustomerAttach (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public abstract fun updatePaymentMethod (Lcom/stripe/android/model/PaymentMethodUpdateParams;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun updatePaymentMethod (Ljava/lang/String;Lcom/stripe/android/model/PaymentMethodUpdateParams;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } public final class com/stripe/android/customersheet/CustomerAdapter$Companion { diff --git a/paymentsheet/src/main/java/com/stripe/android/customersheet/CustomerAdapter.kt b/paymentsheet/src/main/java/com/stripe/android/customersheet/CustomerAdapter.kt index ca947e696aa..ef45d184007 100644 --- a/paymentsheet/src/main/java/com/stripe/android/customersheet/CustomerAdapter.kt +++ b/paymentsheet/src/main/java/com/stripe/android/customersheet/CustomerAdapter.kt @@ -51,11 +51,12 @@ interface CustomerAdapter { suspend fun detachPaymentMethod(paymentMethodId: String): Result /** - * Detaches the given payment method from a customer + * Updates the given payment method from a customer + * @param paymentMethodId, the payment method to update that is attached to the customer * @param params, parameters for updating a payment method * @return the modified [PaymentMethod]. */ - suspend fun updatePaymentMethod(params: PaymentMethodUpdateParams): Result + suspend fun updatePaymentMethod(paymentMethodId: String, params: PaymentMethodUpdateParams): Result /** * Saves the payment option to a data store. diff --git a/paymentsheet/src/main/java/com/stripe/android/customersheet/CustomerSheetViewModel.kt b/paymentsheet/src/main/java/com/stripe/android/customersheet/CustomerSheetViewModel.kt index fff878bb5f9..9a52facff20 100644 --- a/paymentsheet/src/main/java/com/stripe/android/customersheet/CustomerSheetViewModel.kt +++ b/paymentsheet/src/main/java/com/stripe/android/customersheet/CustomerSheetViewModel.kt @@ -433,11 +433,9 @@ internal class CustomerSheetViewModel @Inject constructor( paymentMethod: PaymentMethod, brand: CardBrand ): CustomerAdapter.Result { - val paymentMethodId = paymentMethod.id - return customerAdapter.updatePaymentMethod( + paymentMethodId = paymentMethod.id!!, params = PaymentMethodUpdateParams.createCard( - paymentMethodId = paymentMethodId!!, networks = PaymentMethodUpdateParams.Card.Networks( preferred = brand.code ) diff --git a/paymentsheet/src/main/java/com/stripe/android/customersheet/StripeCustomerAdapter.kt b/paymentsheet/src/main/java/com/stripe/android/customersheet/StripeCustomerAdapter.kt index dfa898c8517..6df9b72ba54 100644 --- a/paymentsheet/src/main/java/com/stripe/android/customersheet/StripeCustomerAdapter.kt +++ b/paymentsheet/src/main/java/com/stripe/android/customersheet/StripeCustomerAdapter.kt @@ -103,6 +103,7 @@ internal class StripeCustomerAdapter @Inject constructor( } override suspend fun updatePaymentMethod( + paymentMethodId: String, params: PaymentMethodUpdateParams ): CustomerAdapter.Result { return getCustomerEphemeralKey().mapCatching { customerEphemeralKey -> @@ -111,6 +112,7 @@ internal class StripeCustomerAdapter @Inject constructor( id = customerEphemeralKey.customerId, ephemeralKeySecret = customerEphemeralKey.ephemeralKey ), + paymentMethodId = paymentMethodId, params = params ).getOrElse { return CustomerAdapter.Result.failure( diff --git a/paymentsheet/src/test/java/com/stripe/android/customersheet/CustomerAdapterTest.kt b/paymentsheet/src/test/java/com/stripe/android/customersheet/CustomerAdapterTest.kt index 03efab5096a..3df4fb1d7e0 100644 --- a/paymentsheet/src/test/java/com/stripe/android/customersheet/CustomerAdapterTest.kt +++ b/paymentsheet/src/test/java/com/stripe/android/customersheet/CustomerAdapterTest.kt @@ -299,9 +299,8 @@ class CustomerAdapterTest { ) ) val result = adapter.updatePaymentMethod( - PaymentMethodUpdateParams.createCard( - paymentMethodId = "pm_1234" - ) + paymentMethodId = "pm_1234", + params = PaymentMethodUpdateParams.createCard() ) assertThat(result.getOrNull()).isEqualTo( PaymentMethodFixtures.CARD_PAYMENT_METHOD @@ -322,9 +321,8 @@ class CustomerAdapterTest { ) ) val result = adapter.updatePaymentMethod( - PaymentMethodUpdateParams.createCard( - paymentMethodId = "pm_1234" - ) + paymentMethodId = "pm_1234", + params = PaymentMethodUpdateParams.createCard() ) assertThat(result.failureOrNull()?.displayMessage) .isEqualTo("Something went wrong") @@ -345,9 +343,8 @@ class CustomerAdapterTest { ) ) val result = adapter.updatePaymentMethod( - PaymentMethodUpdateParams.createCard( - paymentMethodId = "pm_1234" - ) + paymentMethodId = "pm_1234", + params = PaymentMethodUpdateParams.createCard() ) assertThat(result.failureOrNull()?.displayMessage) .isEqualTo("Unable to update payment method") diff --git a/paymentsheet/src/test/java/com/stripe/android/customersheet/CustomerSheetViewModelTest.kt b/paymentsheet/src/test/java/com/stripe/android/customersheet/CustomerSheetViewModelTest.kt index 6adf83dc7ca..df3f630af6a 100644 --- a/paymentsheet/src/test/java/com/stripe/android/customersheet/CustomerSheetViewModelTest.kt +++ b/paymentsheet/src/test/java/com/stripe/android/customersheet/CustomerSheetViewModelTest.kt @@ -2504,7 +2504,7 @@ class CustomerSheetViewModelTest { val customerAdapter = FakeCustomerAdapter( paymentMethods = CustomerAdapter.Result.Success(paymentMethods), - onUpdatePaymentMethod = { + onUpdatePaymentMethod = { _, _ -> CustomerAdapter.Result.Success(updatedMethod) } ) diff --git a/paymentsheet/src/test/java/com/stripe/android/customersheet/FakeCustomerAdapter.kt b/paymentsheet/src/test/java/com/stripe/android/customersheet/FakeCustomerAdapter.kt index b3540fe91c5..91df640d38f 100644 --- a/paymentsheet/src/test/java/com/stripe/android/customersheet/FakeCustomerAdapter.kt +++ b/paymentsheet/src/test/java/com/stripe/android/customersheet/FakeCustomerAdapter.kt @@ -16,7 +16,7 @@ internal class FakeCustomerAdapter( private val onAttachPaymentMethod: ((paymentMethodId: String) -> CustomerAdapter.Result)? = null, private val onDetachPaymentMethod: ((paymentMethodId: String) -> CustomerAdapter.Result)? = null, private val onUpdatePaymentMethod: - ((params: PaymentMethodUpdateParams) -> CustomerAdapter.Result)? = null, + ((paymentMethodId: String, params: PaymentMethodUpdateParams) -> CustomerAdapter.Result)? = null, private val onSetupIntentClientSecretForCustomerAttach: (() -> CustomerAdapter.Result)? = null ) : CustomerAdapter { @@ -35,12 +35,13 @@ internal class FakeCustomerAdapter( } override suspend fun updatePaymentMethod( + paymentMethodId: String, params: PaymentMethodUpdateParams ): CustomerAdapter.Result { - return onUpdatePaymentMethod?.invoke(params) + return onUpdatePaymentMethod?.invoke(paymentMethodId, params) ?: CustomerAdapter.Result.success( paymentMethods.getOrNull()?.find { - it.id!! == params.paymentMethodId + it.id!! == paymentMethodId }!! ) } From a707d5331ac864c02e4114c803b3bb8669afd98c Mon Sep 17 00:00:00 2001 From: Samer Alabi Date: Tue, 21 Nov 2023 13:22:13 -0500 Subject: [PATCH 3/3] Align `CustomerAdapter` docs & remove fraud detection data from `updatePaymentMethod`. --- .../com/stripe/android/networking/StripeApiRepository.kt | 2 +- .../com/stripe/android/customersheet/CustomerAdapter.kt | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/payments-core/src/main/java/com/stripe/android/networking/StripeApiRepository.kt b/payments-core/src/main/java/com/stripe/android/networking/StripeApiRepository.kt index cc14a31d44e..5d2848f0172 100644 --- a/payments-core/src/main/java/com/stripe/android/networking/StripeApiRepository.kt +++ b/payments-core/src/main/java/com/stripe/android/networking/StripeApiRepository.kt @@ -554,7 +554,7 @@ class StripeApiRepository @JvmOverloads internal constructor( apiRequest = apiRequestFactory.createPost( url = getPaymentMethodUrl(paymentMethodId), options = options, - params = paymentMethodUpdateParams.toParamMap() + fraudDetectionData?.params.orEmpty(), + params = paymentMethodUpdateParams.toParamMap(), ), jsonParser = PaymentMethodJsonParser() ) diff --git a/paymentsheet/src/main/java/com/stripe/android/customersheet/CustomerAdapter.kt b/paymentsheet/src/main/java/com/stripe/android/customersheet/CustomerAdapter.kt index ef45d184007..e6c626d592d 100644 --- a/paymentsheet/src/main/java/com/stripe/android/customersheet/CustomerAdapter.kt +++ b/paymentsheet/src/main/java/com/stripe/android/customersheet/CustomerAdapter.kt @@ -51,10 +51,11 @@ interface CustomerAdapter { suspend fun detachPaymentMethod(paymentMethodId: String): Result /** - * Updates the given payment method from a customer - * @param paymentMethodId, the payment method to update that is attached to the customer - * @param params, parameters for updating a payment method - * @return the modified [PaymentMethod]. + * Updates a payment method with the provided [PaymentMethodUpdateParams]. + * + * @param paymentMethodId The payment method to update + * @param paymentMethodUpdateParams The [PaymentMethodUpdateParams] + * @return The updated [PaymentMethod] */ suspend fun updatePaymentMethod(paymentMethodId: String, params: PaymentMethodUpdateParams): Result