Skip to content

Conversation

@cttsai-stripe
Copy link
Contributor

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

Summary

Make Google Payment Card Recognition (GPCR) API the default card scanning implementation by removing the cardScanGooglePayMigration feature flag.

Motivation

Testing

  • Added tests
  • Modified tests
  • Manually verified

Screenshots

Before After
before screenshot after screenshot

Changelog

@github-actions
Copy link
Contributor

github-actions bot commented Oct 20, 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 │ +6.1 KiB │  10.7 MiB │  10.7 MiB │ +14.8 KiB 
     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.2 KiB │   1.6 MiB │   1.6 MiB │  +1.2 KiB 
    other │ 198.7 KiB │ 198.7 KiB │     -1 B │ 375.5 KiB │ 375.5 KiB │       0 B 
──────────┼───────────┼───────────┼──────────┼───────────┼───────────┼───────────
    total │  13.7 MiB │  13.7 MiB │ +7.2 KiB │  25.3 MiB │  25.3 MiB │ +15.9 KiB 

         │         raw          │              unique               
         ├───────┬───────┬──────┼───────┬───────┬───────────────────
 DEX     │ old   │ new   │ diff │ old   │ new   │ diff              
─────────┼───────┼───────┼──────┼───────┼───────┼───────────────────
   files │     2 │     2 │    0 │       │       │                   
 strings │ 55134 │ 55332 │ +198 │ 50335 │ 50378 │ +43 (+110 -67)    
   types │ 19958 │ 20106 │ +148 │ 17560 │ 17580 │ +20 (+83 -63)     
 classes │ 14824 │ 14841 │  +17 │ 14824 │ 14841 │ +17 (+28 -11)     
 methods │ 76010 │ 76183 │ +173 │ 72429 │ 72499 │ +70 (+2604 -2534) 
  fields │ 49686 │ 49806 │ +120 │ 48216 │ 48248 │ +32 (+1958 -1926) 

 ARSC    │ old  │ new  │ diff 
─────────┼──────┼──────┼──────
 configs │  242 │  242 │  0   
 entries │ 6367 │ 6367 │  0
APK
     compressed      │     uncompressed      │                                           
──────────┬──────────┼───────────┬───────────┤                                           
 size     │ diff     │ size      │ diff      │ path                                      
──────────┼──────────┼───────────┼───────────┼───────────────────────────────────────────
  564 KiB │ +9.8 KiB │   1.3 MiB │ +19.7 KiB │ ∆ classes2.dex                            
  4.3 MiB │ -3.7 KiB │   9.5 MiB │    -5 KiB │ ∆ classes.dex                             
  9.2 KiB │ +1.2 KiB │     9 KiB │  +1.2 KiB │ ∆ assets/dexopt/baseline.prof             
  1.2 KiB │     +5 B │     1 KiB │      +5 B │ ∆ assets/dexopt/baseline.profm            
 55.2 KiB │     -4 B │ 122.5 KiB │       0 B │ ∆ META-INF/CERT.SF                        
 51.7 KiB │     +3 B │ 122.5 KiB │       0 B │ ∆ META-INF/MANIFEST.MF                    
    272 B │     +1 B │     120 B │       0 B │ ∆ META-INF/version-control-info.textproto 
  1.2 KiB │     -1 B │   1.2 KiB │       0 B │ ∆ META-INF/CERT.RSA                       
──────────┼──────────┼───────────┼───────────┼───────────────────────────────────────────
    5 MiB │ +7.2 KiB │    11 MiB │ +15.9 KiB │ (total)
