How to Validate Card Numbers With the Luhn Check for Test Data and Redaction QA
Validate card-shaped numbers with the Luhn check and a length test. What Luhn catches, why a valid shape is still only test data, and a worked example.
How to Validate Card Numbers With the Luhn Check for Test Data and Redaction QA
If you have ever generated a batch of fake card numbers for a payment test suite, or scrubbed a support log for redaction QA, you know the awkward question that comes next: are these numbers even the right shape? A number that fails a basic format check will get rejected the moment it hits a payment form, so your test never exercises the path you cared about. A redaction script that misses a card-shaped string leaks something it should have masked. Both problems come down to the same small piece of arithmetic that card forms have used for decades: the Luhn check.
This post is about checking the shape of card-style numbers for legitimate engineering work, with the Credit Card Number List Validator. It runs in your browser, masks the values it shows you, and prints a reason next to every row. It is not a tool for confirming that a real account exists, and I will come back to why that distinction matters.
What the Luhn algorithm actually checks
The Luhn algorithm is a checksum, not a lookup. There is no database behind it. You can compute it by hand:
- Starting from the rightmost digit, double every second digit.
- If doubling gives a two-digit result (10 through 18), add those two digits together (or, equivalently, subtract 9).
- Sum all the resulting digits, including the ones you did not double.
- If the total is a multiple of 10, the number passes.
That single rule catches the two most common human mistakes. It catches any single-digit typo, because changing one digit shifts the total off a multiple of 10. And it catches most adjacent transpositions, where you swap two neighboring digits like 1234 into 1243. Those are exactly the errors people make when they retype a number off a screen, which is why card entry forms have leaned on Luhn since long before online checkout existed.
What Luhn does not check is just as important. It says nothing about whether the issuer exists, whether the account is open, or whether there is any money behind it. A checksum is a guard against accidental corruption, not a claim about reality.
What this validator checks, and what it leaves alone
This tool applies two deterministic tests, both in the browser tab, with no source text sent anywhere:
- The Luhn checksum, exactly as described above. A number whose digits do not sum to a multiple of 10 is flagged as invalid.
- A length and BIN check. A card-shaped number has to have a supported digit count. A string that is too short, too long, or carries an unsupported leading pattern is flagged, even if it happens to pass Luhn by coincidence.
Every row that fails keeps its place in the output with the exact reason printed beside it, because for a validator the failures are the point. You are not trying to produce a clean list and throw away the noise. You are trying to see which rows are wrong and why. The output is a per-row pass/fail report, and you can keep unique rows only, preserve the invalid ones for review, sort the normalized result, and export as CSV, JSON, Markdown, SQL IN, TypeScript union, or plain lines. Because cards are a sensitive profile, the values are masked in the output while you still get the full validation signal.
A worked example
Suppose you paste this short list, one number per line:
4242 4242 4242 4242
4000056655665556
4242424242424241
1234567890123456
4242
Here is what the report tells you, row by row:
| Input | Result | Reason | |---|---|---| | 4242 4242 4242 4242 | valid | passes Luhn, 16 digits | | 4000056655665556 | valid | passes Luhn, 16 digits | | 4242424242424241 | invalid | fails Luhn (last digit changed from 2 to 1) | | 1234567890123456 | invalid | fails Luhn checksum | | 4242 | invalid | length not supported |
Notice that the first row has spaces and still validates: normalization strips the formatting before the check, so a number copied with grouping spaces is treated the same as a bare one. The third row is the textbook single-digit typo, the very mistake Luhn was designed to catch. The fourth has a believable shape but fails the checksum. The last one is the right family of digits but the wrong count, so the length test flags it before Luhn even matters.
A valid shape is still just test data
This is the part I want to be blunt about, because it is where people get the wrong idea. A number that passes both checks is a number that could be a card. It is not proof that it is one. The classic 4242 4242 4242 4242 is a Stripe test number: it passes Luhn perfectly and will never move a cent. Plenty of randomly generated, Luhn-valid 16-digit strings correspond to no issued card at all.
So treat a "valid" verdict as exactly what it is: this string is well-formed enough to enter a card field without being rejected on syntax. That is genuinely useful. It means your test fixtures will exercise the real submit path instead of bouncing off front-end validation, and it means your redaction QA can confirm that a card-shaped token in a log would have been caught. It does not mean anything about an account, and the tool's own notes say so: do not treat validation as proof that the account or resource exists.
How I use it during redaction QA
The first time this saved me real time was a redaction review. We had a masking rule that was supposed to replace any card-shaped number in support transcripts with a placeholder, and I needed to confirm it was firing on everything it should. I pasted a chunk of synthetic transcript into the validator, kept the invalid rows visible, and sorted the normalized output. Within a few seconds I could see which strings the engine considered card-shaped and which it had skipped for failing Luhn or the length test. That gave me a clean, defensible list of "these are the numbers a masking rule must catch" to check the redaction output against, all without any real customer data leaving the tab. The masked display meant I could screenshot the report for the ticket without redacting my own screenshot first.
Responsible use
To be clear about intent: this validator exists for test-data generation and redaction or compliance QA, not for collecting or harvesting card numbers. Validating the shape of a number tells you nothing about a person and gives you no access to anything. Keep your inputs to synthetic test fixtures, sandbox numbers, and data you are already authorized to handle, and remember that everything here runs locally precisely so that real values never travel to a server. If you are cleaning a noisy export rather than judging individual numbers, the Credit Card Number Extractor pulls candidates out of messy text, and the Credit Card Number Deduplicator collapses a list to unique entries before you validate it. Used for the right job, a checksum and a length test are a quiet, reliable way to keep test data honest.
Made by Toolora · Updated 2026-06-13