Skip to content

Init Card Scan Migration with Feature Flag #11393

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

Merged

Conversation

cttsai-stripe
Copy link
Contributor

@cttsai-stripe cttsai-stripe commented Aug 19, 2025

Summary

Use feature flag to switch between legacy impl and GPCR impl in playground

Motivation

Testing

  • Added tests
  • Modified tests
  • Manually verified

Screenshots

Before After
IMG_4632.MOV
IMG_4630.MOV

Changelog

Copy link
Contributor Author

@cttsai-stripe cttsai-stripe left a comment

Choose a reason for hiding this comment

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

Tested manually, but let me know if there is any missing automatic test.

val request = PaymentCardRecognitionIntentRequest.getDefaultInstance()

paymentsClient.getPaymentCardRecognitionIntent(request)
.addOnSuccessListener { intentResponse ->
Copy link
Contributor Author

Choose a reason for hiding this comment

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

These listeners, or say DefaultPaymentCardRecognitionClient overall, are not testable because the response can not be produced with test context.

Comment on lines +49 to +58
val cardScanGoogleLauncher = if (FeatureFlags.cardScanGooglePayMigration.isEnabled) {
rememberCardScanGoogleLauncher(context, onGoogleCardScanResult)
} else {
null
}
val isCardScanGoogleAvailable by if (FeatureFlags.cardScanGooglePayMigration.isEnabled) {
cardScanGoogleLauncher?.isAvailable?.collectAsState() ?: remember { mutableStateOf(false) }
} else {
remember { mutableStateOf(false) }
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This should prevent any of GPCR api being called accidentally.

Copy link
Contributor

github-actions bot commented Aug 19, 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.7 MiB │   4.7 MiB │ +332 B │  10.5 MiB │  10.5 MiB │ +868 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.1 KiB │  30.1 KiB │    0 B 
      res │ 923.8 KiB │ 923.8 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 │  +13 B │   1.6 MiB │   1.6 MiB │  +13 B 
    other │ 198.4 KiB │ 198.4 KiB │   +3 B │ 374.7 KiB │ 374.7 KiB │    0 B 
──────────┼───────────┼───────────┼────────┼───────────┼───────────┼────────
    total │  13.5 MiB │  13.5 MiB │ +348 B │    25 MiB │    25 MiB │ +881 B 

         │         raw          │             unique             
         ├───────┬───────┬──────┼───────┬───────┬────────────────
 DEX     │ old   │ new   │ diff │ old   │ new   │ diff           
─────────┼───────┼───────┼──────┼───────┼───────┼────────────────
   files │     2 │     2 │    0 │       │       │                
 strings │ 53205 │ 53209 │   +4 │ 49573 │ 49577 │ +4 (+31 -27)   
   types │ 18947 │ 18949 │   +2 │ 17235 │ 17237 │ +2 (+28 -26)   
 classes │ 14551 │ 14553 │   +2 │ 14551 │ 14553 │ +2 (+3 -1)     
 methods │ 74127 │ 74135 │   +8 │ 71300 │ 71308 │ +8 (+793 -785) 
  fields │ 48318 │ 48321 │   +3 │ 47318 │ 47321 │ +3 (+454 -451) 

 ARSC    │ old  │ new  │ diff 
─────────┼──────┼──────┼──────
 configs │  242 │  242 │  0   
 entries │ 6351 │ 6351 │  0
APK
     compressed     │    uncompressed    │                                
───────────┬────────┼───────────┬────────┤                                
 size      │ diff   │ size      │ diff   │ path                           
───────────┼────────┼───────────┼────────┼────────────────────────────────
   4.3 MiB │ +333 B │   9.4 MiB │ +868 B │ ∆ classes.dex                  
   7.8 KiB │  +10 B │   7.7 KiB │  +10 B │ ∆ assets/dexopt/baseline.prof  
  51.6 KiB │   +6 B │ 122.1 KiB │    0 B │ ∆ META-INF/MANIFEST.MF         
    55 KiB │   -4 B │ 122.1 KiB │    0 B │ ∆ META-INF/CERT.SF             
   1.1 KiB │   +3 B │     1 KiB │   +3 B │ ∆ assets/dexopt/baseline.profm 
 471.4 KiB │   -1 B │   1.1 MiB │    0 B │ ∆ classes2.dex                 
   1.2 KiB │   +1 B │   1.2 KiB │    0 B │ ∆ META-INF/CERT.RSA            
───────────┼────────┼───────────┼────────┼────────────────────────────────
   4.9 MiB │ +348 B │  10.7 MiB │ +881 B │ (total)
DEX
STRINGS:

   old   │ new   │ diff         
  ───────┼───────┼──────────────
   49573 │ 49577 │ +4 (+31 -27) 
  
  + LU9/c;
  + LX9/h2;
  + Ls7/P;
  + Use Google Payment Card Recognition API for Card Scan
  + [LX9/A0;
  + [LX9/D0;
  + [LX9/D1;
  + [LX9/G0;
  + [LX9/J0;
  + [LX9/J1;
  + [LX9/L0;
  + [LX9/M1;
  + [LX9/P0;
  + [LX9/T0;
  + [LX9/T1;
  + [LX9/a2;
  + [LX9/c1;
  + [LX9/e1;
  + [LX9/f2;
  + [LX9/h1;
  + [LX9/h2;
  + [LX9/i0;
  + [LX9/k1;
  + [LX9/l0;
  + [LX9/q1;
  + [LX9/v1;
  + [LX9/y1;
  + [Ls7/J;
  + [Ls7/O;
  + onGoogleCardScanResult
  + ~~R8{"backend":"dex","compilation-mode":"release","has-checksums":false,"min-api":21,"pg-map-id":"23f3c73","r8-mode":"full","version":"8.8.34"}
  
  - Lr5/m;
  - [LX9/B1;
  - [LX9/C0;
  - [LX9/F0;
  - [LX9/I0;
  - [LX9/I1;
  - [LX9/K0;
  - [LX9/L1;
  - [LX9/O0;
  - [LX9/S0;
  - [LX9/S1;
  - [LX9/Z1;
  - [LX9/b1;
  - [LX9/d1;
  - [LX9/e2;
  - [LX9/g1;
  - [LX9/g2;
  - [LX9/h0;
  - [LX9/j1;
  - [LX9/k0;
  - [LX9/p1;
  - [LX9/t1;
  - [LX9/x1;
  - [LX9/z0;
  - [Ls7/I;
  - [Ls7/N;
  - ~~R8{"backend":"dex","compilation-mode":"release","has-checksums":false,"min-api":21,"pg-map-id":"b7c17ec","r8-mode":"full","version":"8.8.34"}
  

TYPES:

   old   │ new   │ diff         
  ───────┼───────┼──────────────
   17235 │ 17237 │ +2 (+28 -26) 
  
  + LU9/c;
  + LX9/h2;
  + Ls7/P;
  + [LX9/A0;
  + [LX9/D0;
  + [LX9/D1;
  + [LX9/G0;
  + [LX9/J0;
  + [LX9/J1;
  + [LX9/L0;
  + [LX9/M1;
  + [LX9/P0;
  + [LX9/T0;
  + [LX9/T1;
  + [LX9/a2;
  + [LX9/c1;
  + [LX9/e1;
  + [LX9/f2;
  + [LX9/h1;
  + [LX9/h2;
  + [LX9/i0;
  + [LX9/k1;
  + [LX9/l0;
  + [LX9/q1;
  + [LX9/v1;
  + [LX9/y1;
  + [Ls7/J;
  + [Ls7/O;
  
  - Lr5/m;
  - [LX9/B1;
  - [LX9/C0;
  - [LX9/F0;
  - [LX9/I0;
  - [LX9/I1;
  - [LX9/K0;
  - [LX9/L1;
  - [LX9/O0;
  - [LX9/S0;
  - [LX9/S1;
  - [LX9/Z1;
  - [LX9/b1;
  - [LX9/d1;
  - [LX9/e2;
  - [LX9/g1;
  - [LX9/g2;
  - [LX9/h0;
  - [LX9/j1;
  - [LX9/k0;
  - [LX9/p1;
  - [LX9/t1;
  - [LX9/x1;
  - [LX9/z0;
  - [Ls7/I;
  - [Ls7/N;
  

METHODS:

   old   │ new   │ diff           
  ───────┼───────┼────────────────
   71300 │ 71308 │ +8 (+793 -785) 
  
  + A8.Z <init>(Context, i, String)
  + B4.r l(int, Parcel, Parcel) → boolean
  + B4.r m(Parcel, int) → boolean
  + B8.C b(String, boolean, Function1, r, int)
  + C9.a P(C1, List, boolean, t0, t1) → T0
  + D7.h <init>(m, T1, int, int, Integer, boolean, b, Integer, int)
  + D8.k <init>(o0, boolean, q, l0)
  + E3.b n(IBinder) → a
  + E3.b o(a) → Object
  + E7.x c(o, T1, a, w) → List
  + E7.x e(o, T1) → h
  + E7.x f(o, T1, e) → a
  + E7.x g(o, T1, a) → ArrayList
  + F3.f w(o, T1, a) → ArrayList
  + F3.f x(x, o, T1, a, w) → List
  + F3.f y(x, o, T1, e) → a
  + F3.j k(b, int) → a
  + F3.j l(b, int, b) → a
  + F3.j m(b, int) → a
  + F3.j n(b, boolean, long) → a
  + F3.k l(b, int, b) → a
  + F7.T c(o, T1, a, w) → List
  + F7.T e(o, T1) → h
  + F7.T f(o, T1, e) → a
  + F7.T g(o, T1, a) → ArrayList
  + F7.b c(o, T1, a, w) → List
  + F7.b e(o, T1) → h
  + F7.b f(o, T1, e) → a
  + F7.b g(o, T1, a) → ArrayList
  + G.d j(n1, p, r, int)
  + H.i m(boolean, G1, p, r, int)
  + I6.b <init>(boolean, Object, Object, Object, int, int)
  + J3.d k(int, Parcel, Parcel) → boolean
  + J3.e m(Parcel, int) → boolean
  + J3.h m(Parcel, int) → boolean
  + J3.h n()
  + J3.u m(Parcel, int) → boolean
  + L3.a d(Parcel, IInterface)
  + L3.h j(Status, h)
  + L3.i j(Status, h)
  + P3.c k(int, Parcel, Parcel) → boolean
  + Q3.l c(Executor, h) → s
  + Q3.l e(c) → s
  + Q3.l f() → Exception
  + Q3.l g() → Object
  + Q3.l i() → boolean
  + Q3.l j(k) → s
  + Q3.q <init>(Executor, g)
  + Q3.s c(Executor, h) → s
  + Q3.s e(c) → s
  + Q3.s f() → Exception
  + Q3.s g() → Object
  + Q3.s i() → boolean
  + Q3.s j(k) → s
  + Q3.s k(Exception)
  + Q3.s l(Object)
  + Q3.s m()
  + Q3.s n(Object) → boolean
  + Q3.s p()
  + R3.q c(int)
  + R9.m k() → i
  + S3.b k(b, ButtonOptions) → a
  + X9.A0 valueOf(String) → A0
  + X9.A0 values() → A0[]
  + X9.B0 <clinit>()
  + X9.B0 a() → a[]
  + X9.B0 b() → g
  + X9.B0 c(d, Object)
  + X9.B0 d(c) → Object
  + X9.B1 serializer() → a
  + X9.C0 serializer() → a
  + X9.C1 valueOf(String) → C1
  + X9.C1 values() → C1[]
  + X9.D0 <init>(String, int, String)
  + X9.D0 <init>(String, String)
  + X9.D0 describeContents() → int
  + X9.D0 equals(Object) → boolean
  + X9.D0 hashCode() → int
  + X9.D0 toString() → String
  + X9.D0 writeToParcel(Parcel, int)
  + X9.D1 <clinit>()
  + X9.D1 <init>(int, p0, C1)
  + X9.D1 <init>(p0, C1)
  + X9.D1 describeContents() → int

...✂

@cttsai-stripe cttsai-stripe marked this pull request as ready for review August 19, 2025 07:04
@cttsai-stripe cttsai-stripe requested review from a team as code owners August 19, 2025 07:04
Copy link
Collaborator

@amk-stripe amk-stripe left a comment

Choose a reason for hiding this comment

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

Can you add a screen recording to the PR description to show what the new card scan impl looks like?

Copy link
Collaborator

Choose a reason for hiding this comment

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

It'd be good to have some tests for this UI -- e.g. we should test that the button is shown/hidden when expected and that onClick does what we expect

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is tricky to fake the controller and card scan launchers to cover the test cases we want. Do you have any suggestion?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I have an idea for how we could do this -- let's discuss in our 1:1

Copy link
Collaborator

Choose a reason for hiding this comment

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

Discussed offline and these tests will be added as a follow up to this PR

@cttsai-stripe cttsai-stripe force-pushed the cttsai/card-scan-migration-init-with-feature-flag branch 2 times, most recently from 451fc2a to 057c426 Compare August 19, 2025 18:58
@cttsai-stripe cttsai-stripe force-pushed the cttsai/card-scan-migration-init-with-feature-flag branch from 057c426 to 06cf75e Compare August 19, 2025 20:32
Copy link
Collaborator

Choose a reason for hiding this comment

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

Discussed offline and these tests will be added as a follow up to this PR

@cttsai-stripe cttsai-stripe merged commit 4e5622c into master Aug 19, 2025
16 checks passed
@cttsai-stripe cttsai-stripe deleted the cttsai/card-scan-migration-init-with-feature-flag branch August 19, 2025 23:13
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.

2 participants