Skip to content

Conversation

@cttsai-stripe
Copy link
Contributor

@cttsai-stripe cttsai-stripe commented Oct 16, 2025

Summary

Add radarOptions/hCaptcha support for saved payment methods when using client-side confirmation (CSC) with confirmation tokens. This addresses the gap where radarOptions couldn't be passed for saved PMs in the CT flow, only for new PMs (via payment_method_data.radar_options)

Motivation

The solution "tacks radar_options onto the confirm params outside of the CT" as discussed in this Slack thread, since the ConfirmationToken API doesn't support radar_options directly for saved PMs.

Testing

Screenshots

Before After
before screenshot after screenshot

Changelog

cttsai-stripe and others added 5 commits October 16, 2025 15:58
Add tests to verify that hCaptcha tokens (radarOptions) are properly
passed through to confirm params when using saved payment methods with
client-side confirmation (CSC) and confirmation tokens.

Tests verify:
- radarOptions included in confirm params when hCaptchaToken is provided
- radarOptions excluded when hCaptchaToken is null

Updated test helpers to support CSC flow testing by adding parameters
for observing confirmPaymentIntent calls and controlling intent status.

Tests currently fail as implementation is pending.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Committed-By-Agent: claude
Add radarOptions/hCaptcha support for saved payment methods when using
client-side confirmation (CSC) with confirmation tokens. This addresses
the gap where radarOptions couldn't be passed for saved PMs in the CT
flow, only for new PMs (via payment_method_data.radar_options).

Implementation:
- Add radarOptions parameter to ConfirmStripeIntentParamsFactory.create(confirmationTokenId)
- Thread hCaptchaToken through ConfirmationTokenConfirmationInterceptor CSC flow
- Convert hCaptchaToken to RadarOptions when creating confirm params

The solution "tacks radar_options onto the confirm params outside of the CT"
as discussed, since the ConfirmationToken API doesn't support radar_options
directly for saved PMs. This is a CSC-only feature - SSC is not supported
(merchant responsibility).

Tests verify radarOptions are correctly included/excluded based on
hCaptchaToken presence for saved PM + CSC flow.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Committed-By-Agent: claude
Remove the unused observedConfirmParams parameter from test helper functions.
The radarOptions tests now extract params directly from the action result
using asConfirmParams<ConfirmPaymentIntentParams>(), making the Turbine
observation of network calls unnecessary.

Changes:
- Removed observedConfirmParams parameter from createFakeStripeRepositoryForConfirmationToken
- Removed confirmPaymentIntent override (never called in CSC flow)
- Removed observedConfirmParams from runConfirmationTokenInterceptorScenario signature
- Updated radarOptions tests to not pass unused observedParams parameter

This simplifies the test helpers and makes them more focused on what
they actually need to observe.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Committed-By-Agent: claude
@github-actions
Copy link
Contributor

github-actions bot commented Oct 16, 2025

Diffuse output:

OLD: paymentsheet-example-release-master.apk (signature: V1, V2)
NEW: paymentsheet-example-release-pr.apk (signature: V1, V2)

          │           compressed           │          uncompressed          
          ├───────────┬───────────┬────────┼───────────┬───────────┬────────
 APK      │ old       │ new       │ diff   │ old       │ new       │ diff   
──────────┼───────────┼───────────┼────────┼───────────┼───────────┼────────
      dex │   4.8 MiB │   4.8 MiB │ +296 B │  10.7 MiB │  10.7 MiB │ +192 B 
     arsc │   2.6 MiB │   2.6 MiB │    0 B │   2.6 MiB │   2.6 MiB │    0 B 
 manifest │   5.8 KiB │   5.8 KiB │    0 B │  30.6 KiB │  30.6 KiB │    0 B 
      res │ 929.9 KiB │ 929.9 KiB │    0 B │   1.5 MiB │   1.5 MiB │    0 B 
   native │   3.5 MiB │   3.5 MiB │    0 B │   8.5 MiB │   8.5 MiB │    0 B 
    asset │   1.6 MiB │   1.6 MiB │   -1 B │   1.6 MiB │   1.6 MiB │   -1 B 
    other │ 198.7 KiB │ 198.8 KiB │  +15 B │ 375.5 KiB │ 375.5 KiB │    0 B 
──────────┼───────────┼───────────┼────────┼───────────┼───────────┼────────
    total │  13.7 MiB │  13.7 MiB │ +310 B │  25.3 MiB │  25.3 MiB │ +191 B 

         │         raw          │            unique            
         ├───────┬───────┬──────┼───────┬───────┬──────────────
 DEX     │ old   │ new   │ diff │ old   │ new   │ diff         
─────────┼───────┼───────┼──────┼───────┼───────┼──────────────
   files │     2 │     2 │    0 │       │       │              
 strings │ 55121 │ 55121 │    0 │ 50325 │ 50325 │  0 (+1 -1)   
   types │ 19952 │ 19952 │    0 │ 17557 │ 17557 │  0 (+0 -0)   
 classes │ 14822 │ 14822 │    0 │ 14822 │ 14822 │  0 (+0 -0)   
 methods │ 75996 │ 75996 │    0 │ 72416 │ 72416 │  0 (+8 -8)   
  fields │ 49668 │ 49671 │   +3 │ 48202 │ 48205 │ +3 (+13 -10) 

 ARSC    │ old  │ new  │ diff 
─────────┼──────┼──────┼──────
 configs │  242 │  242 │  0   
 entries │ 6365 │ 6365 │  0
