-
Notifications
You must be signed in to change notification settings - Fork 663
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add set as default feature enabled and disabled state to CustomerState #10046
Changes from all commits
de56fbd
46a4866
adbb7c2
1555850
9694251
c2a7c1f
80ca7e4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,7 +9,6 @@ import com.stripe.android.common.model.CommonConfiguration | |
import com.stripe.android.core.Logger | ||
import com.stripe.android.core.exception.StripeException | ||
import com.stripe.android.core.injection.IOContext | ||
import com.stripe.android.core.utils.FeatureFlags | ||
import com.stripe.android.core.utils.UserFacingLogger | ||
import com.stripe.android.googlepaylauncher.GooglePayEnvironment | ||
import com.stripe.android.googlepaylauncher.GooglePayRepository | ||
|
@@ -200,7 +199,7 @@ internal class DefaultPaymentElementLoader @Inject constructor( | |
val initialPaymentSelection = async { | ||
retrieveInitialPaymentSelection( | ||
savedSelection = savedSelection, | ||
customer = customer, | ||
customerDeferred = customer, | ||
isGooglePayReady = isGooglePayReady, | ||
) | ||
} | ||
|
@@ -355,7 +354,7 @@ internal class DefaultPaymentElementLoader @Inject constructor( | |
paymentMethods = state.paymentMethods | ||
.withDefaultPaymentMethodOrLastUsedPaymentMethodFirst( | ||
savedSelection = savedSelection, | ||
defaultPaymentMethodId = state.defaultPaymentMethodId | ||
defaultPaymentMethodState = state.defaultPaymentMethodState, | ||
).filter { cardBrandFilter.isAccepted(it) } | ||
) | ||
} | ||
|
@@ -558,29 +557,29 @@ internal class DefaultPaymentElementLoader @Inject constructor( | |
|
||
private suspend fun retrieveInitialPaymentSelection( | ||
savedSelection: Deferred<SavedSelection>, | ||
customer: Deferred<CustomerState?>, | ||
customerDeferred: Deferred<CustomerState?>, | ||
isGooglePayReady: Boolean, | ||
): PaymentSelection? { | ||
// get default payment method if dpm enabled, otherwise try to get savedSelection | ||
val primaryPaymentSelection = if (FeatureFlags.enableDefaultPaymentMethods.isEnabled) { | ||
customer.await()?.defaultPaymentMethodId?.let { | ||
customer.await()?.paymentMethods?.firstOrNull { | ||
it.id == customer.await()?.defaultPaymentMethodId | ||
val customer = customerDeferred.await() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a difference between and
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oh good point! I don't think so. I can follow up with this change (I have another PR that will build on top of this one and I don't want that PR to be blocked on this) |
||
val primaryPaymentSelection = when (val defaultPaymentMethodState = customer?.defaultPaymentMethodState) { | ||
is CustomerState.DefaultPaymentMethodState.Enabled -> | ||
customer.paymentMethods.firstOrNull { | ||
it.id == defaultPaymentMethodState.defaultPaymentMethodId | ||
}?.toPaymentSelection() | ||
} | ||
} else { | ||
when (val selection = savedSelection.await()) { | ||
is SavedSelection.GooglePay -> PaymentSelection.GooglePay | ||
is SavedSelection.Link -> PaymentSelection.Link | ||
is SavedSelection.PaymentMethod -> { | ||
customer.await()?.paymentMethods?.find { it.id == selection.id }?.toPaymentSelection() | ||
CustomerState.DefaultPaymentMethodState.Disabled, null -> { | ||
when (val selection = savedSelection.await()) { | ||
is SavedSelection.GooglePay -> PaymentSelection.GooglePay | ||
is SavedSelection.Link -> PaymentSelection.Link | ||
is SavedSelection.PaymentMethod -> { | ||
customer?.paymentMethods?.find { it.id == selection.id }?.toPaymentSelection() | ||
} | ||
is SavedSelection.None -> null | ||
} | ||
is SavedSelection.None -> null | ||
} | ||
} | ||
|
||
return primaryPaymentSelection | ||
?: customer.await()?.paymentMethods?.firstOrNull()?.toPaymentSelection() | ||
?: customer?.paymentMethods?.firstOrNull()?.toPaymentSelection() | ||
?: PaymentSelection.GooglePay.takeIf { isGooglePayReady } | ||
} | ||
|
||
|
@@ -750,21 +749,18 @@ internal class DefaultPaymentElementLoader @Inject constructor( | |
|
||
private suspend fun List<PaymentMethod>.withDefaultPaymentMethodOrLastUsedPaymentMethodFirst( | ||
savedSelection: Deferred<SavedSelection>, | ||
defaultPaymentMethodId: String? | ||
defaultPaymentMethodState: CustomerState.DefaultPaymentMethodState, | ||
): List<PaymentMethod> { | ||
val primaryPaymentMethodIndex = if (FeatureFlags.enableDefaultPaymentMethods.isEnabled) { | ||
defaultPaymentMethodId?.let { | ||
indexOfFirst { it.id == defaultPaymentMethodId } | ||
} | ||
} else { | ||
val selection = savedSelection.await() | ||
(selection as? SavedSelection.PaymentMethod)?.let { | ||
indexOfFirst { it.id == selection.id }.takeIf { it != -1 } | ||
val primaryPaymentMethodId = when (defaultPaymentMethodState) { | ||
is CustomerState.DefaultPaymentMethodState.Enabled -> defaultPaymentMethodState.defaultPaymentMethodId | ||
CustomerState.DefaultPaymentMethodState.Disabled -> { | ||
(savedSelection.await() as? SavedSelection.PaymentMethod)?.id | ||
} | ||
} | ||
|
||
return primaryPaymentMethodIndex?.let { | ||
val primaryPaymentMethod = get(primaryPaymentMethodIndex) | ||
val primaryPaymentMethod = this.firstOrNull { it.id == primaryPaymentMethodId } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is better than what I wrote There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah, firstOrNull is really helpful here, I just didn't think of it until I was trying to make this work with this new type! |
||
|
||
return primaryPaymentMethod?.let { | ||
listOf(primaryPaymentMethod) + (this - primaryPaymentMethod) | ||
} ?: this | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -40,21 +40,10 @@ class CustomerStateTest { | |
customerSessionClientSecret = "cuss_123", | ||
) | ||
|
||
assertThat(customerState).isEqualTo( | ||
CustomerState( | ||
id = "cus_1", | ||
ephemeralKeySecret = "ek_1", | ||
customerSessionClientSecret = "cuss_123", | ||
paymentMethods = paymentMethods, | ||
permissions = CustomerState.Permissions( | ||
canRemovePaymentMethods = false, | ||
canRemoveLastPaymentMethod = false, | ||
// Always true for `customer_session` | ||
canRemoveDuplicates = true, | ||
), | ||
defaultPaymentMethodId = null | ||
) | ||
) | ||
assertThat(customerState.permissions.canRemovePaymentMethods).isFalse() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I updated a few of these tests to only assert on the fields relevant to the test, so that I wouldn't need to update the defaultPaymentMethodId field in as many places There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I realized that we can remove
from the calls of
As the default values already There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oh nice! I'm probably not going to make this update, because I don't think including them is adding much maintenance overhead, but it's good to know we don't need it for new tests or if we wanted to update these in the future |
||
assertThat(customerState.permissions.canRemoveLastPaymentMethod).isFalse() | ||
// Always true for `customer_session` | ||
assertThat(customerState.permissions.canRemoveDuplicates).isTrue() | ||
} | ||
|
||
@Test | ||
|
@@ -79,21 +68,10 @@ class CustomerStateTest { | |
customerSessionClientSecret = "cuss_123", | ||
) | ||
|
||
assertThat(customerState).isEqualTo( | ||
CustomerState( | ||
id = "cus_1", | ||
ephemeralKeySecret = "ek_1", | ||
customerSessionClientSecret = "cuss_123", | ||
paymentMethods = paymentMethods, | ||
permissions = CustomerState.Permissions( | ||
canRemovePaymentMethods = true, | ||
canRemoveLastPaymentMethod = true, | ||
// Always true for `customer_session` | ||
canRemoveDuplicates = true, | ||
), | ||
defaultPaymentMethodId = null | ||
) | ||
) | ||
assertThat(customerState.permissions.canRemovePaymentMethods).isTrue() | ||
assertThat(customerState.permissions.canRemoveLastPaymentMethod).isTrue() | ||
// Always true for `customer_session` | ||
assertThat(customerState.permissions.canRemoveDuplicates).isTrue() | ||
} | ||
|
||
@Test | ||
|
@@ -118,21 +96,10 @@ class CustomerStateTest { | |
customerSessionClientSecret = "cuss_123", | ||
) | ||
|
||
assertThat(customerState).isEqualTo( | ||
CustomerState( | ||
id = "cus_3", | ||
ephemeralKeySecret = "ek_3", | ||
customerSessionClientSecret = "cuss_123", | ||
paymentMethods = paymentMethods, | ||
permissions = CustomerState.Permissions( | ||
canRemovePaymentMethods = false, | ||
canRemoveLastPaymentMethod = false, | ||
// Always true for `customer_session` | ||
canRemoveDuplicates = true, | ||
), | ||
defaultPaymentMethodId = null | ||
) | ||
) | ||
assertThat(customerState.permissions.canRemovePaymentMethods).isFalse() | ||
assertThat(customerState.permissions.canRemoveLastPaymentMethod).isFalse() | ||
// Always true for `customer_session` | ||
assertThat(customerState.permissions.canRemoveDuplicates).isTrue() | ||
} | ||
|
||
private fun createCustomerSessionForTestingDefaultPaymentMethod(defaultPaymentMethodId: String?): CustomerState { | ||
|
@@ -173,7 +140,13 @@ class CustomerStateTest { | |
defaultPaymentMethodId = defaultPaymentMethodId | ||
) | ||
|
||
assertThat(customerState.defaultPaymentMethodId).isEqualTo(defaultPaymentMethodId) | ||
assertThat(customerState.defaultPaymentMethodState).isInstanceOf( | ||
CustomerState.DefaultPaymentMethodState.Enabled::class.java | ||
) | ||
val actualDefaultPaymentMethodId = ( | ||
customerState.defaultPaymentMethodState as CustomerState.DefaultPaymentMethodState.Enabled | ||
).defaultPaymentMethodId | ||
assertThat(actualDefaultPaymentMethodId).isEqualTo(defaultPaymentMethodId) | ||
} | ||
|
||
@Test | ||
|
@@ -185,7 +158,7 @@ class CustomerStateTest { | |
defaultPaymentMethodId = defaultPaymentMethodId | ||
) | ||
|
||
assertThat(customerState.defaultPaymentMethodId).isNull() | ||
assertThat(customerState.defaultPaymentMethodState).isEqualTo(CustomerState.DefaultPaymentMethodState.Disabled) | ||
} | ||
|
||
@Test | ||
|
@@ -197,7 +170,13 @@ class CustomerStateTest { | |
defaultPaymentMethodId = defaultPaymentMethodId | ||
) | ||
|
||
assertThat(customerState.defaultPaymentMethodId).isNull() | ||
assertThat(customerState.defaultPaymentMethodState).isInstanceOf( | ||
CustomerState.DefaultPaymentMethodState.Enabled::class.java | ||
) | ||
val actualDefaultPaymentMethodId = ( | ||
customerState.defaultPaymentMethodState as CustomerState.DefaultPaymentMethodState.Enabled | ||
).defaultPaymentMethodId | ||
assertThat(actualDefaultPaymentMethodId).isEqualTo(defaultPaymentMethodId) | ||
} | ||
|
||
@Test | ||
|
@@ -209,7 +188,7 @@ class CustomerStateTest { | |
defaultPaymentMethodId = defaultPaymentMethodId | ||
) | ||
|
||
assertThat(customerState.defaultPaymentMethodId).isNull() | ||
assertThat(customerState.defaultPaymentMethodState).isEqualTo(CustomerState.DefaultPaymentMethodState.Disabled) | ||
} | ||
|
||
@Test | ||
|
@@ -238,7 +217,7 @@ class CustomerStateTest { | |
// Always 'false' for legacy ephemeral keys | ||
canRemoveDuplicates = false, | ||
), | ||
defaultPaymentMethodId = null | ||
defaultPaymentMethodState = CustomerState.DefaultPaymentMethodState.Disabled | ||
) | ||
) | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I re-worked this function and also withDefaultPaymentMethodOrLastUsedPaymentMethodFirst.
Accessing the defaultPaymentMethodId is a bit more complicated now and the code became hard to read unless I adjusted a bit. The behavior is the same though
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it worth breaking this code out into another object, testing the defaulting logic in isolation, and injecting that object into the loader for use?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Testing the defaulting logic in isolation sounds like a good idea to me. I think we could also achieve that by just making this function @VisibleForTesting. What's the advantage of breaking it out into another object? Want to make sure I'm fully understanding your suggestion here.
I can follow up with this change!