A secure testing tool for payment system developers and QA professionals. All test numbers are non-functional and strictly for development environments.

4 min read By Namso Gen

The Luhn Algorithm Explained: How Card Number Validation Works

Every card number you have ever typed into a form was checked by a 70-year-old algorithm before it left your browser. The Luhn algorithm โ€” also called the MOD 10 or modulus 10 algorithm โ€” is the checksum that all major card networks use to catch typos. This guide explains how it works, how to implement it, and what it cannot do.

What is the Luhn algorithm?

The Luhn algorithm was invented by IBM scientist Hans Peter Luhn in 1954 and patented in 1960. It is a simple checksum formula that detects accidental errors in identification numbers: single-digit mistakes, most adjacent transpositions and a few other error classes.

It is not a security measure, and it is not encryption. It is a data-integrity check. Card networks append one Luhn check digit to every card number, and any system can verify it with a few lines of code and no network access.

How it works, step by step

Given a number, for example 79927398713:

  1. Starting from the rightmost digit and moving left, double every second digit.
  2. If doubling produces a value greater than 9, subtract 9 (this is equivalent to adding the two digits of the result together).
  3. Sum all digits โ€” both the doubled and the untouched ones.
  4. If the total is divisible by 10, the number passes the check.

For 79927398713 the processed digits sum to 70, so it is valid.

For a real card, the check digit is not known in advance: the issuer computes it so that the final number passes. That is why every valid card number in the world satisfies the same one-line condition: total % 10 === 0.

Implementation

JavaScript

function isValidLuhn(number) {
  const digits = number.replace(/\D/g, '');
  let sum = 0;
  let double = false;

  for (let i = digits.length - 1; i >= 0; i--) {
    let digit = Number(digits[i]);

    if (double) {
      digit *= 2;
      if (digit > 9) digit -= 9;
    }

    sum += digit;
    double = !double;
  }

  return sum % 10 === 0;
}

Python

def is_valid_luhn(number: str) -> bool:
    digits = [int(c) for c in number if c.isdigit()]
    total = 0

    for index, digit in enumerate(reversed(digits)):
        if index % 2 == 1:
            digit *= 2
            if digit > 9:
                digit -= 9
        total += digit

    return total % 10 == 0

PHP

function isValidLuhn(string $number): bool
{
    $digits = preg_replace('/\D/', '', $number);
    $sum = 0;
    $double = false;

    for ($i = strlen($digits) - 1; $i >= 0; $i--) {
        $digit = (int) $digits[$i];

        if ($double) {
            $digit *= 2;
            if ($digit > 9) {
                $digit -= 9;
            }
        }

        $sum += $digit;
        $double = !$double;
    }

    return $sum % 10 === 0;
}

You can try any number instantly with our Luhn validator.

What Luhn does not catch

The Luhn algorithm is deliberately simple, which also makes it easy to fool:

  • It cannot tell whether a card exists. Any number can be made Luhn-valid, including numbers for BINs that were never issued. Luhn says nothing about the account, balance or issuer.
  • It misses some transpositions. Swapping 09 and 90 produces the same sum, so that particular transposition is not detected. Most other adjacent transpositions are caught.
  • It does not validate length or BIN. Length rules and BIN ranges are separate checks. A 16-digit number can pass Luhn while being invalid for a 15-digit Amex BIN.
  • It is not a security feature. Attackers generate Luhn-valid numbers trivially; the check exists to protect users from typos, not systems from fraud.

Why developers care

If you build anything that accepts card data, you will meet Luhn in at least three places:

  1. Client-side validation โ€” give users instant feedback before submitting a form, and avoid a round trip to your payment provider for an obvious typo.
  2. Test data generation โ€” fixtures, seeds and QA scripts need numbers that pass validation, which means the check digit must be computed correctly. That is exactly what our test card generator does.
  3. Parsing and normalization โ€” when numbers arrive from customers, spreadsheets or logs, Luhn is the cheapest first-line sanity check before you send anything to a gateway.

Generating valid test numbers

To generate structurally valid test numbers from any BIN, use the Namso Gen card generator. It computes the Luhn check digit for you and can add expiry dates and CVVs. If you want to enumerate every combination of a pattern, the advanced generator accepts x wildcards.

For a full walkthrough of checks that go beyond Luhn, see BIN numbers explained or check any prefix with the BIN checker.

Summary

  • Luhn (MOD 10) is a checksum, not a validity or security check.
  • Doubling every second digit from the right, summing, and testing divisibility by 10 is all there is to it.
  • Every major card network uses it, which is why every real card number passes.
  • Use it for typo detection, never as your only validation.

Related articles