Skip to content
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

Link Cvc & Expiry recollection #9880

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft

Conversation

toluo-stripe
Copy link
Contributor

@toluo-stripe toluo-stripe commented Jan 9, 2025

Summary

Perform cvc recollection and collect updated expiry date for expired cards

Motivation

JIRA

Testing

  • Added tests
  • Modified tests
  • Manually verified

Screenshots

Screen.Recording.2025-01-09.at.1.50.06.AM.mov

Changelog

Comment on lines 47 to 51
get() = true
// get() = !DateUtils.isExpiryDataValid(
// expiryMonth = expiryMonth,
// expiryYear = expiryYear
// )
Copy link

Choose a reason for hiding this comment

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

The isExpired property appears to be hardcoded to true for testing purposes, with the actual expiry validation logic commented out. This will cause all cards to be treated as expired in production. The original validation using DateUtils.isExpiryDataValid() should be uncommented and restored to ensure correct expiry checking behavior.

Spotted by Graphite Reviewer

Is this helpful? React 👍 or 👎 to let us know.

Copy link
Contributor

github-actions bot commented Jan 9, 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 │   3.9 MiB │   3.9 MiB │ +2.7 KiB │  8.6 MiB │  8.6 MiB │ +7.8 KiB 
     arsc │   2.3 MiB │   2.3 MiB │      0 B │  2.3 MiB │  2.3 MiB │      0 B 
 manifest │     5 KiB │     5 KiB │      0 B │ 24.9 KiB │ 24.9 KiB │      0 B 
      res │ 908.4 KiB │ 908.4 KiB │      0 B │  1.4 MiB │  1.4 MiB │      0 B 
   native │   2.6 MiB │   2.6 MiB │      0 B │    6 MiB │    6 MiB │      0 B 
    asset │   1.6 MiB │   1.6 MiB │    +33 B │  1.6 MiB │  1.6 MiB │    +33 B 
    other │   1.4 MiB │   1.4 MiB │    +14 B │  1.6 MiB │  1.6 MiB │      0 B 
──────────┼───────────┼───────────┼──────────┼──────────┼──────────┼──────────
    total │  12.6 MiB │  12.6 MiB │ +2.7 KiB │ 21.5 MiB │ 21.5 MiB │ +7.9 KiB 

 DEX     │ old   │ new   │ diff              
─────────┼───────┼───────┼───────────────────
   files │     1 │     1 │   0               
 strings │ 40548 │ 40571 │ +23 (+83 -60)     
   types │ 13988 │ 13998 │ +10 (+67 -57)     
 classes │ 11663 │ 11672 │  +9 (+17 -8)      
 methods │ 59534 │ 59558 │ +24 (+2501 -2477) 
  fields │ 39768 │ 39802 │ +34 (+1974 -1940) 

 ARSC    │ old  │ new  │ diff 
─────────┼──────┼──────┼──────
 configs │  243 │  243 │  0   
 entries │ 6208 │ 6208 │  0
APK
     compressed      │     uncompressed     │                                           
──────────┬──────────┼───────────┬──────────┤                                           
 size     │ diff     │ size      │ diff     │ path                                      
──────────┼──────────┼───────────┼──────────┼───────────────────────────────────────────
  3.9 MiB │ +2.7 KiB │   8.6 MiB │ +7.8 KiB │ ∆ classes.dex                             
  7.8 KiB │    +32 B │   7.7 KiB │    +32 B │ ∆ assets/dexopt/baseline.prof             
 50.2 KiB │    +11 B │ 118.4 KiB │      0 B │ ∆ META-INF/MANIFEST.MF                    
 53.4 KiB │     +3 B │ 118.5 KiB │      0 B │ ∆ META-INF/CERT.SF                        
    272 B │     +1 B │     120 B │      0 B │ ∆ META-INF/version-control-info.textproto 
    997 B │     +1 B │     865 B │     +1 B │ ∆ assets/dexopt/baseline.profm            
  1.2 KiB │     -1 B │   1.2 KiB │      0 B │ ∆ META-INF/CERT.RSA                       
