From 865f1ae0a145d025d1e2db1e8e1ea4c1b33e7e79 Mon Sep 17 00:00:00 2001 From: Josh Pensky Date: Mon, 2 Feb 2026 16:38:15 +0000 Subject: [PATCH 1/5] skip CVC validation when card number validation fails --- packages/ui-components/src/Card/index.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/ui-components/src/Card/index.tsx b/packages/ui-components/src/Card/index.tsx index afc1ada8f..21c469bfd 100644 --- a/packages/ui-components/src/Card/index.tsx +++ b/packages/ui-components/src/Card/index.tsx @@ -105,8 +105,9 @@ export function Card({ config }: { config: CardConfig }) { if (!fields.includes("cvc")) return undefined; const cardValidation = validateNumber(values.number); - const cvcValidation = validateCVC(values.cvc, values.number); + if (!cardValidation.isValid) return undefined; + const cvcValidation = validateCVC(values.cvc, values.number); if (!cvcValidation.isValid) { return "invalid"; } From 6327f9f08c3a05d759297520cd6b27713b51022b Mon Sep 17 00:00:00 2001 From: Josh Pensky Date: Mon, 2 Feb 2026 17:17:37 +0000 Subject: [PATCH 2/5] fix specs with validateCVC reason --- packages/card-validator/index.ts | 16 +++++++++++++--- packages/card-validator/types.ts | 14 ++++++++++---- packages/ui-components/src/Card/index.tsx | 10 ++++++++-- 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/packages/card-validator/index.ts b/packages/card-validator/index.ts index db6f9e767..9c238e9f9 100644 --- a/packages/card-validator/index.ts +++ b/packages/card-validator/index.ts @@ -114,12 +114,13 @@ export function validateCVC( return { cvc: null, isValid: false, + reason: "invalid_cvc", }; } if (!cardNumber) { return { - cvc: cvc, + cvc, isValid: true, }; } @@ -129,6 +130,7 @@ export function validateCVC( return { cvc: null, isValid: false, + reason: "invalid_number", }; } @@ -146,9 +148,17 @@ export function validateCVC( return brand.securityCodeValidationRules.lengths.includes(cvc.length); }); + if (!isCVCValid) { + return { + cvc: null, + isValid: false, + reason: "invalid_brand_cvc", + }; + } + return { - cvc: isCVCValid ? cvc : null, - isValid: isCVCValid, + cvc, + isValid: true, }; } diff --git a/packages/card-validator/types.ts b/packages/card-validator/types.ts index 946189672..e239989fe 100644 --- a/packages/card-validator/types.ts +++ b/packages/card-validator/types.ts @@ -35,7 +35,13 @@ export type CardExpiryValidationResult = { isValid: boolean; }; -export type CardCVCValidationResult = { - cvc: string | null; - isValid: boolean; -}; +export type CardCVCValidationResult = + | { + cvc: string; + isValid: true; + } + | { + cvc: null; + isValid: false; + reason: "invalid_cvc" | "invalid_number" | "invalid_brand_cvc"; + }; diff --git a/packages/ui-components/src/Card/index.tsx b/packages/ui-components/src/Card/index.tsx index 21c469bfd..b25e5684a 100644 --- a/packages/ui-components/src/Card/index.tsx +++ b/packages/ui-components/src/Card/index.tsx @@ -105,10 +105,16 @@ export function Card({ config }: { config: CardConfig }) { if (!fields.includes("cvc")) return undefined; const cardValidation = validateNumber(values.number); - if (!cardValidation.isValid) return undefined; - const cvcValidation = validateCVC(values.cvc, values.number); if (!cvcValidation.isValid) { + // Skip CVC validation if failed because of invalid card number + if ( + cvcValidation.reason === "invalid_number" && + !cardValidation.isValid + ) { + return undefined; + } + return "invalid"; } From cfe14143ab01e788f55a58095e4f9835cdda0e66 Mon Sep 17 00:00:00 2001 From: Josh Pensky Date: Mon, 2 Feb 2026 17:18:58 +0000 Subject: [PATCH 3/5] fix specs with validateCVC reason --- packages/card-validator/test/validate-cvc.test.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/card-validator/test/validate-cvc.test.ts b/packages/card-validator/test/validate-cvc.test.ts index 584cb702b..029bef3c3 100644 --- a/packages/card-validator/test/validate-cvc.test.ts +++ b/packages/card-validator/test/validate-cvc.test.ts @@ -14,31 +14,31 @@ const testCases: TestCase[] = [ scope: "Invalid card number", cardNumber: "123", cvc: "123", - expectedResult: { cvc: null, isValid: false }, + expectedResult: { cvc: null, isValid: false, reason: "invalid_number" }, }, { scope: "Non digit CVC", cardNumber: "4242424242424242", cvc: "abc", - expectedResult: { cvc: null, isValid: false }, + expectedResult: { cvc: null, isValid: false, reason: "invalid_cvc" }, }, { scope: "CVC with spaces", cardNumber: "4242424242424242", cvc: "123 ", - expectedResult: { cvc: null, isValid: false }, + expectedResult: { cvc: null, isValid: false, reason: "invalid_cvc" }, }, { scope: "CVC with wrong length mastercard", cardNumber: "5555555555554444", cvc: "1234", - expectedResult: { cvc: null, isValid: false }, + expectedResult: { cvc: null, isValid: false, reason: "invalid_brand_cvc" }, }, { scope: "CVC with wrong length amex", cardNumber: "378282246310005", cvc: "12", - expectedResult: { cvc: null, isValid: false }, + expectedResult: { cvc: null, isValid: false, reason: "invalid_brand_cvc" }, }, { scope: "Valid CVC Mastercard", @@ -86,13 +86,13 @@ const testCases: TestCase[] = [ scope: "2 digit CVC", cardNumber: "", cvc: "12", - expectedResult: { cvc: null, isValid: false }, + expectedResult: { cvc: null, isValid: false, reason: "invalid_cvc" }, }, { scope: "5 digit CVC", cardNumber: "", cvc: "12345", - expectedResult: { cvc: null, isValid: false }, + expectedResult: { cvc: null, isValid: false, reason: "invalid_cvc" }, }, ]; From 2a7e7d3faea32f8b5cc94a286a0722c33aef9381 Mon Sep 17 00:00:00 2001 From: Josh Pensky Date: Tue, 3 Feb 2026 08:50:13 +0000 Subject: [PATCH 4/5] fix unit test --- packages/card-validator/test/validate-cvc.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/card-validator/test/validate-cvc.test.ts b/packages/card-validator/test/validate-cvc.test.ts index 029bef3c3..6db2b4084 100644 --- a/packages/card-validator/test/validate-cvc.test.ts +++ b/packages/card-validator/test/validate-cvc.test.ts @@ -38,7 +38,7 @@ const testCases: TestCase[] = [ scope: "CVC with wrong length amex", cardNumber: "378282246310005", cvc: "12", - expectedResult: { cvc: null, isValid: false, reason: "invalid_brand_cvc" }, + expectedResult: { cvc: null, isValid: false, reason: "invalid_cvc" }, }, { scope: "Valid CVC Mastercard", From a763cbe199c6fc2751271dbde71ba13b67feddf8 Mon Sep 17 00:00:00 2001 From: Josh Pensky Date: Tue, 3 Feb 2026 09:31:42 +0000 Subject: [PATCH 5/5] add changeset --- .changeset/spicy-mayflies-give.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/spicy-mayflies-give.md diff --git a/.changeset/spicy-mayflies-give.md b/.changeset/spicy-mayflies-give.md new file mode 100644 index 000000000..354f309c4 --- /dev/null +++ b/.changeset/spicy-mayflies-give.md @@ -0,0 +1,7 @@ +--- +"@evervault/card-validator": minor +"@evervault/ui-components": patch +--- + +- Adds `reason` to CVC validation failures +- Fixes bug where failed card validation forces a failed CVC validation