Skip to content

Commit 46f4e54

Browse files
authored
Limit to Passive Challenge And Attestation to New Cards only (#12055)
1 parent 3c26dc1 commit 46f4e54

File tree

10 files changed

+92
-610
lines changed

10 files changed

+92
-610
lines changed

paymentsheet/src/androidTest/java/com/stripe/android/paymentsheet/ConfirmWithAttestationTest.kt

Lines changed: 0 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import com.stripe.android.core.utils.FeatureFlags
1212
import com.stripe.android.core.utils.urlEncode
1313
import com.stripe.android.networktesting.NetworkRule
1414
import com.stripe.android.networktesting.RequestMatchers.bodyPart
15-
import com.stripe.android.networktesting.RequestMatchers.host
1615
import com.stripe.android.networktesting.RequestMatchers.method
1716
import com.stripe.android.networktesting.RequestMatchers.path
1817
import com.stripe.android.networktesting.ResponseReplacement
@@ -53,16 +52,6 @@ internal class ConfirmWithAttestationTest {
5352
setupNewPaymentMethodTest(testContext)
5453
}
5554

56-
@Test
57-
fun savedPaymentMethod_withAttestationEnabled_includesAndroidVerificationObjectInConfirmRequest() =
58-
runProductIntegrationTest(
59-
networkRule = networkRule,
60-
integrationType = ProductIntegrationType.PaymentSheet,
61-
resultCallback = ::assertCompleted,
62-
) { testContext ->
63-
setupSavedPaymentMethodTest(testContext)
64-
}
65-
6655
@Test
6756
fun paymentMethodCreation_withAttestationEnabled_includesAndroidVerificationObjectInCreateRequest() =
6857
runProductIntegrationTest(
@@ -92,42 +81,8 @@ internal class ConfirmWithAttestationTest {
9281
paymentSheetPage.clickPrimaryButton()
9382
}
9483

95-
private fun setupSavedPaymentMethodTest(testContext: ProductIntegrationTestRunnerContext) {
96-
enqueueElementsSessionWithAttestationForSavedCards(networkRule)
97-
enqueuePaymentMethodsGet()
98-
99-
intendedActivityResult(AttestationActivityResult.Success(ATTESTATION_TOKEN))
100-
101-
testContext.launch(configuration = createCustomerConfiguration())
102-
103-
enqueuePaymentIntentConfirmWithAttestation(SAVED_PM_ATTESTATION_TOKEN_PATH)
104-
105-
val paymentSheetPage = createPaymentSheetPage()
106-
paymentSheetPage.fillCvcRecollection(CVC_VALUE)
107-
paymentSheetPage.clickPrimaryButton()
108-
}
109-
11084
private fun createPaymentSheetPage() = PaymentSheetPage(testRules.compose)
11185

112-
private fun createCustomerConfiguration() = PaymentSheet.Configuration(
113-
merchantDisplayName = MERCHANT_DISPLAY_NAME,
114-
customer = PaymentSheet.CustomerConfiguration(
115-
id = CUSTOMER_ID,
116-
ephemeralKeySecret = EPHEMERAL_KEY_SECRET,
117-
),
118-
paymentMethodLayout = PaymentSheet.PaymentMethodLayout.Horizontal
119-
)
120-
121-
private fun enqueuePaymentMethodsGet() {
122-
networkRule.enqueue(
123-
host(API_HOST),
124-
method("GET"),
125-
path("/v1/payment_methods"),
126-
) { response ->
127-
response.testBodyFromFile(PAYMENT_METHODS_GET_FILE)
128-
}
129-
}
130-
13186
private fun setupPaymentMethodCreateWithDeferredTest(testContext: ProductIntegrationTestRunnerContext) {
13287
enqueueElementsSessionWithAttestation(networkRule, isDeferredIntent = true)
13388

@@ -164,14 +119,6 @@ internal class ConfirmWithAttestationTest {
164119
)
165120
}
166121

167-
private fun enqueueElementsSessionWithAttestationForSavedCards(networkRule: NetworkRule) {
168-
enqueueElementsSessionWithAttestationEnabled(
169-
networkRule = networkRule,
170-
baseFile = CVC_RECOLLECTION_ELEMENTS_SESSION_FILE,
171-
additionalReplacements = emptyList()
172-
)
173-
}
174-
175122
private fun enqueueElementsSessionWithAttestationEnabled(
176123
networkRule: NetworkRule,
177124
baseFile: String,
@@ -248,19 +195,12 @@ internal class ConfirmWithAttestationTest {
248195
"payment_method_data[radar_options][android_verification_object][android_verification_token]"
249196
private const val SAVED_PM_ATTESTATION_TOKEN_PATH =
250197
"radar_options[android_verification_object][android_verification_token]"
251-
private const val CVC_VALUE = "123"
252-
private const val MERCHANT_DISPLAY_NAME = "Merchant, Inc."
253-
private const val CUSTOMER_ID = "cus_1"
254-
private const val EPHEMERAL_KEY_SECRET = "ek_123"
255-
private const val API_HOST = "api.stripe.com"
256198

257199
private const val STANDARD_ELEMENTS_SESSION_FILE = "elements-sessions-with_pi_and_default_pms_enabled.json"
258200
private const val DEFERRED_INTENT_ELEMENTS_SESSION_FILE =
259201
"elements-sessions-deferred_intent_and_default_pms_enabled.json"
260-
private const val CVC_RECOLLECTION_ELEMENTS_SESSION_FILE = "elements-sessions-requires_cvc_recollection.json"
261202
private const val PAYMENT_INTENT_CONFIRM_FILE = "payment-intent-confirm.json"
262203
private const val PAYMENT_METHOD_CREATE_FILE = "payment-methods-create.json"
263-
private const val PAYMENT_METHODS_GET_FILE = "payment-methods-get-success.json"
264204
private const val PAYMENT_INTENT_GET_FILE = "payment-intent-get-requires_payment_method.json"
265205

266206
private const val PAYMENT_INTENT_CONFIRM_PATH = "/v1/payment_intents/pi_example/confirm"

paymentsheet/src/androidTest/java/com/stripe/android/paymentsheet/HCaptchaTokenTest.kt

Lines changed: 1 addition & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,15 @@ import com.stripe.android.core.utils.FeatureFlags
55
import com.stripe.android.core.utils.urlEncode
66
import com.stripe.android.networktesting.NetworkRule
77
import com.stripe.android.networktesting.RequestMatchers.bodyPart
8-
import com.stripe.android.networktesting.RequestMatchers.host
98
import com.stripe.android.networktesting.RequestMatchers.method
109
import com.stripe.android.networktesting.RequestMatchers.path
1110
import com.stripe.android.networktesting.ResponseReplacement
1211
import com.stripe.android.networktesting.testBodyFromFile
1312
import com.stripe.android.paymentsheet.utils.ConfirmationType
13+
import com.stripe.android.paymentsheet.utils.ProductIntegrationTestRunnerContext
1414
import com.stripe.android.paymentsheet.utils.ProductIntegrationType
1515
import com.stripe.android.paymentsheet.utils.TestRules
1616
import com.stripe.android.paymentsheet.utils.assertCompleted
17-
import com.stripe.android.paymentsheet.utils.ProductIntegrationTestRunnerContext
1817
import com.stripe.android.paymentsheet.utils.runProductIntegrationTest
1918
import com.stripe.android.testing.FeatureFlagTestRule
2019
import org.junit.Rule
@@ -45,16 +44,6 @@ internal class HCaptchaTokenTest {
4544
setupNewPaymentMethodTest(testContext)
4645
}
4746

48-
@Test
49-
fun savedPaymentMethod_withPassiveCaptchaEnabled_includesHCaptchaTokenInConfirmRequest() =
50-
runProductIntegrationTest(
51-
networkRule = networkRule,
52-
integrationType = ProductIntegrationType.PaymentSheet,
53-
resultCallback = ::assertCompleted,
54-
) { testContext ->
55-
setupSavedPaymentMethodTest(testContext)
56-
}
57-
5847
@Test
5948
fun paymentMethodCreation_withPassiveCaptchaEnabled_includesHCaptchaTokenInCreateRequest() =
6049
runProductIntegrationTest(
@@ -81,19 +70,6 @@ internal class HCaptchaTokenTest {
8170
paymentSheetPage.clickPrimaryButton()
8271
}
8372

84-
private fun setupSavedPaymentMethodTest(testContext: ProductIntegrationTestRunnerContext) {
85-
enqueueElementsSessionWithPassiveCaptchaForSavedCards(networkRule)
86-
enqueuePaymentMethodsGet()
87-
88-
testContext.launch(configuration = createCustomerConfiguration())
89-
90-
enqueuePaymentIntentConfirmWithHCaptcha(SAVED_PM_HCAPTCHA_TOKEN_PATH)
91-
92-
val paymentSheetPage = createPaymentSheetPage()
93-
paymentSheetPage.fillCvcRecollection(CVC_VALUE)
94-
paymentSheetPage.clickPrimaryButton()
95-
}
96-
9773
private fun setupPaymentMethCreateWithDeferredTest(testContext: ProductIntegrationTestRunnerContext) {
9874
enqueueElementsSessionWithPassiveCaptcha(networkRule, isDeferredIntent = true)
9975
testContext.launch(isDeferredIntent = true)
@@ -114,25 +90,6 @@ internal class HCaptchaTokenTest {
11490

11591
private fun createPaymentSheetPage() = PaymentSheetPage(testRules.compose)
11692

117-
private fun createCustomerConfiguration() = PaymentSheet.Configuration(
118-
merchantDisplayName = MERCHANT_DISPLAY_NAME,
119-
customer = PaymentSheet.CustomerConfiguration(
120-
id = CUSTOMER_ID,
121-
ephemeralKeySecret = EPHEMERAL_KEY_SECRET,
122-
),
123-
paymentMethodLayout = PaymentSheet.PaymentMethodLayout.Horizontal
124-
)
125-
126-
private fun enqueuePaymentMethodsGet() {
127-
networkRule.enqueue(
128-
host(API_HOST),
129-
method("GET"),
130-
path("/v1/payment_methods"),
131-
) { response ->
132-
response.testBodyFromFile(PAYMENT_METHODS_GET_FILE)
133-
}
134-
}
135-
13693
private fun enqueueDeferredIntentRequests() {
13794
networkRule.enqueue(
13895
method("GET"),
@@ -164,14 +121,6 @@ internal class HCaptchaTokenTest {
164121
)
165122
}
166123

167-
private fun enqueueElementsSessionWithPassiveCaptchaForSavedCards(networkRule: NetworkRule) {
168-
enqueueElementsSessionWithCaptchaEnabled(
169-
networkRule = networkRule,
170-
baseFile = CVC_RECOLLECTION_ELEMENTS_SESSION_FILE,
171-
additionalReplacements = emptyList()
172-
)
173-
}
174-
175124
private fun enqueueElementsSessionWithCaptchaEnabled(
176125
networkRule: NetworkRule,
177126
baseFile: String,
@@ -225,19 +174,12 @@ internal class HCaptchaTokenTest {
225174
private const val HCAPTCHA_SITE_KEY = "20000000-ffff-ffff-ffff-000000000002"
226175
private const val NEW_PM_HCAPTCHA_TOKEN_PATH = "payment_method_data[radar_options][hcaptcha_token]"
227176
private const val SAVED_PM_HCAPTCHA_TOKEN_PATH = "radar_options[hcaptcha_token]"
228-
private const val CVC_VALUE = "123"
229-
private const val MERCHANT_DISPLAY_NAME = "Merchant, Inc."
230-
private const val CUSTOMER_ID = "cus_1"
231-
private const val EPHEMERAL_KEY_SECRET = "ek_123"
232-
private const val API_HOST = "api.stripe.com"
233177

234178
private const val STANDARD_ELEMENTS_SESSION_FILE = "elements-sessions-with_pi_and_default_pms_enabled.json"
235179
private const val DEFERRED_INTENT_ELEMENTS_SESSION_FILE =
236180
"elements-sessions-deferred_intent_and_default_pms_enabled.json"
237-
private const val CVC_RECOLLECTION_ELEMENTS_SESSION_FILE = "elements-sessions-requires_cvc_recollection.json"
238181
private const val PAYMENT_INTENT_CONFIRM_FILE = "payment-intent-confirm.json"
239182
private const val PAYMENT_METHOD_CREATE_FILE = "payment-methods-create.json"
240-
private const val PAYMENT_METHODS_GET_FILE = "payment-methods-get-success.json"
241183
private const val PAYMENT_INTENT_GET_FILE = "payment-intent-get-requires_payment_method.json"
242184

243185
private const val PAYMENT_INTENT_CONFIRM_PATH = "/v1/payment_intents/pi_example/confirm"

paymentsheet/src/main/java/com/stripe/android/paymentelement/confirmation/PaymentMethodConfirmationOption.kt

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.stripe.android.paymentelement.confirmation
22

3+
import com.stripe.android.model.PaymentMethod
34
import com.stripe.android.model.PaymentMethodCreateParams
45
import com.stripe.android.model.PaymentMethodExtraParams
56
import com.stripe.android.model.PaymentMethodOptionsParams
@@ -9,8 +10,6 @@ import com.stripe.android.paymentsheet.PaymentSheet
910
import kotlinx.parcelize.Parcelize
1011

1112
internal sealed interface PaymentMethodConfirmationOption : ConfirmationHandler.Option {
12-
val passiveChallengeComplete: Boolean
13-
val attestationComplete: Boolean
1413
val optionsParams: PaymentMethodOptionsParams?
1514

1615
fun updatedForDeferredIntent(
@@ -26,8 +25,6 @@ internal sealed interface PaymentMethodConfirmationOption : ConfirmationHandler.
2625
val originatedFromWallet: Boolean = false,
2726
val hCaptchaToken: String? = null,
2827
val attestationToken: String? = null,
29-
override val passiveChallengeComplete: Boolean = false,
30-
override val attestationComplete: Boolean = false,
3128
) : PaymentMethodConfirmationOption {
3229
override fun updatedForDeferredIntent(
3330
intentConfiguration: PaymentSheet.IntentConfiguration,
@@ -48,8 +45,8 @@ internal sealed interface PaymentMethodConfirmationOption : ConfirmationHandler.
4845
override val optionsParams: PaymentMethodOptionsParams?,
4946
val extraParams: PaymentMethodExtraParams?,
5047
val shouldSave: Boolean,
51-
override val passiveChallengeComplete: Boolean = false,
52-
override val attestationComplete: Boolean = false,
48+
val passiveChallengeComplete: Boolean = false,
49+
val attestationComplete: Boolean = false,
5350
) : PaymentMethodConfirmationOption {
5451

5552
override fun updatedForDeferredIntent(

paymentsheet/src/main/java/com/stripe/android/paymentelement/confirmation/attestation/AttestationConfirmationDefinition.kt

Lines changed: 23 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,15 @@ internal class AttestationConfirmationDefinition @Inject constructor(
3434
@Named(PUBLISHABLE_KEY) private val publishableKeyProvider: () -> String,
3535
@Named(PRODUCT_USAGE) private val productUsage: Set<String>
3636
) : ConfirmationDefinition<
37-
PaymentMethodConfirmationOption,
37+
PaymentMethodConfirmationOption.New,
3838
ActivityResultLauncher<AttestationActivityContract.Args>,
3939
AttestationActivityContract.Args,
4040
AttestationActivityResult
4141
> {
4242
override val key = "Attestation"
4343

44-
override fun option(confirmationOption: ConfirmationHandler.Option): PaymentMethodConfirmationOption? {
45-
return confirmationOption as? PaymentMethodConfirmationOption
44+
override fun option(confirmationOption: ConfirmationHandler.Option): PaymentMethodConfirmationOption.New? {
45+
return confirmationOption as? PaymentMethodConfirmationOption.New
4646
}
4747

4848
override fun bootstrap(paymentMethodMetadata: PaymentMethodMetadata) {
@@ -63,16 +63,17 @@ internal class AttestationConfirmationDefinition @Inject constructor(
6363
}
6464

6565
override fun canConfirm(
66-
confirmationOption: PaymentMethodConfirmationOption,
66+
confirmationOption: PaymentMethodConfirmationOption.New,
6767
confirmationArgs: ConfirmationHandler.Args
6868
): Boolean {
69-
return confirmationArgs.paymentMethodMetadata.attestOnIntentConfirmation &&
69+
return confirmationOption.createParams.typeCode == "card" &&
70+
confirmationArgs.paymentMethodMetadata.attestOnIntentConfirmation &&
7071
confirmationOption.attestationComplete.not() &&
7172
confirmationOption.hasToken().not()
7273
}
7374

7475
override fun toResult(
75-
confirmationOption: PaymentMethodConfirmationOption,
76+
confirmationOption: PaymentMethodConfirmationOption.New,
7677
confirmationArgs: ConfirmationHandler.Args,
7778
deferredIntentConfirmationType: DeferredIntentConfirmationType?,
7879
result: AttestationActivityResult
@@ -103,14 +104,14 @@ internal class AttestationConfirmationDefinition @Inject constructor(
103104
override fun launch(
104105
launcher: ActivityResultLauncher<AttestationActivityContract.Args>,
105106
arguments: AttestationActivityContract.Args,
106-
confirmationOption: PaymentMethodConfirmationOption,
107+
confirmationOption: PaymentMethodConfirmationOption.New,
107108
confirmationArgs: ConfirmationHandler.Args
108109
) {
109110
launcher.launch(arguments)
110111
}
111112

112113
override suspend fun action(
113-
confirmationOption: PaymentMethodConfirmationOption,
114+
confirmationOption: PaymentMethodConfirmationOption.New,
114115
confirmationArgs: ConfirmationHandler.Args
115116
): ConfirmationDefinition.Action<AttestationActivityContract.Args> {
116117
if (confirmationArgs.paymentMethodMetadata.attestOnIntentConfirmation) {
@@ -137,30 +138,20 @@ internal class AttestationConfirmationDefinition @Inject constructor(
137138
)
138139
}
139140

140-
private fun PaymentMethodConfirmationOption.attachToken(token: String?): PaymentMethodConfirmationOption {
141-
return when (this) {
142-
is PaymentMethodConfirmationOption.New -> {
143-
copy(
144-
createParams = createParams.copy(
145-
radarOptions = token?.let {
146-
RadarOptions(
147-
hCaptchaToken = null,
148-
androidVerificationObject = AndroidVerificationObject(
149-
androidVerificationToken = it
150-
)
151-
)
152-
}
153-
),
154-
attestationComplete = true
155-
)
156-
}
157-
is PaymentMethodConfirmationOption.Saved -> {
158-
copy(
159-
attestationToken = token,
160-
attestationComplete = true
161-
)
162-
}
163-
}
141+
private fun PaymentMethodConfirmationOption.New.attachToken(token: String?): PaymentMethodConfirmationOption {
142+
return copy(
143+
createParams = createParams.copy(
144+
radarOptions = token?.let {
145+
RadarOptions(
146+
hCaptchaToken = null,
147+
androidVerificationObject = AndroidVerificationObject(
148+
androidVerificationToken = it
149+
)
150+
)
151+
}
152+
),
153+
attestationComplete = true
154+
)
164155
}
165156

166157
private fun PaymentMethodConfirmationOption.hasToken(): Boolean {

0 commit comments

Comments
 (0)