Skip to content
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

Preserve payment selection for guests #5480

Merged
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
10 changes: 6 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@

### PaymentSheet

* [CHANGED][5487](https://github.com/stripe/stripe-android/pull/5487) Updated Google Pay button to
match new brand guidelines.
* [ADDED][5502](https://github.com/stripe/stripe-android/pull/5502) Added phone number minimum
* [ADDED][5502](https://github.com/stripe/stripe-android/pull/5502) Added phone number minimum
length validation
* [ADDED][5518](https://github.com/stripe/stripe-android/pull/5518) Added state/province dropdown
* [ADDED][5518](https://github.com/stripe/stripe-android/pull/5518) Added state/province dropdown
for US and Canada.
* [CHANGED][5487](https://github.com/stripe/stripe-android/pull/5487) Updated Google Pay button to
match new brand guidelines.
* [FIXED][5480](https://github.com/stripe/stripe-android/pull/5480) `FlowController` now correctly
preserves the previously selected payment method for guests.

## 20.11.0 - 2022-08-29
This release adds postal code validation for PaymentSheet and fixed a fileprovider naming bug for Identity.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ class LinkPaymentLauncher @AssistedInject internal constructor(

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
companion object {
val LINK_ENABLED = BuildConfig.DEBUG
val LINK_ENABLED = false
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had to revert my earlier change because this breaks the PaymentOptionsActivityTest.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revert this change?

Copy link
Collaborator Author

@tillh-stripe tillh-stripe Sep 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, unfortunately had to do this to stop PaymentOptionsActivityTest from failing.

val supportedFundingSources = SupportedPaymentMethod.allTypes
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ const val CUSTOMER_NAME = "customerName"
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
const val SHIPPING_VALUES = "shippingValues"

/**
* Identifies whether Link is enabled.
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
const val LINK_ENABLED = "linkEnabled"

/**
* Identifies the Stripe Intent being processed by Link.
*/
Expand Down
14 changes: 11 additions & 3 deletions paymentsheet/api/paymentsheet.api
Original file line number Diff line number Diff line change
Expand Up @@ -1068,11 +1068,11 @@ public final class com/stripe/android/paymentsheet/flowcontroller/DefaultFlowCon
}

public final class com/stripe/android/paymentsheet/flowcontroller/DefaultFlowControllerInitializer_Factory : dagger/internal/Factory {
public fun <init> (Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;)V
public static fun create (Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;)Lcom/stripe/android/paymentsheet/flowcontroller/DefaultFlowControllerInitializer_Factory;
public fun <init> (Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;)V
public static fun create (Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;)Lcom/stripe/android/paymentsheet/flowcontroller/DefaultFlowControllerInitializer_Factory;
public fun get ()Lcom/stripe/android/paymentsheet/flowcontroller/DefaultFlowControllerInitializer;
public synthetic fun get ()Ljava/lang/Object;
public static fun newInstance (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lcom/stripe/android/paymentsheet/repositories/StripeIntentRepository;Lcom/stripe/android/paymentsheet/model/StripeIntentValidator;Lcom/stripe/android/paymentsheet/repositories/CustomerRepository;Lcom/stripe/android/ui/core/forms/resources/ResourceRepository;Lcom/stripe/android/core/Logger;Lcom/stripe/android/paymentsheet/analytics/EventReporter;Lkotlin/coroutines/CoroutineContext;)Lcom/stripe/android/paymentsheet/flowcontroller/DefaultFlowControllerInitializer;
public static fun newInstance (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lcom/stripe/android/paymentsheet/repositories/StripeIntentRepository;Lcom/stripe/android/paymentsheet/model/StripeIntentValidator;Lcom/stripe/android/paymentsheet/repositories/CustomerRepository;Lcom/stripe/android/ui/core/forms/resources/ResourceRepository;Lcom/stripe/android/core/Logger;Lcom/stripe/android/paymentsheet/analytics/EventReporter;Lkotlin/coroutines/CoroutineContext;Z)Lcom/stripe/android/paymentsheet/flowcontroller/DefaultFlowControllerInitializer;
}

public final class com/stripe/android/paymentsheet/flowcontroller/DefaultFlowController_Factory : dagger/internal/Factory {
Expand Down Expand Up @@ -1206,6 +1206,14 @@ public final class com/stripe/android/paymentsheet/injection/FlowControllerModul
public static fun provideEventReporterMode ()Lcom/stripe/android/paymentsheet/analytics/EventReporter$Mode;
}

public final class com/stripe/android/paymentsheet/injection/FlowControllerModule_Companion_ProvideLinkEnabledFactory : dagger/internal/Factory {
public fun <init> ()V
public static fun create ()Lcom/stripe/android/paymentsheet/injection/FlowControllerModule_Companion_ProvideLinkEnabledFactory;
public fun get ()Ljava/lang/Boolean;
public synthetic fun get ()Ljava/lang/Object;
public static fun provideLinkEnabled ()Z
}

public final class com/stripe/android/paymentsheet/injection/FlowControllerModule_Companion_ProvidePrefsRepositoryFactoryFactory : dagger/internal/Factory {
public fun <init> (Ljavax/inject/Provider;Ljavax/inject/Provider;)V
public static fun create (Ljavax/inject/Provider;Ljavax/inject/Provider;)Lcom/stripe/android/paymentsheet/injection/FlowControllerModule_Companion_ProvidePrefsRepositoryFactoryFactory;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import kotlin.coroutines.CoroutineContext

internal class DefaultPrefsRepository(
private val context: Context,
private val customerId: String,
private val customerId: String?,
private val workContext: CoroutineContext
) : PrefsRepository {
private val prefs: SharedPreferences by lazy {
Expand Down Expand Up @@ -51,7 +51,7 @@ internal class DefaultPrefsRepository(
}

private fun getKey(): String {
return "customer[$customerId]"
return customerId?.let { "customer[$it]" } ?: "guest"
}

private companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,4 @@ internal interface PrefsRepository {
): SavedSelection

fun savePaymentSelection(paymentSelection: PaymentSelection?)

class Noop : PrefsRepository {
override suspend fun getSavedSelection(
isGooglePayAvailable: Boolean,
isLinkAvailable: Boolean
) = SavedSelection.None

override fun savePaymentSelection(paymentSelection: PaymentSelection?) {}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import com.stripe.android.core.Logger
import com.stripe.android.core.injection.IOContext
import com.stripe.android.googlepaylauncher.GooglePayEnvironment
import com.stripe.android.googlepaylauncher.GooglePayRepository
import com.stripe.android.link.LinkPaymentLauncher
import com.stripe.android.link.injection.LINK_ENABLED
import com.stripe.android.model.PaymentMethod
import com.stripe.android.model.StripeIntent
import com.stripe.android.paymentsheet.PaymentSheet
Expand All @@ -23,6 +23,7 @@ import com.stripe.android.ui.core.forms.resources.ResourceRepository
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.withContext
import javax.inject.Inject
import javax.inject.Named
import javax.inject.Singleton
import kotlin.coroutines.CoroutineContext

Expand All @@ -38,7 +39,8 @@ internal class DefaultFlowControllerInitializer @Inject constructor(
private val lpmResourceRepository: ResourceRepository<LpmRepository>,
private val logger: Logger,
val eventReporter: EventReporter,
@IOContext private val workContext: CoroutineContext
@IOContext private val workContext: CoroutineContext,
@Named(LINK_ENABLED) private val isLinkEnabled: Boolean
) : FlowControllerInitializer {

override suspend fun init(
Expand All @@ -50,24 +52,16 @@ internal class DefaultFlowControllerInitializer @Inject constructor(
retrieveStripeIntent(clientSecret)
}.fold(
onSuccess = { stripeIntent ->
val isLinkReady = LinkPaymentLauncher.LINK_ENABLED &&
val isLinkReady = isLinkEnabled &&
stripeIntent.paymentMethodTypes.contains(PaymentMethod.Type.Link.code)

paymentSheetConfiguration?.customer?.let { customerConfig ->
createWithCustomer(
clientSecret,
stripeIntent,
customerConfig,
paymentSheetConfiguration,
isGooglePayReady,
isLinkReady
)
} ?: createWithoutCustomer(
clientSecret,
stripeIntent,
paymentSheetConfiguration,
isGooglePayReady,
isLinkReady
create(
clientSecret = clientSecret,
stripeIntent = stripeIntent,
customerConfig = paymentSheetConfiguration?.customer,
config = paymentSheetConfiguration,
isGooglePayReady = isGooglePayReady,
isLinkReady = isLinkReady
)
},
onFailure = {
Expand All @@ -92,16 +86,50 @@ internal class DefaultFlowControllerInitializer @Inject constructor(
}?.isReady()?.first() ?: false
}

private suspend fun createWithCustomer(
private suspend fun create(
clientSecret: ClientSecret,
stripeIntent: StripeIntent,
customerConfig: PaymentSheet.CustomerConfiguration,
customerConfig: PaymentSheet.CustomerConfiguration?,
config: PaymentSheet.Configuration?,
isGooglePayReady: Boolean,
isLinkReady: Boolean
): FlowControllerInitializer.InitResult {
val prefsRepository = prefsRepositoryFactory(customerConfig)

val paymentMethods = if (customerConfig != null) {
retrieveCustomerPaymentMethods(
stripeIntent,
config,
customerConfig
)
} else {
emptyList()
}

val savedSelection = retrieveSavedPaymentSelection(
prefsRepository,
isGooglePayReady,
isLinkReady,
paymentMethods
)

return FlowControllerInitializer.InitResult.Success(
InitData(
config = config,
clientSecret = clientSecret,
stripeIntent = stripeIntent,
paymentMethods = paymentMethods,
savedSelection = savedSelection,
isGooglePayReady = isGooglePayReady
)
)
}

private suspend fun retrieveCustomerPaymentMethods(
stripeIntent: StripeIntent,
config: PaymentSheet.Configuration?,
customerConfig: PaymentSheet.CustomerConfiguration
): List<PaymentMethod> {
val paymentMethodTypes = getSupportedSavedCustomerPMs(
stripeIntent,
config,
Expand All @@ -117,74 +145,43 @@ internal class DefaultFlowControllerInitializer @Inject constructor(
paymentMethodTypes
).filter { paymentMethod ->
paymentMethod.hasExpectedDetails()
}.let { paymentMethods ->
setLastSavedPaymentMethod(
prefsRepository,
isGooglePayReady,
isLinkReady,
paymentMethods
)

FlowControllerInitializer.InitResult.Success(
InitData(
config = config,
clientSecret = clientSecret,
stripeIntent = stripeIntent,
paymentMethods = paymentMethods,
savedSelection = prefsRepository.getSavedSelection(
isGooglePayReady,
isLinkReady
),
isGooglePayReady = isGooglePayReady
)
)
}
}

private fun createWithoutCustomer(
clientSecret: ClientSecret,
stripeIntent: StripeIntent,
config: PaymentSheet.Configuration?,
private suspend fun retrieveSavedPaymentSelection(
prefsRepository: PrefsRepository,
isGooglePayReady: Boolean,
isLinkReady: Boolean
): FlowControllerInitializer.InitResult {
val savedSelection = if (isLinkReady) {
SavedSelection.Link
} else if (isGooglePayReady) {
SavedSelection.GooglePay
} else {
SavedSelection.None
isLinkReady: Boolean,
paymentMethods: List<PaymentMethod>
): SavedSelection {
val savedSelection = prefsRepository.getSavedSelection(isGooglePayReady, isLinkReady)
if (savedSelection != SavedSelection.None) {
return savedSelection
}

return FlowControllerInitializer.InitResult.Success(
InitData(
config = config,
clientSecret = clientSecret,
stripeIntent = stripeIntent,
paymentMethods = emptyList(),
savedSelection = savedSelection,
isGooglePayReady = isGooglePayReady
)
// No saved selection has been set yet, so we'll initialize it with a default
// value based on which payment methods are available.
val paymentSelection = determineDefaultPaymentSelection(
isGooglePayReady,
isLinkReady,
paymentMethods
)

prefsRepository.savePaymentSelection(paymentSelection)

return prefsRepository.getSavedSelection(isGooglePayReady, isLinkReady)
}

private suspend fun setLastSavedPaymentMethod(
prefsRepository: PrefsRepository,
private fun determineDefaultPaymentSelection(
isGooglePayReady: Boolean,
isLinkReady: Boolean,
paymentMethods: List<PaymentMethod>
) {
if (
prefsRepository.getSavedSelection(isGooglePayReady, isLinkReady) == SavedSelection.None
) {
when {
paymentMethods.isNotEmpty() -> PaymentSelection.Saved(paymentMethods.first())
isLinkReady -> PaymentSelection.Link
isGooglePayReady -> PaymentSelection.GooglePay
else -> null
}?.let {
prefsRepository.savePaymentSelection(it)
}
): PaymentSelection? {
return when {
paymentMethods.isNotEmpty() -> PaymentSelection.Saved(paymentMethods.first())
isLinkReady -> PaymentSelection.Link
isGooglePayReady -> PaymentSelection.GooglePay
else -> null
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import android.content.Context
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelStoreOwner
import com.stripe.android.core.injection.IOContext
import com.stripe.android.link.LinkPaymentLauncher
import com.stripe.android.link.injection.LINK_ENABLED
import com.stripe.android.payments.core.injection.PRODUCT_USAGE
import com.stripe.android.paymentsheet.DefaultPrefsRepository
import com.stripe.android.paymentsheet.PaymentSheet
Expand Down Expand Up @@ -54,13 +56,11 @@ internal abstract class FlowControllerModule {
appContext: Context,
@IOContext workContext: CoroutineContext
): (PaymentSheet.CustomerConfiguration?) -> PrefsRepository = { customerConfig ->
customerConfig?.let {
DefaultPrefsRepository(
appContext,
it.id,
workContext
)
} ?: PrefsRepository.Noop()
DefaultPrefsRepository(
appContext,
customerConfig?.id,
workContext
)
}

@Provides
Expand All @@ -72,6 +72,11 @@ internal abstract class FlowControllerModule {
@Named(PRODUCT_USAGE)
fun provideProductUsageTokens() = setOf("PaymentSheet.FlowController")

@Provides
@Singleton
@Named(LINK_ENABLED)
fun provideLinkEnabled() = LinkPaymentLauncher.LINK_ENABLED

@Provides
@Singleton
fun provideViewModel(viewModelStoreOwner: ViewModelStoreOwner): FlowControllerViewModel =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,11 @@ internal class PaymentOptionsViewModelModule {
appContext: Context,
@IOContext workContext: CoroutineContext
): (PaymentSheet.CustomerConfiguration?) -> PrefsRepository = { customerConfig ->
customerConfig?.let {
DefaultPrefsRepository(
appContext,
it.id,
workContext
)
} ?: PrefsRepository.Noop()
DefaultPrefsRepository(
appContext,
customerConfig?.id,
workContext
)
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,10 @@ internal class PaymentSheetViewModelModule(private val starterArgs: PaymentSheet
appContext: Context,
@IOContext workContext: CoroutineContext
): PrefsRepository {
return starterArgs.config?.customer?.let { (id) ->
DefaultPrefsRepository(
appContext,
customerId = id,
workContext = workContext
)
} ?: PrefsRepository.Noop()
return DefaultPrefsRepository(
appContext,
customerId = starterArgs.config?.customer?.id,
workContext = workContext
)
}
}
Loading