The Luhn Algorithm Explained
Every credit card number you've ever seen — real or fake — follows a simple mathematical rule called the Luhn algorithm (also called "modulus 10" or "mod 10"). It's not a security feature and it doesn't verify that a card is real, active, or belongs to anyone. It's a checksum: a quick way to catch typos and malformed numbers before they ever reach a payment processor. This is exactly what our own card generator uses to produce valid-format numbers, and it's what every payment form's client-side validation should check first.
Advertisement
What the Luhn Algorithm Actually Checks
The Luhn algorithm confirms that a number is internally consistent — that its digits satisfy a specific formula. It cannot tell you whether a card exists, is active, has funds, or was issued by a real bank. A number can pass Luhn validation and still be completely fake, exactly like the ones our generator produces. This is why Luhn validation is only ever the first, most basic check in a payment flow — real transactions still go through the card network and issuing bank for the checks that actually matter.
How It Works, Step by Step
- Starting from the rightmost digit (the check digit) and moving left, double the value of every second digit.
- If doubling a digit results in a number greater than 9, subtract 9 from it (this is the same as summing its two digits — e.g. 14 becomes 1+4=5, or equivalently 14-9=5).
- Sum all the digits together — both the ones you doubled (after adjustment) and the ones you left alone.
- If the total sum is evenly divisible by 10 (i.e.,
sum % 10 === 0), the number passes Luhn validation.
Worked Example
Let's validate the number 4532015112830366 (a fictional Visa-format number):
| Position (right to left) | Digit | Double every 2nd | Adjusted |
|---|---|---|---|
| 1 | 6 | no | 6 |
| 2 | 6 | yes (12) | 3 (1+2) |
| 3 | 3 | no | 3 |
| 4 | 0 | yes (0) | 0 |
| 5 | 3 | no | 3 |
| 6 | 8 | yes (16) | 7 (1+6) |
| 7 | 2 | no | 2 |
| 8 | 1 | yes (2) | 2 |
| 9 | 1 | no | 1 |
| 10 | 5 | yes (10) | 1 (1+0) |
| 11 | 1 | no | 1 |
| 12 | 0 | yes (0) | 0 |
| 13 | 2 | no | 2 |
| 14 | 3 | yes (6) | 6 |
| 15 | 5 | no | 5 |
| 16 | 4 | yes (8) | 8 |
Sum of adjusted digits: 6+3+3+0+3+7+2+2+1+1+1+0+2+6+5+8 = 50. Since 50 is evenly divisible by 10, the number passes Luhn validation.
Code Examples
JavaScript
function isValidLuhn(cardNumber) {
const digits = cardNumber
.replace(/\D/g, '')
.split('')
.reverse()
.map(Number);
let sum = 0;
for (let i = 0; i < digits.length; i++) {
let digit = digits[i];
if (i % 2 === 1) {
digit *= 2;
if (digit > 9) digit -= 9;
}
sum += digit;
}
return sum % 10 === 0;
}
isValidLuhn("4532015112830366"); // truePython
def is_valid_luhn(card_number: str) -> bool:
digits = [int(d) for d in card_number if d.isdigit()][::-1]
total = 0
for i, digit in enumerate(digits):
if i % 2 == 1:
digit *= 2
if digit > 9:
digit -= 9
total += digit
return total % 10 == 0PHP
function isValidLuhn(string $cardNumber): bool {
$digits = array_reverse(str_split(preg_replace('/\D/', '', $cardNumber)));
$sum = 0;
foreach ($digits as $i => $digit) {
$digit = (int)$digit;
if ($i % 2 === 1) {
$digit *= 2;
if ($digit > 9) {
$digit -= 9;
}
}
$sum += $digit;
}
return $sum % 10 === 0;
}Generating a Valid Check Digit (Not Just Validating One)
To generate a Luhn-valid number rather than just check one, you build all the digits except the last, run the same doubling process on those (the doubling now starts with the rightmost of them, since the check digit will be appended after it), sum them, and set the final check digit to whatever value makes the total a multiple of 10 — that is, (10 - (sum % 10)) % 10. This is exactly what our own generator does automatically.
Don't want to implement this yourself? Generate Luhn-valid test numbers instantly with our free tool →
Why This Matters for Payment Form Testing
Client-side Luhn validation should catch obviously malformed input before a form ever submits, improving UX and reducing unnecessary API calls to your payment processor. But passing Luhn is not the same as being a real, chargeable card. For a full walkthrough, see our guide on how to test a payment form without real card data.
For numbers that actually process through a sandbox environment and simulate real outcomes, see our Stripe test cards and PayPal/Braintree test cards guides.
FAQ
No. Passing the Luhn check only means the digits are internally consistent with the mod 10 formula. It says nothing about whether the card was issued by a bank, is active, belongs to anyone, or has funds. Randomly generated test numbers pass Luhn and are completely fake.