──────────┼──────────┼───────────┼──────────┼───────────────────────────────────────────
    4 MiB │ +2.7 KiB │   8.8 MiB │ +7.9 KiB │ (total)
DEX
STRINGS:

   old   │ new   │ diff          
  ───────┼───────┼───────────────
   40548 │ 40571 │ +23 (+83 -60) 
  
  + , alertMessage=
  + , cvcInput=
  + , expiryDateInput=
  + Collection contains more than one matching element.
  + LC6/A;
  + LC6/B;
  + LC6/C;
  + LC6/u;
  + LC6/v;
  + LC6/w;
  + LC6/x;
  + LC6/y;
  + LC6/z;
  + LL6/s3;
  + LLLLZLLLI
  + LO6/U;
  + LO6/V;
  + LS5/r;
  + LY7/k;
  + Lo6/p;
  + Lt6/o;
  + Lu6/r;
  + VLLZLZLLLL
  + VLZLLLLLLII
  + [LG8/r1;
  + [LG8/u1;
  + [LG8/z1;
  + [LL6/A1;
  + [LL6/C;
  + [LL6/F0;
  + [LL6/I2;
  + [LL6/K;
  + [LL6/N;
  + [LL6/S0;
  + [LL6/S;
  + [LL6/X0;
  + [LL6/X2;
  + [LL6/Z1;
  + [LL6/a0;
  + [LL6/c2;
  + [LL6/c3;
  + [LL6/d1;
  + [LL6/f0;
  + [LL6/f2;
  + [LL6/h1;
  + [LL6/i3;
  + [LL6/l0;
  + [LL6/o0;
  + [LL6/s3;
  + [LL6/u0;
  + [LL6/u1;
  + [LL6/w2;
  + [LL6/y0;
  + [LS5/f;
  + [LV7/B1;
  + [LV7/a0;
  + [LV7/v1;
  + [LW5/l;
  + [LX5/T0;
  + [LZ6/c;
  + [La9/D;
  + [La9/M;
  + [La9/c0;
  + [La9/e0;
  + [La9/j;
  + [La9/o0;
  + [La9/w;
  + [Lc7/n;
  + [Lg5/c;
  + [Li8/u;
  + [Ll3/f;
  + [Ln6/h;
  + [Ls5/f;
  + [Lu6/k;
  + consumers/payment_details/
  + cvcController
  + cvcInput
  + expiryDateController
  + expiryDateInput
  + sectionFieldElement
  + wallet_screen_recollection_form_error
  + wallet_screen_recollection_form_fields
  + ~~R8{"backend":"dex","compilation-mode":"release","has-checksums":false,"min-api":21,"pg-map-id":"0935e15","r8-mode":"full","version":"8.7.14"}
  
  - LC8/f;
  - LE5/x;
  - LP4/a;
  - LS9/l;
  - LV7/O1;
  - La/a;
  - La9/r0;
  - Lw5/e;
  - VLLLZLI
  - VLZLLLLII
  - [LG8/p1;
  - [LG8/t1;
  - [LG8/x1;
  - [LL6/B0;
  - [LL6/B1;
  - [LL6/G0;
  - [LL6/G;
  - [LL6/J2;
  - [LL6/M;
  - [LL6/P;
  - [LL6/T0;
  - [LL6/W;
  - [LL6/Y0;
  - [LL6/Y2;
  - [LL6/a2;
  - [LL6/b0;
  - [LL6/d2;
  - [LL6/d3;
  - [LL6/e1;
  - [LL6/g0;
  - [LL6/g2;
  - [LL6/i1;
  - [LL6/m0;
  - [LL6/m3;
  - [LL6/p0;
  - [LL6/v0;
  - [LL6/v1;
  - [LL6/v2;
  - [LS5/e;
  - [LV7/C1;
  - [LV7/b0;
  - [LV7/y1;
  - [LW5/k;
  - [LX5/R0;
  - [LZ6/b;
  - [La9/E;
  - [La9/N;
  - [La9/d0;
  - [La9/f0;
  - [La9/l;
  - [La9/p0;
  - [La9/x;
  - [Lc7/l;
  - [Lg5/b;
  - [Li8/t;
  - [Ll3/e;
  - [Ln6/f;
  - [Ls5/d;
  - [Lu6/j;
  - ~~R8{"backend":"dex","compilation-mode":"release","has-checksums":false,"min-api":21,"pg-map-id":"eb135a1","r8-mode":"full","version":"8.7.14"}
  

TYPES:

   old   │ new   │ diff          
  ───────┼───────┼───────────────
   13988 │ 13998 │ +10 (+67 -57) 
  
  + LC6/A;
  + LC6/B;
  + LC6/C;
  + LC6/u;
  + LC6/v;
  + LC6/w;
  + LC6/x;
  + LC6/y;
  + LC6/z;
  + LL6/s3;
  + LO6/U;
  + LO6/V;
  + LS5/r;
  + LY7/k;
  + Lo6/p;
  + Lt6/o;
  + Lu6/r;
  + [LG8/r1;
  + [LG8/u1;
  + [LG8/z1;
  + [LL6/A1;
  + [LL6/C;
  + [LL6/F0;
  + [LL6/I2;
  + [LL6/K;
  + [LL6/N;
  + [LL6/S0;
  + [LL6/S;
  + [LL6/X0;
  + [LL6/X2;
  + [LL6/Z1;
  + [LL6/a0;
  + [LL6/c2;
  + [LL6/c3;
  + [LL6/d1;
  + [LL6/f0;
  + [LL6/f2;
  + [LL6/h1;
  + [LL6/i3;
  + [LL6/l0;
  + [LL6/o0;
  + [LL6/s3;
  + [LL6/u0;
  + [LL6/u1;
  + [LL6/w2;
  + [LL6/y0;
  + [LS5/f;
  + [LV7/B1;
  + [LV7/a0;
  + [LV7/v1;
  + [LW5/l;
  + [LX5/T0;
  + [LZ6/c;
  + [La9/D;
  + [La9/M;
  + [La9/c0;
  + [La9/e0;
  + [La9/j;
  + [La9/o0;
  + [La9/w;
  + [Lc7/n;
  + [Lg5/c;
  + [Li8/u;
  + [Ll3/f;
  + [Ln6/h;
  + [Ls5/f;
  + [Lu6/k;
  
  - LC8/f;
  - LE5/x;
  - LP4/a;
  - LS9/l;
  - LV7/O1;
  - La/a;
  - La9/r0;
  - Lw5/e;
  - [LG8/p1;
  - [LG8/t1;
  - [LG8/x1;
  - [LL6/B0;
  - [LL6/B1;
  - [LL6/G0;
  - [LL6/G;
  - [LL6/J2;
  - [LL6/M;
  - [LL6/P;
  - [LL6/T0;
  - [LL6/W;
  - [LL6/Y0;
  - [LL6/Y2;
  - [LL6/a2;
  - [LL6/b0;
  - [LL6/d2;
  - [LL6/d3;
  - [LL6/e1;
  - [LL6/g0;
  - [LL6/g2;
  - [LL6/i1;
  - [LL6/m0;
  - [LL6/m3;
  - [LL6/p0;
  - [LL6/v0;
  - [LL6/v1;
  - [LL6/v2;
  - [LS5/e;
  - [LV7/C1;
  - [LV7/b0;
  - [LV7/y1;
  - [LW5/k;
  - [LX5/R0;
  - [LZ6/b;
  - [La9/E;
  - [La9/N;
  - [La9/d0;
  - [La9/f0;
  - [La9/l;
  - [La9/p0;
  - [La9/x;
  - [Lc7/l;
  - [Lg5/b;
  - [Li8/t;
  - [Ll3/e;
  - [Ln6/f;
  - [Ls5/d;
  - [Lu6/j;
  

METHODS:

   old   │ new   │ diff              
  ───────┼───────┼───────────────────
   59534 │ 59558 │ +24 (+2501 -2477) 
  
  + A6.B <init>(s, n, c, d)
  + A6.l <init>(String, boolean, boolean, m, d)
  + A6.l a(l, boolean, m, d, int) → l
  + B6.r <init>(b, n, c, d, a, c)
  + B6.r i(j) → d
  + B6.s <init>(boolean, boolean, d, boolean, boolean, String, String)
  + B6.s a(s, boolean, d, boolean, boolean, int) → s
  + B9.i q0(Iterator) → g
  + B9.i r0(d) → Object
  + B9.i s0(Object, c) → g
  + B9.i t
...✂

@@ -23,7 +30,11 @@ internal data class WalletUiState(
val isExpired = card?.isExpired ?: false
val requiresCvcRecollection = card?.cvcCheck?.requiresRecollection ?: false

val disableButton = isExpired || requiresCvcRecollection
val isMissingExpiryDateInput = (expiryDateInput.isComplete && cvcInput.isComplete).not()
Copy link

Choose a reason for hiding this comment

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

The isMissingExpiryDateInput check should only depend on expiryDateInput.isComplete. Including cvcInput.isComplete in this check is incorrect since it's meant to validate expiry date completeness only. The check should be simplified to:

val isMissingExpiryDateInput = expiryDateInput.isComplete.not()

Spotted by Graphite Reviewer

Is this helpful? React 👍 or 👎 to let us know.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

expired cards requires both updated expiryDate and cvc

Comment on lines 143 to 169
private suspend fun performPaymentConfirmation(
selectedPaymentDetails: ConsumerPaymentDetails.PaymentDetails,
) {
val card = _uiState.value.selectedCard
val isExpired = card != null && card.isExpired

if (isExpired) {
performPaymentDetailsUpdate(selectedPaymentDetails).fold(
onSuccess = { result ->
val updatedPaymentDetails = result.paymentDetails.single {
it.id == selectedPaymentDetails.id
}
performPaymentConfirmation(updatedPaymentDetails)
},
onFailure = { error ->
_uiState.update {
it.copy(
alertMessage = error.stripeErrorMessage(),
isProcessing = false
)
}
}
)
}

// Confirm payment with LinkConfirmationHandler
}
Copy link

Choose a reason for hiding this comment

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

The performPaymentConfirmation method needs a return statement after the if (isExpired) block to prevent executing the payment confirmation logic twice. Currently, if a card is expired, the method will both update the card details and attempt to confirm the payment in the same call, which could lead to duplicate transactions.

Spotted by Graphite Reviewer

Is this helpful? React 👍 or 👎 to let us know.

Comment on lines +224 to +231
private fun WalletUiState.toPaymentMethodCreateParams(): PaymentMethodCreateParams {
val expiryDateValues = createExpiryDateFormFieldValues(expiryDateInput)
return FieldValuesToParamsMapConverter.transformToPaymentMethodCreateParams(
fieldValuePairs = expiryDateValues,
code = PaymentMethod.Type.Card.code,
requiresMandate = false
)
}
Copy link

Choose a reason for hiding this comment

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

The toPaymentMethodCreateParams() function appears incomplete - it transforms the expiry date but omits the CVC value from cvcInput. Both values should be included in the fieldValuePairs map passed to transformToPaymentMethodCreateParams() to properly validate the card details.

Spotted by Graphite Reviewer

Is this helpful? React 👍 or 👎 to let us know.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

cvc is not part of PaymentMethodCreateParams. It will be passed extraParams to the LinkConfirmationHandler` here

@toluo-stripe toluo-stripe changed the title Tolu/link/cvc expiry Link Cvc & Expiry recollection Jan 9, 2025
}
)
} else {
// Confirm payment with LinkConfirmationHandler
Copy link
Contributor Author

Choose a reason for hiding this comment

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


onWalletAddPaymentMethodRow().assertIsDisplayed().assertHasClickAction()
onExpandedWalletHeader().assertIsDisplayed()
onPaymentMethodList().assertCountEquals(2)
onWalletPayButton().assertIsDisplayed().assertIsNotEnabled().assertHasClickAction()
Copy link
Contributor Author

@toluo-stripe toluo-stripe Jan 9, 2025

Choose a reason for hiding this comment

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

Pay button is hidden on Android for small devices, when the wallet is expanded. I created a ticket to resolve this

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant