A CVV cannot be worked out from a card number. Not with a formula, not with a lookup, not with a tool. The value is produced by the issuing bank using two cryptographic keys that never leave the bank’s hardware security module, and without those keys there is no calculation to run.

That is not an obstacle someone has failed to solve yet. It is the entire design. The card number is printed on the card and passes through dozens of systems; the CVV exists precisely to be the one value that cannot be worked out from the rest.

The CVV generator page walks through the issuer’s calculation step by step and the four common misconceptions about it. This guide takes the other half: why the impossibility is structural rather than incidental, the four different values that all get called “the CVV”, what your gateway actually tells you about one, and why plenty of legitimate payments never ask for it at all.

What the digits are for

A security code answers one narrow question: is the person entering these details holding the physical card?

In a card-present transaction the chip answers that question cryptographically. Online or over the phone there is no chip, so the merchant falls back on something printed on the card and nowhere else — not in the magnetic stripe, not in the chip, not in a receipt, not in the data a terminal transmits. Someone who copied a stripe or intercepted a transaction has the number and the expiry. They do not have the code, unless they photographed the card or the cardholder typed it into something they should not have.

That is also why the code must never be retained after authorisation. A stored CVV converts the one piece of evidence that a card was physically present into just another database column, and its entire value depends on that not happening.

CVV1, CVV2, iCVV and dCVV

Four different values, one family, and confusing them causes real integration bugs:

ValueWhere it livesUsed for
CVV1 / CVC1Encoded in the magnetic stripeCard-present swipe transactions
CVV2 / CVC2 / CIDPrinted on the cardCard-not-present — online and phone
iCVVStored in the EMV chipChip transactions; deliberately differs from CVV1
dCVVGenerated per transactionContactless and digital wallets; changes every time

The iCVV design decision is the interesting one. If the chip carried the same value as the magnetic stripe, then data read from a chip transaction could be written onto a blank stripe and used as a counterfeit card — chip data would become a recipe for cloning the older technology. Making them different by design severs that path, and it is why a terminal that validates chip data against stripe expectations rejects perfectly good cards.

dCVV extends the same idea one step further. Rather than a static value that is identical on every transaction, a device generates a fresh code each time, so intercepting one is worth nothing. This is what Apple Pay and Google Pay use alongside device tokens, and it is the direction the whole mechanism is moving — see the tokenisation guide for the surrounding architecture. EMVCo publishes the specifications these values are defined in.

Why it is unforgeable, structurally

The single most common misconception is that the CVV algorithm is a secret. It is not. It is documented in hardware security module manuals and implemented in commercial card-management software.

That is not a weakness — it is the design working correctly, and it has a name. Kerckhoffs’s principle holds that a cryptographic system should remain secure even if everything about it except the key is public knowledge. A scheme whose safety depends on nobody learning the steps is not secure, merely unexamined. The CVV mechanism follows the principle exactly: publish the algorithm, protect the keys.

The useful contrast is with the Luhn checksum, which sits on the other end of the same card:

Luhn check digitSecurity code
InputThe card number aloneNumber, expiry, service code, and a secret key
Who can compute itAnyoneOnly the key holder
What it detectsTyping mistakesSomeone who does not have the card
Reversible from outputYesNo

A checksum contains no secret, which is why anyone can generate one and why it stops nothing. A security code is closer to a message authentication code: the whole point is that possessing the message does not let you produce the tag. Both values sit on the same card, three centimetres apart, and they are opposite kinds of object — which the card number structure guide sets out in full.

What your gateway tells you about a CVV

Developers meet the security code mostly through a result field, and the values are less binary than expected. Across processors you will see roughly:

ResultMeaningWhat it usually means for you
MatchThe issuer verified the codeProceed
No matchThe issuer says it is wrongDecline, or trigger a step-up
Not processedThe issuer did not check itDecide deliberately — this is not a pass
Not presentNo code was sentYour own form probably has a gap
UnsupportedThe issuer does not check codesMore common than you would think

Stripe surfaces this as cvc_check on the charge object; other gateways use different names for the same states. The two worth writing code for are the middle rows. “Not processed” and “unsupported” are not approvals, and treating them as matches quietly removes the check from a share of your traffic. Whether you accept them is a risk decision your fraud rules should make explicitly rather than one your parsing makes by accident.