DEX
STRINGS:

   old   │ new   │ diff           
  ───────┼───────┼────────────────
   50335 │ 50378 │ +43 (+110 -67) 
  
  + , expirationMonth=
  + , expirationYear=
  + LR3/w;
  + LR3/x;
  + LR3/y;
  + LR5/i;
  + LY6/M;
  + LZ0/L;
  + La6/q;
  + Laa/d;
  + Laa/e;
  + Laa/f;
  + Laa/g;
  + Laa/h;
  + Laa/i;
  + Laa/j;
  + Laa/k;
  + Laa/l;
  + Laa/m;
  + Laa/n;
  + Laa/o;
  + Lb8/d;
  + Lda/f2;
  + Leb/f;
  + Lf4/c;
  + Ln6/D0;
  + Lw6/k;
  + RemoteException during getPaymentCardRecognitionIntent
  + [LR3/m;
  + [LR3/n;
  + [LR3/r;
  + [LR3/s;
  + [LR3/w;
  + [LR3/x;
  + [LR3/y;
  + [LR5/d;
  + [LR5/g;
  + [LR7/b;
  + [LS6/B0;
  + [LS6/G0;
  + [LS6/J0;
  + [LT7/p;
  + [LY6/L;
  + [LY7/b;
  + [LY7/d;
  + [La6/l;
  + [Laa/b;
  + [Lb8/d;
  + [Lba/b;
  + [Lda/J1;
  + [Lda/M1;
  + [Lda/S1;
  + [Lda/Z1;
  + [Lda/d2;
  + [Lda/f2;
  + [Lf4/b;
  + [Lf8/g;
  + [Lk7/W0;
  + [Lk7/j0;
  + [Lk7/r0;
  + [Lk7/z0;
  + [Ln6/M;
  + [Ln6/c;
  + [Ln6/k;
  + [Ln8/b;
  + [Ln9/X0;
  + [Lq8/j;
  + [Lu9/C;
  + [Lw7/J;
  + [Lw7/Q;
  + activityLauncher
  + cs_cardscan_api_check_failed
  + cs_cardscan_api_check_succeeded
  + cs_cardscan_cancel
  + cs_cardscan_failed
  + cs_cardscan_scan_started
  + cs_cardscan_success
  + eventsReporter
  + getIntentSender(...)
  + getPaymentCardRecognitionPendingIntent(...)
  + implementation
  + intentSenderRequest
  + mc_cardscan_api_check_failed
  + mc_cardscan_api_check_succeeded
  + mc_cardscan_cancel
  + mc_cardscan_failed
  + mc_cardscan_scan_started
  + mc_cardscan_success
  + paymentCardRecognitionClient
  + ~~R8{"backend":"dex","compilation-mode":"release","has-checksums":false,"min-api":21,"pg-map-id":"19e20f6","r8-mode":"full","version":"8.8.34"}
  + , hasData=
  + Invalid activity result: code=
  + Lio/sentry/H2;
  + Lub/A;
  + Lya/p0;
  + PAN not found in PaymentCardRecognitionResult
  + [Lio/sentry/A2;
  + [Lio/sentry/W1;
  + [Lio/sentry/a2;
  + [Lio/sentry/k2;
  + [Lio/sentry/n2;
  + [Lio/sentry/p2;
  + [Lio/sentry/u2;
  + [Lub/A;
  + [Lya/N;
  + [Lya/c0;
  + [Lya/e0;
  + [Lya/n0;
  + [Lz5/a;
  + com.google.android.gms.wallet.PaymentCardRecognitionResult
  
  - CardScanEventsReporter not provided
  - LR7/j;
  - LY7/h;
  - LZ1/m;
  - Lba/d;
  - Lf8/i;
  - Li6/O;
  - Lio/sentry/android/core/internal/util/p;
  - Ln8/f;
  - Lv4/l;
  - Lw7/T;
  - Lz5/y;
  - Use Google Payment Card Recognition API for Card Scan
  - VLLZLLLL
  - [LR3/p;
  - [LR3/t;
  - [LR3/u;
  - [LR3/v;
  - [LR5/c;
  - [LR5/f;
  - [LR7/i;
  - [LS6/A0;
  - [LS6/F0;
  - [LS6/I0;
  - [LT7/m;
  - [LY6/I;
  - [LY7/c;
  - [LY7/g;
  - [La6/i;
  - [Laa/a;
  - [Lb8/c;
  - [Lba/c;
  - [Lda/I1;
  - [Lda/L1;
  - [Lda/R1;
  - [Lda/Y1;
  - [Lda/c2;
  - [Lda/e2;
  - [Lf4/a;
  - [Lf8/i;
  - [Lk7/U0;
  - [Lk7/i0;
  - [Lk7/q0;
  - [Lk7/y0;
  - [Ln6/L;
  - [Ln6/b;
  - [Ln6/j;
  - [Ln8/d;
  - [Ln9/W0;
  - [Lq8/i;
  - [Lu9/A;
  - [Lw7/K;
  - [Lw7/S;
  - [Lya/M;
  - [Lya/m0;
  - [Lz5/b;
  - ~~R8{"backend":"dex","compilation-mode":"release","has-checksums":false,"min-api":21,"pg-map-id":"ec36ee5","r8-mode":"full","version":"8.8.34"}
  - [Lio/sentry/U1;
  - [Lio/sentry/Z1;
  - [Lio/sentry/j2;
  - [Lio/sentry/m2;
  - [Lio/sentry/o2;
  - [Lio/sentry/t2;
  - [Lio/sentry/z2;
  - [Lub/z;
  - [Lya/b0;
  - [Lya/d0;
  

TYPES:

   old   │ new   │ diff          
  ───────┼───────┼───────────────
   17560 │ 17580 │ +20 (+83 -63) 
  
  + LR3/w;
  + LR3/x;
  + LR3/y;
  + LR5/i;
  + LY6/M;
  + LZ0/L;
  + La6/q;
  + Laa/d;
  + Laa/e;
  + Laa/f;
  + Laa/g;
  + Laa/h;
  + Laa/i;
  + Laa/j;
  + Laa/k;
  + Laa/l;
  + Laa/m;
  + Laa/n;
  + Laa/o;
  + Lb8/d;
  + Lda/f2;
  + Leb/f;
  + Lf4/c;
  + Ln6/D0;
  + Lw6/k;
  + [LR3/m;
  + [LR3/n;
  + [LR3/r;
  + [LR3/s;
  + [LR3/w;
  + [LR3/x;
  + [LR3/y;
  + [LR5/d;
  + [LR5/g;
  + [LR7/b;
  + [LS6/B0;
  + [LS6/G0;
  + [LS6/J0;
  + [LT7/p;
  + [LY6/L;
  + [LY7/b;
  + [LY7/d;
  + [La6/l;
  + [Laa/b;
  + [Lb8/d;
  + [Lba/b;
  + [Lda/J1;
  + [Lda/M1;
  + [Lda/S1;
  + [Lda/Z1;
  + [Lda/d2;
  + [Lda/f2;
  + [Lf4/b;
  + [Lf8/g;
  + [Lk7/W0;
  + [Lk7/j0;
  + [Lk7/r0;
  + [Lk7/z0;
  + [Ln6/M;
  + [Ln6/c;
  + [Ln6/k;
  + [Ln8/b;
  + [Ln9/X0;
  + [Lq8/j;
  + [Lu9/C;
  + [Lw7/J;
  + [Lw7/Q;
  + Lio/sentry/H2;
  + Lub/A;
  + Lya/p0;
  + [Lio/sentry/A2;
  + [Lio/sentry/W1;
  + [Lio/sentry/a2;
  + [Lio/sentry/k2;
  + [Lio/sentry/n2;
  + [Lio/sentry/p2;
  + [Lio/sentry/u2;
  + [Lub/A;
  + [Lya/N;
  + [Lya/c0;
  + [Lya/e0;
  + [Lya/n0;
  + [Lz5/a;
  
  - LR7/j;
  - LY7/h;
  - LZ1/m;
  - Lba/d;
  - Lf8/i;
  - Li6/O;
  - Lio/sentry/android/core/internal/u
...✂

@cttsai-stripe cttsai-stripe changed the title ScanCardButtonUITestCttsai/set gpcr as default cardscan Set GPCR as default card scanning implementation Oct 20, 2025
@cttsai-stripe
Copy link
Contributor Author

Do we need a changelog in this PR?

@cttsai-stripe cttsai-stripe force-pushed the cttsai/set-gpcr-as-default-cardscan branch from 08de030 to 02bacdc Compare October 20, 2025 23:54
@cttsai-stripe cttsai-stripe marked this pull request as ready for review October 20, 2025 23:54
@cttsai-stripe cttsai-stripe requested review from a team as code owners October 20, 2025 23:54
@cttsai-stripe cttsai-stripe force-pushed the cttsai/set-gpcr-as-default-cardscan branch from 02bacdc to 99decfa Compare October 21, 2025 03:55
validateAnalyticsRequest(eventName = "mc_embedded_sheet_newpm_show")
validateAnalyticsRequest(eventName = "mc_carousel_payment_method_tapped")
validateAnalyticsRequest(eventName = "mc_form_shown")
// cardscan is not available in test mode
Copy link
Collaborator

Choose a reason for hiding this comment

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

In test mode as in stripe test mode? Why not?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good question. The error is 10: The Application invoking the current API hasn't registered for Google card recognition API.

GPCR works in paymentsheet-example but fails in paymentsheet tests because Google identifies apps using both package name and certificate fingerprint.

  • paymentsheet-example: com.stripe.android.paymentsheet.example + Stripe keystore (SHA-1: BA:B2:79:56...) ✅ Registered in Google Cloud Console
  • paymentsheet tests: com.stripe.android.paymentsheet.test + Android debug keystore (SHA-1: BD:14:55:B8...) ❌ Not registered

Even changing the test application ID to match wouldn't work because the tests use a different signing certificate (the default Android debug keystore vs. the custom Stripe keystore). Google treats them as separate apps since the fingerprints don't match.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we sign our test APK with the same cert we have checked into the repo?

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 that should work if we also change the application id. I saw the paymentsheet-example was signed with a custom key by you. Should we chat on slack how to sign the test apk correctly?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we shouldn't block on it. Let's do it as a follow up task.

cttsai-stripe and others added 13 commits October 21, 2025 14:28
- Remove legacy CardScan test cases
- Add GPCR-only test scenarios
- Simplify test helper methods to remove feature flag dependencies
- Part of migration to set GPCR as default card scanning implementation

Co-Authored-By: Claude <noreply@anthropic.com>
Committed-By-Agent: claude
…LaunchedCardScanFormDataHelper

- Make GPCR the default card scanning implementation
- Remove cardScanGooglePayMigration feature flag checks from main code
- Update ScanCardButtonUI to use only CardScanGoogleLauncher
- Remove legacy CardScanContract imports and launcher code
- Update tests to test GPCR-only behavior

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

Co-Authored-By: Claude <noreply@anthropic.com>
Committed-By-Agent: claude
- Remove FeatureFlags import
- Remove all setEnabled(true/false) calls
- Remove test for feature flag disabled scenario (no longer relevant)
- GPCR is now always enabled by default

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

Co-Authored-By: Claude <noreply@anthropic.com>
Committed-By-Agent: claude
…llyTest

- Remove FeatureFlags import
- Remove setEnabled calls
- GPCR is now always enabled

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

Co-Authored-By: Claude <noreply@anthropic.com>
Committed-By-Agent: claude
Batch remove FeatureFlags.cardScanGooglePayMigration from:
- DefaultVerticalModeFormInteractorTest.kt
- CardDefinitionTest.kt
- CustomerSheetViewModelTest.kt

Removed imports and all setEnabled() calls. GPCR is now always enabled.

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

Co-Authored-By: Claude <noreply@anthropic.com>
Committed-By-Agent: claude
Remove FeatureFlagSettingsDefinition for cardScanGooglePayMigration from
playground UI. GPCR is now always enabled, no toggle needed.

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

Co-Authored-By: Claude <noreply@anthropic.com>
Committed-By-Agent: claude
GPCR is now the default card scanning implementation. Remove the feature
flag from FeatureFlags.kt as it's no longer needed.

This completes Phase 1: Setting GPCR as default. The legacy cardscan
modules remain in the codebase but are unused.

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

Co-Authored-By: Claude <noreply@anthropic.com>
Committed-By-Agent: claude
- Add EmptyCardScanEventsReporter with no-op implementations that error only in DEBUG builds
- Refactor ScanCardButtonUI to accept CardScanGoogleLauncher? parameter instead of pulling from composition locals
- Update CardDetailsSectionElementUI to conditionally create launcher based on ActivityResultRegistry availability
- Simplify test setup by removing need for composition local providers in most tests
- Update ScanCardButtonUITest to use new API

This follows the pattern established by LocalAnalyticsEventReporter and eliminates the need for extensive test helper code while maintaining fail-fast behavior in DEBUG builds for production integration errors.

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

Co-Authored-By: Claude <noreply@anthropic.com>
Committed-By-Agent: claude
Replace Mockito mock with a simple NoOpCardScanEventsReporter object in
PaymentMethodScreenTest to follow the codebase preference for fakes over
mocks. The no-op implementation is sufficient since these tests don't
exercise card scanning functionality.

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

Co-Authored-By: Claude <noreply@anthropic.com>
Committed-By-Agent: claude
@cttsai-stripe cttsai-stripe force-pushed the cttsai/set-gpcr-as-default-cardscan branch from a243a52 to a9a8a95 Compare October 21, 2025 21:29
* Remove cardscan module references from build configuration

Remove stripecardscan module and related ML modules from settings.gradle
and build dependencies. Preserve camera-core module as it's required by
the identity module.

Changes:
- Remove 5 cardscan modules from settings.gradle
- Remove stripecardscan-example from API validation ignored projects
- Remove stripecardscan dependencies from payments-ui-core
- Remove stripecardscan dependency from paymentsheet-example
- Keep camera-core module for identity module compatibility

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

Co-Authored-By: Claude <noreply@anthropic.com>
Committed-By-Agent: claude

* Delete cardscan module directories

Remove physical directories for cardscan modules:
- stripecardscan/ - main cardscan module
- stripecardscan-example/ - example app
- stripecardscan-tflite/ - TensorFlow Lite runtime
- ml-core/cardscan/ - ML processing module
- ml-core/googleplay/ - Google Play ML services module

Deleted 191 files across 5 module directories.

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

Co-Authored-By: Claude <noreply@anthropic.com>
Committed-By-Agent: claude

* Remove old cardscan code references and files

- Delete old cardscan activity and contract files
- Remove old cardscan proxy and availability check
- Clean up CardDetailsSectionController and CardNumberController
- Remove old cardscan test files

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

Co-Authored-By: Claude <noreply@anthropic.com>
Committed-By-Agent: claude

* Remove remaining usages

* Remove run-cardscan-instrumentation-tests

* api dump

* Remove CardScanActivity from AndroidManifest.xml

* Update bitrise instrumentation tests

* Update MIGRATING.md

* Fix lokalise

* Update transitive dependencies

* Remove card_scan_available in PaymentSheetEventTest

* Fix tests

* Restore ml-core:googleplay

* Update MIGRATING.md

---------

Co-authored-by: Claude <noreply@anthropic.com>
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.

3 participants