APK
     compressed     │    uncompressed    │                                           
───────────┬────────┼───────────┬────────┤                                           
 size      │ diff   │ size      │ diff   │ path                                      
───────────┼────────┼───────────┼────────┼───────────────────────────────────────────
   4.3 MiB │ +297 B │   9.5 MiB │ +192 B │ ∆ classes.dex                             
  55.2 KiB │   +6 B │ 122.5 KiB │    0 B │ ∆ META-INF/CERT.SF                        
  51.7 KiB │   +5 B │ 122.5 KiB │    0 B │ ∆ META-INF/MANIFEST.MF                    
     272 B │   +3 B │     120 B │    0 B │ ∆ META-INF/version-control-info.textproto 
     8 KiB │   -1 B │   7.9 KiB │   -1 B │ ∆ assets/dexopt/baseline.prof             
 553.3 KiB │   -1 B │   1.2 MiB │    0 B │ ∆ classes2.dex                            
   1.2 KiB │   +1 B │   1.2 KiB │    0 B │ ∆ META-INF/CERT.RSA                       
───────────┼────────┼───────────┼────────┼───────────────────────────────────────────
     5 MiB │ +310 B │    11 MiB │ +191 B │ (total)
DEX
STRINGS:

   old   │ new   │ diff      
  ───────┼───────┼───────────
   50325 │ 50325 │ 0 (+1 -1) 
  
  + ~~R8{"backend":"dex","compilation-mode":"release","has-checksums":false,"min-api":21,"pg-map-id":"4c64b36","r8-mode":"full","version":"8.8.34"}
  
  - ~~R8{"backend":"dex","compilation-mode":"release","has-checksums":false,"min-api":21,"pg-map-id":"4ef44b3","r8-mode":"full","version":"8.8.34"}
  

METHODS:

   old   │ new   │ diff      
  ───────┼───────┼───────────
   72416 │ 72416 │ 0 (+8 -8) 
  
  + d8.e c(String, String, p, String, ContinuationImpl) → Object
  + d8.e d(H4, u, p, String, ContinuationImpl) → Object
  + o5.b b(String, C2, r3, h3, o, x3, n) → s
  + o5.b c(String, x3) → s
  + o5.c b(String, C2, r3, h3, o, x3, n) → s
  + o5.c c(String, x3) → s
  + o5.d b(String, C2, r3, h3, o, x3, n) → s
  + o5.d c(String, x3) → s
  
  - d8.e c(String, String, p, ContinuationImpl) → Object
  - d8.e d(H4, u, p, ContinuationImpl) → Object
  - o5.b b(String) → s
  - o5.b c(String, C2, r3, h3, o, x3, n) → s
  - o5.c b(String) → s
  - o5.c c(String, C2, r3, h3, o, x3, n) → s
  - o5.d b(String) → s
  - o5.d c(String, C2, r3, h3, o, x3, n) → s
  

FIELDS:

   old   │ new   │ diff         
  ───────┼───────┼──────────────
   48202 │ 48205 │ +3 (+13 -10) 
  
  + d8.a Y: String
  + d8.a Z: Object
  + d8.a a0: e
  + d8.a b0: int
  + d8.b Y: String
  + d8.b Z: Object
  + d8.b a0: e
  + d8.b b0: int
  + d8.d W: Y
  + d8.d X: p
  + d8.d Y: Object
  + d8.d Z: e
  + d8.d a0: int
  
  - d8.a Y: Object
  - d8.a Z: e
  - d8.a a0: int
  - d8.b Y: Object
  - d8.b Z: e
  - d8.b a0: int
  - d8.d W: p
  - d8.d X: Object
  - d8.d Y: e
  - d8.d Z: int

@cttsai-stripe cttsai-stripe marked this pull request as ready for review October 16, 2025 23:41
@cttsai-stripe cttsai-stripe requested review from a team as code owners October 16, 2025 23:41
abstract fun create(
confirmationTokenId: String
confirmationTokenId: String,
radarOptions: RadarOptions? = null
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'd think we'd always want to specify a value for radar options here. Should we remove the null default?

Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure this looks right we should:

  • New PMs: Don’t set radar options on confirm, it is already present on payment method data when creating a confirmation token
  • Saved PMs: Set radar options at confirm time (not when creating the confirmation token)

Copy link
Contributor

Choose a reason for hiding this comment

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

For saved payment methods we should confirm the intent with the following args:

  • confirmation_token
  • radar_options

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh nvm this is on the create params, not create CT.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah I think this PR has done what is required. I just removed the null default, which is not needed.

intent = intent,
confirmationToken = confirmationToken,
shippingValues = shippingValues,
hCaptchaToken = null,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why is this null?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For new PM, radar options is attached in paymentMethodData.radarOptions if provided. hCaptchaToken = null here means we don't need to send radar options when confirming the intent. Sending radar options when confirming the intent is only required for saved PM.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Probably worth a code comment saying this!

Copy link
Contributor

Choose a reason for hiding this comment

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

Yh, this is accurate. For new PMs, radar options is added to the create_params which will be transformed to paymentMethodData.radarOptions

You can find more details here

@cttsai-stripe cttsai-stripe force-pushed the cttsai/ct-radar-options branch from 7d956f9 to 8a54a8a Compare October 17, 2025 17:24
@cttsai-stripe cttsai-stripe merged commit 2aff4f1 into master Oct 17, 2025
22 checks passed
@cttsai-stripe cttsai-stripe deleted the cttsai/ct-radar-options branch October 17, 2025 17:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants