Skip to content

Commit bb1c623

Browse files
authored
Preserve Link and Google Pay on FC reconfiguration (#11630)
* Preserve Link and Google Pay on FC reconfiguration * Remove outdated tests
1 parent 594cbd0 commit bb1c623

File tree

5 files changed

+49
-163
lines changed

5 files changed

+49
-163
lines changed

paymentsheet-example/detekt-baseline.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
<ID>MagicNumber:DrawablePainter.kt$DrawablePainter$23</ID>
2121
<ID>MagicNumber:DrawablePainter.kt$DrawablePainter$255</ID>
2222
<ID>MagicNumber:Payment.kt$0.5f</ID>
23+
<ID>MagicNumber:SharedPaymentTokenPlaygroundActivity.kt$SharedPaymentTokenPlaygroundActivity$9999L</ID>
2324
<ID>PackageNaming:CompleteFlowActivity.kt$package com.stripe.android.paymentsheet.example.samples.ui.paymentsheet.complete_flow</ID>
2425
<ID>PackageNaming:CompleteFlowViewModel.kt$package com.stripe.android.paymentsheet.example.samples.ui.paymentsheet.complete_flow</ID>
2526
<ID>PackageNaming:CompleteFlowViewState.kt$package com.stripe.android.paymentsheet.example.samples.ui.paymentsheet.complete_flow</ID>

paymentsheet-example/src/main/java/com/stripe/android/paymentsheet/example/playground/PlaygroundState.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ internal sealed interface PlaygroundState : Parcelable {
183183
}
184184

185185
@OptIn(SharedPaymentTokenSessionPreview::class)
186-
fun intentConfiguration(): PaymentSheet.IntentConfiguration {
186+
fun intentConfiguration(amount: Long): PaymentSheet.IntentConfiguration {
187187
return PaymentSheet.IntentConfiguration(
188188
sharedPaymentTokenSessionWithMode = PaymentSheet.IntentConfiguration.Mode.Payment(
189189
amount = amount,
@@ -195,7 +195,7 @@ internal sealed interface PlaygroundState : Parcelable {
195195
networkId = "internal",
196196
externalId = "stripe_test_merchant"
197197
),
198-
paymentMethodTypes = listOf("card", "link", "shop_pay")
198+
paymentMethodTypes = listOf("card", "shop_pay")
199199
)
200200
}
201201
}

paymentsheet-example/src/main/java/com/stripe/android/paymentsheet/example/playground/spt/SharedPaymentTokenPlaygroundActivity.kt

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import android.widget.Toast
99
import androidx.activity.compose.setContent
1010
import androidx.appcompat.app.AppCompatActivity
1111
import androidx.compose.foundation.BorderStroke
12+
import androidx.compose.foundation.clickable
1213
import androidx.compose.foundation.layout.Arrangement
1314
import androidx.compose.foundation.layout.Box
1415
import androidx.compose.foundation.layout.Column
@@ -81,8 +82,10 @@ internal class SharedPaymentTokenPlaygroundActivity : AppCompatActivity() {
8182

8283
setContent {
8384
var confirming by remember { mutableStateOf(false) }
85+
var configuring by remember { mutableStateOf(false) }
8486
var screenContent by remember { mutableStateOf(ScreenContent.Loading) }
8587
var paymentOption by remember { mutableStateOf<PaymentOption?>(null) }
88+
var amount by remember { mutableStateOf(9999L) }
8689

8790
val requester = remember {
8891
SharedPaymentTokenPlaygroundRequester(
@@ -105,13 +108,12 @@ internal class SharedPaymentTokenPlaygroundActivity : AppCompatActivity() {
105108

106109
val configure = remember(flowController) {
107110
{
108-
screenContent = ScreenContent.Loading
109-
110111
flowController.configureWithIntentConfiguration(
111-
intentConfiguration = playgroundState.intentConfiguration(),
112+
intentConfiguration = playgroundState.intentConfiguration(amount),
112113
configuration = playgroundState.paymentSheetConfiguration(),
113114
callback = { success, error ->
114115
paymentOption = flowController.getPaymentOption()
116+
configuring = false
115117

116118
Log.d("TEST", error?.message ?: "")
117119

@@ -138,6 +140,12 @@ internal class SharedPaymentTokenPlaygroundActivity : AppCompatActivity() {
138140
playgroundState.snapshot[WalletButtonsSettingsDefinition]
139141
!= WalletButtonsPlaygroundType.Disabled,
140142
confirming = confirming,
143+
configuring = configuring,
144+
configure = {
145+
amount += 100L
146+
configuring = true
147+
configure()
148+
},
141149
confirm = {
142150
confirming = true
143151
flowController.confirm()
@@ -281,30 +289,44 @@ internal class SharedPaymentTokenPlaygroundActivity : AppCompatActivity() {
281289
modifier = Modifier.padding(vertical = 12.dp),
282290
verticalArrangement = Arrangement.spacedBy(12.dp)
283291
) {
284-
if (parameters.showWalletButtons) {
285-
Box(modifier = horizontalModifier) {
286-
flowController.WalletButtons()
292+
if (parameters.paymentOption == null) {
293+
if (parameters.showWalletButtons) {
294+
Box(modifier = horizontalModifier) {
295+
flowController.WalletButtons()
296+
}
287297
}
288-
}
289298

290-
PrimaryButton(
291-
label = "Pay another way",
292-
enabled = !parameters.confirming,
293-
modifier = horizontalModifier
294-
) {
295-
flowController.presentPaymentOptions()
296-
}
299+
PrimaryButton(
300+
label = "Pay another way",
301+
enabled = !parameters.confirming,
302+
modifier = horizontalModifier
303+
) {
304+
flowController.presentPaymentOptions()
305+
}
297306

298-
parameters.paymentOption?.let {
299307
Divider()
308+
}
300309

310+
parameters.paymentOption?.let {
301311
Summary(
302312
option = it,
303-
modifier = horizontalModifier
313+
modifier = horizontalModifier.clickable {
314+
flowController.presentPaymentOptions()
315+
},
304316
)
305317

306318
Divider()
307319

320+
Button(
321+
onClick = parameters.configure,
322+
enabled = !parameters.configuring && !parameters.confirming,
323+
modifier = horizontalModifier,
324+
) {
325+
Text(if (parameters.configuring) "Configuring…" else "Simulate re-configure")
326+
}
327+
328+
Divider()
329+
308330
PrimaryButton(
309331
label = "Confirm",
310332
enabled = !parameters.confirming,
@@ -416,9 +438,11 @@ internal class SharedPaymentTokenPlaygroundActivity : AppCompatActivity() {
416438
@Immutable
417439
class Parameters(
418440
val confirming: Boolean,
441+
val configuring: Boolean,
419442
val paymentOption: PaymentOption?,
420443
val flowController: PaymentSheet.FlowController,
421444
val showWalletButtons: Boolean,
445+
val configure: () -> Unit,
422446
val confirm: () -> Unit,
423447
val retry: () -> Unit,
424448
)

paymentsheet/src/main/java/com/stripe/android/paymentsheet/flowcontroller/PaymentSelectionUpdater.kt

Lines changed: 6 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@ package com.stripe.android.paymentsheet.flowcontroller
33
import com.stripe.android.common.model.asCommonConfiguration
44
import com.stripe.android.common.model.containsVolatileDifferences
55
import com.stripe.android.lpmfoundations.paymentmethod.PaymentMethodMetadata
6-
import com.stripe.android.lpmfoundations.paymentmethod.WalletType
76
import com.stripe.android.paymentsheet.PaymentSheet
8-
import com.stripe.android.paymentsheet.configType
97
import com.stripe.android.paymentsheet.model.PaymentSelection
108
import com.stripe.android.paymentsheet.state.PaymentSheetState
119
import javax.inject.Inject
@@ -37,20 +35,16 @@ internal class DefaultPaymentSelectionUpdater @Inject constructor() : PaymentSel
3735
val potentialSelection = selection ?: newState.paymentSelection
3836

3937
return potentialSelection?.takeIf {
40-
canUseSelection(it, newState, newConfig, walletButtonsAlreadyShown, selection) &&
41-
previousConfig?.let { previousConfig ->
42-
!previousConfig.asCommonConfiguration()
43-
.containsVolatileDifferences(newConfig.asCommonConfiguration())
44-
} != false
38+
canUseSelection(it, newState) && previousConfig?.let { previousConfig ->
39+
!previousConfig.asCommonConfiguration()
40+
.containsVolatileDifferences(newConfig.asCommonConfiguration())
41+
} != false
4542
}
4643
}
4744

4845
private fun canUseSelection(
4946
potentialSelection: PaymentSelection,
5047
state: PaymentSheetState.Full,
51-
configuration: PaymentSheet.Configuration,
52-
walletButtonsAlreadyShown: Boolean,
53-
selection: PaymentSelection?,
5448
): Boolean {
5549
// The types that are allowed for this intent, as returned by the backend
5650
val allowedTypes = state.paymentMethodMetadata.supportedPaymentMethodTypes()
@@ -70,12 +64,10 @@ internal class DefaultPaymentSelectionUpdater @Inject constructor() : PaymentSel
7064
code in allowedTypes && paymentMethod in (state.customer?.paymentMethods ?: emptyList())
7165
}
7266
is PaymentSelection.GooglePay -> {
73-
state.paymentMethodMetadata.isGooglePayReady &&
74-
walletCanBeUsed(potentialSelection, configuration, walletButtonsAlreadyShown, selection)
67+
state.paymentMethodMetadata.isGooglePayReady
7568
}
7669
is PaymentSelection.Link -> {
77-
state.paymentMethodMetadata.linkState != null &&
78-
walletCanBeUsed(potentialSelection, configuration, walletButtonsAlreadyShown, selection)
70+
state.paymentMethodMetadata.linkState != null
7971
}
8072
is PaymentSelection.ExternalPaymentMethod -> {
8173
state.paymentMethodMetadata.isExternalPaymentMethod(potentialSelection.type)
@@ -89,40 +81,6 @@ internal class DefaultPaymentSelectionUpdater @Inject constructor() : PaymentSel
8981
}
9082
}
9183

92-
private fun walletCanBeUsed(
93-
potentialSelection: PaymentSelection,
94-
configuration: PaymentSheet.Configuration,
95-
walletButtonsAlreadyShown: Boolean,
96-
selection: PaymentSelection?
97-
): Boolean {
98-
if (!configuration.walletButtons.willDisplayExternally && !walletButtonsAlreadyShown) {
99-
return true
100-
}
101-
102-
val walletButtonsViewVisibility = configuration.walletButtons.visibility.walletButtonsView
103-
val walletTypesDisplayedExternally = visibleWallets(walletButtonsViewVisibility)
104-
105-
val walletType = when (potentialSelection) {
106-
is PaymentSelection.GooglePay -> WalletType.GooglePay
107-
is PaymentSelection.Link -> WalletType.Link
108-
else -> null
109-
}
110-
111-
// If this selection is the existing selection (likely selected externally)
112-
// AND specific wallets are hidden externally, preserve it
113-
if (
114-
potentialSelection == selection &&
115-
walletType in walletTypesDisplayedExternally &&
116-
hasHiddenWallets(walletButtonsViewVisibility)
117-
) {
118-
return true
119-
}
120-
121-
// Allow wallets that are NOT in the external list
122-
// (because external wallets are handled separately to avoid duplication)
123-
return walletType != null && !walletTypesDisplayedExternally.contains(walletType)
124-
}
125-
12684
private fun shouldAskForMandate(
12785
currentSelection: PaymentSelection.New,
12886
metadata: PaymentMethodMetadata,
@@ -137,31 +95,6 @@ internal class DefaultPaymentSelectionUpdater @Inject constructor() : PaymentSel
13795
false
13896
}
13997
}
140-
141-
private fun hasHiddenWallets(
142-
walletButtonsViewVisibility: Map<
143-
PaymentSheet.WalletButtonsConfiguration.Wallet,
144-
PaymentSheet.WalletButtonsConfiguration.WalletButtonsViewVisibility
145-
>
146-
): Boolean {
147-
return walletButtonsViewVisibility.isNotEmpty() && walletButtonsViewVisibility.any { (_, visibility) ->
148-
visibility == PaymentSheet.WalletButtonsConfiguration.WalletButtonsViewVisibility.Never
149-
}
150-
}
151-
152-
private fun visibleWallets(
153-
visibility: Map<
154-
PaymentSheet.WalletButtonsConfiguration.Wallet,
155-
PaymentSheet.WalletButtonsConfiguration.WalletButtonsViewVisibility
156-
>
157-
): List<WalletType> {
158-
return WalletType.entries.filter { walletType ->
159-
val configuredVisibility = visibility[walletType.configType]
160-
161-
configuredVisibility == null || configuredVisibility ==
162-
PaymentSheet.WalletButtonsConfiguration.WalletButtonsViewVisibility.Always
163-
}
164-
}
16598
}
16699

167100
private val PaymentSelection.New.customerAcknowledgedMandate: Boolean

paymentsheet/src/test/java/com/stripe/android/paymentsheet/flowcontroller/PaymentSelectionUpdaterTest.kt

Lines changed: 0 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -411,78 +411,6 @@ class PaymentSelectionUpdaterTest {
411411
assertThat(result).isEqualTo(null)
412412
}
413413

414-
@Test
415-
fun `If wallet buttons are already shown and existing selection is Google Pay, should be null`() {
416-
val updater = createUpdater()
417-
418-
val result = updater(
419-
selection = PaymentSelection.GooglePay,
420-
previousConfig = null,
421-
newState = mockPaymentSheetStateWithPaymentIntent(),
422-
newConfig = defaultPaymentSheetConfiguration,
423-
walletButtonsAlreadyShown = true,
424-
)
425-
426-
assertThat(result).isNull()
427-
}
428-
429-
@Test
430-
fun `If wallet buttons are already shown and existing selection is Link, should be null`() {
431-
val updater = createUpdater()
432-
433-
val result = updater(
434-
selection = PaymentSelection.Link(linkExpressMode = LinkExpressMode.DISABLED),
435-
previousConfig = null,
436-
newState = mockPaymentSheetStateWithPaymentIntent(),
437-
newConfig = defaultPaymentSheetConfiguration,
438-
walletButtonsAlreadyShown = true,
439-
)
440-
441-
assertThat(result).isNull()
442-
}
443-
444-
@OptIn(WalletButtonsPreview::class)
445-
@Test
446-
fun `If using wallet buttons config option and existing selection is Google Pay, should be null`() {
447-
val updater = createUpdater()
448-
449-
val result = updater(
450-
selection = PaymentSelection.GooglePay,
451-
previousConfig = null,
452-
newState = mockPaymentSheetStateWithPaymentIntent(),
453-
newConfig = defaultPaymentSheetConfiguration.newBuilder()
454-
.walletButtons(
455-
PaymentSheet.WalletButtonsConfiguration(
456-
willDisplayExternally = true,
457-
),
458-
).build(),
459-
walletButtonsAlreadyShown = false,
460-
)
461-
462-
assertThat(result).isNull()
463-
}
464-
465-
@OptIn(WalletButtonsPreview::class)
466-
@Test
467-
fun `If using wallet buttons config option and existing selection is Link, should be null`() {
468-
val updater = createUpdater()
469-
470-
val result = updater(
471-
selection = PaymentSelection.Link(linkExpressMode = LinkExpressMode.DISABLED),
472-
previousConfig = null,
473-
newState = mockPaymentSheetStateWithPaymentIntent(),
474-
newConfig = defaultPaymentSheetConfiguration.newBuilder()
475-
.walletButtons(
476-
PaymentSheet.WalletButtonsConfiguration(
477-
willDisplayExternally = true,
478-
),
479-
).build(),
480-
walletButtonsAlreadyShown = false,
481-
)
482-
483-
assertThat(result).isNull()
484-
}
485-
486414
@OptIn(WalletButtonsPreview::class)
487415
@Test
488416
fun `If using wallet buttons config option with only GPay visible and selection is Link, should be Link`() {

0 commit comments

Comments
 (0)