Note also that a CVV result is advisory. The issuer can approve a payment whose code did not match, and it can decline one whose code did — authorisation and verification are separate answers arriving in the same response.

Why some payments never ask for a CVV

This surprises people who assume the code is mandatory. It is not, and the reasons are routine:

Recurring payments. The code is collected on the first transaction and cannot be retained, so renewals run without it as merchant-initiated transactions.

Stored cards. A card on file is a token, and the token was never accompanied by a retrievable code. One-click checkout works precisely because the merchant kept the thing it is allowed to keep and discarded the thing it is not.

Some Maestro cards. Certain Maestro products carry no printed code at all, which is why a form that hard-requires three digits blocks them outright.

Digital wallets. Apple Pay, Google Pay, and similar authenticate the user on the device and present a dynamic cryptogram instead. Asking for a static code would add nothing.

Some MOTO transactions. Mail-order and telephone-order flows have their own rules, which vary by acquirer and region.

The pattern behind all five: the code proves physical possession at the moment of entry, and each of these flows has either already established that or replaced it with something stronger. Strong customer authentication, covered in the 3-D Secure guide, is that stronger thing — which is why the CVV has quietly become the weakest of the checks a modern checkout runs.

Auditing your own logs for it

PCI DSS prohibits retaining the security code after authorisation, and the leak is almost never deliberate — it arrives through logging the whole request body. That makes it testable. A cheap assertion, worth running in CI against whatever your logger produces:

// Fail the build if a payment payload ever reaches the logs intact.
const FORBIDDEN = /\b(cvv2?|cvc2?|cid|security[_-]?code)\b\s*[:=]\s*["']?\d{3,4}/i;

test('payment logs never contain a security code', async () => {
  await submitPayment({ number: '4242424242424242', cvc: '123', exp: '12/34' });
  const written = await readCapturedLogs();
  expect(written).not.toMatch(FORBIDDEN);
  expect(written).not.toMatch(/4242424242424242/); // the PAN, while you are here
});

Point it at your real logger output rather than a mock, and run it against the error tracker payload too — exception context is the second most common escape route. The wider set of rules is in the PCI DSS guide for developers, and the standard itself is published by the PCI Security Standards Council.

What our generator produces

A random number of the correct length for the network: three digits, or four when the card is American Express. Nothing is derived from the card number it appears beside, because nothing can be.

It exists so your form has something of the right shape to validate — field length, input mask, the four-digit branch. For anything that needs a processor to actually verify a code, use your gateway’s sandbox values instead; several processors treat the code as a trigger for specific results, so an arbitrary one will not behave as you expect.

Frequently Asked Questions

No. The value is produced by the issuing bank from the card number, expiry date and service code, encrypted under a pair of Card Verification Keys that live inside the bank’s hardware security module. Every input except those keys is printed on the card; the keys are what make the result unforgeable, and they never leave the HSM. Without them there is no calculation to run, and no tool can supply them.
Card Verification Value. Each network brands it differently — CVV2 at Visa, CVC2 at Mastercard, CID at American Express and Discover, CAV2 at JCB, CVN2 at UnionPay — but they are the same idea under different names. In code the only difference that matters is the length: four digits on American Express, three everywhere else.
Because American Express specified it that way, and its cards are 15 digits rather than 16 for the same reason: the network defined its own format before the industry converged on a common one. There is no security advantage to the extra digit worth speaking of. What matters practically is that a hard-coded three-character validation rule rejects every Amex card that reaches it.
Because the merchant cannot store it. The code is captured on the first transaction, sent for authorisation, and must then be discarded — so a saved card has a token and no code to re-send. Subsequent charges run as merchant-initiated transactions against that token, which is why recurring billing, one-click checkout, and most digital wallets never prompt for it again.
Only by the issuer, which has to hold the keys to verify it, and by organisations supporting issuing services. For everyone else PCI DSS prohibits retaining it after authorisation — not prohibits storing it unencrypted, prohibits storing it at all. A data set containing security codes therefore came from a non-compliant system, or from phishing or skimming that captured the code as it was typed.