fix: size and validate the CVC from the detected card brand - #1707
Conversation
Changed Files
|
|
[should-fix] The new |
|
[should-fix]
Also confirm that |
43c192b to
7176706
Compare
7176706 to
27d10fb
Compare
|
Thanks for both notes (#1707 (comment) and #1707 (comment)). Both point at On the second half, about the
That is existing behaviour rather than something this PR introduces, so I have deliberately kept it out of a CVC-length change. Happy to open a separate issue for it if that would be useful. |
27d10fb to
4fbd304
Compare
4fbd304 to
0839518
Compare
Screen.Recording.2026-07-27.at.4.02.38.pm.movI noticed that when I focus on the CVC input and then blur it, the colour does not change to red. |
|
Could you also please attach videos showing the behaviour before and after your changes? |
0839518 to
0aa3dae
Compare
There was a problem hiding this comment.
Re: this comment
Thanks for the catch. Reproduced and fixed in 0aa3dae.
The cause is the auto-advance rather than the blur. Completing the expiry focuses the CVC (CommonCardProps.res#L211), and focusing clears that field's validity (InputField.res#L72), so the verdict went without the user editing anything. It now auto-advances only when the CVC is empty.
Submit stayed blocked throughout (Payment.res#L56), so the defect was the missing red only.
0aa3dae to
d403faa
Compare
|
Any suggestions how to move this PR forward / make a decision on it? … cc: @aritro2002 @Shivam25092001 |
|
@AbhishekChorotiya @sakksham7 could you please help us with the review here. |
3b445f0 to
392b3e4
Compare
Type of Change
Description
Fixes #1706.
In a bare
cardelement every CVC rule runs against an empty brand, so the rules fall back to the default card pattern, which accepts 3 or 4 digits, and the input'smaxLengthwas a literal4. A 4-digit CVC on a Visa therefore passed the client and failed at the processor as an opaquecvv_invalid.This derives a CVC-scoped brand and routes the CVC rules through it: the input's
maxLength, the formatter, the range checks, and the CVC-to-ZIP auto-advance. A CVC already in the field when the brand arrives is re-judged against it, by the same rule a blur already uses, so narrowing invalidates it and widening makes it valid again. The value the user typed is never rewritten and never deleted.The hook's
cardBrandis left byte-identical to upstream. That is deliberate and is the whole reason the change is scoped this way: widening it would also putcard_networkon the wire in this mode, makecheckIsCardSupporteddecline brands outside the merchant's configuredcard_networks, narrowmaxCardLength, and wake the clear-expiry-and-CVC-on-brand-change effect. A PR about CVC length should change none of those, and this one changes none of them.Payment.resis untouched.Three things reviewers should see explicitly
cardCvcelement is NOT fixed. It is a separate iframe whose local card number is always empty, so it cannot know the brand, and there is no association between acardNumberand thecardCvcof the same form to route one to it. Its input keepsmaxLength=4. Tightening only the submit-time gate was tried and dropped: it rejects input that the CVC iframe still shows as valid, and the error surfaces in a frame with no CVC field, which is worse than the status quo. Closing this properly looks like a design decision rather than something to invent inside a bugfix.maxLength=4on purpose. In those flows the scheme atom IS written, so the CVC brand is the saved card's scheme and the formatter already caps the value. Measured: typing1234into a saved Visa's CVC re-collect field yields123, with the attribute still reading 4. The literal never binds, so converting it would be churn.maxLengthattribute does change (4 becomes 3 on a Visa), because that is where the brand was already available. The submitted value does not change, since the formatter already capped it. The only user-visible delta is that the browser now blocks a phantom 4th keystroke it previously accepted and discarded.One pre-existing gap this does not widen, but worth knowing
emitIsFormReadyForSubmissiononly fires when all three of(isCardValid, isExpiryValid, isCVCValid)areSome, so any transition toNoneemits nothing and a previously-emittedtrueis never retracted. Upstream already reaches that state on its own:changeCVCNumbersetsNoneon any invalid keystroke, so deleting a digit from an accepted CVC leaves the same staletrue. Using the blur rule for the re-judge means this change reachesSome(false)rather thanNonewhen a CVC goes stale, so the signal retracts correctly here. Making the emit total is a change to the merchant-facing event contract and belongs in its own PR.How did you test it?
Added two cypress assertions to
02-cards/01-card-validation.cy.ts, next to the existing per-brand formatting tests: the CVCmaxlengthis 3 for a Visa and 4 for an American Express. The first fails onmain. Note thecypressjob is skipped on fork PRs, so these have not run in CI here.Beyond that, manually against a hosted sandbox with a real connector, driving the elements in a browser. The bare
cardelement is not in any demo UI, so it was mounted directly to exercise it.CVC
maxlength:cardelement, no PANcardelement, Visacardelement, AmexcardCvcelementValidity and submit, in the bare
cardelement. These matter more than the attribute, because a wrong-length CVC used to be reported as valid:123, blurfalse123, blurtrue1234, blurtrue1234typed before the PAN, then a Visa PAN1234kept, marked invalidChecklist
npm run re:build