What CVV validation actually checks
CVV validation is two checks, not one. The first runs in your form: the card verification value must be 3 digits for Visa, Mastercard, Discover, and most other brands, and 4 digits for American Express. The second runs at the issuer: your gateway sends the value, and the issuer answers match, no match, not processed, or not present. Your code only controls the first check. The issuer controls the second, and a match does not prove the card is legitimate.
Most failures come from a wrong digit count in the form or a gateway setting that skips the check. Fix the input, confirm the gateway rule, then test.
Prerequisites
- Access to the checkout form or hosted payment element settings
- Sandbox credentials from your processor
- The processor test card list, including one Amex number
- Your PCI scope summary and data retention policy
Validate a CVV in a checkout form
- Read the card number prefix to identify the brand before you set any length rule.
- Set the field length from the brand: 3 digits for Visa, Mastercard, and Discover, 4 digits for American Express.
- Accept digits only. Strip spaces and dashes, and reject letters.
- Label the field with the name printed on the card: CVV, CVC, CVV2, or card identification code.
- Skip any Luhn check on this field. Luhn applies to the primary account number, not the verification value.
- Send the value straight to the gateway token field. Do not route it through your own application logs.
- Read the gateway response for the CVV result and store only that result code.
- Map each result code to a distinct message and a decision to hold or release the order.
- Confirm the value is never written to a database, cache, or analytics event after authorization.
- Run the flow in sandbox with cards from both the 3-digit and 4-digit brands.
What the gateway result codes mean
- Match: the issuer confirmed the value. Continue the order.
- No match: the value is wrong. Ask the shopper to re-enter it once, then route the order to review.
- Not processed: the issuer skipped the check. Do not treat this as a pass or a fail.
- Not present: the value was missing from the request. Check the form submission path.
- Unavailable: the issuer could not answer. Retry later or apply your own risk rule.
Test the check before launch
Use processor sandbox cards that trigger each result. Confirm the form accepts 3 digits on a Visa test number and 4 digits on an Amex test number, then confirm the same card is rejected when you enter one digit too few. Submit a card that returns a no-match and verify the shopper sees a specific message rather than a generic decline. Check the request payload in your gateway dashboard to confirm the value arrives in the correct field for each brand. Repeat the run on mobile, since numeric keypads and autofill behave differently there.
Rules that keep the check compliant
PCI DSS bars storing the card verification value after authorization in any form, including encrypted storage. Treat the field as sensitive input: mask it in the UI, keep it out of logs, and clear it from memory after the gateway call. If you use a hosted payment element, the processor collects the value directly and your servers never see it, which shrinks your PCI scope.