Reference guide · updated September 2026

The Luhn algorithm, explained and implemented.

Every card number you've ever used passes this exact checksum. Here's precisely how it works, and working code in six languages so you can verify it yourself rather than take our word for it.

The Algorithm, Step by Step

Starting from the rightmost digit, double every second digit

Moving right to left, double the value of every second digit. The rightmost digit itself (the check digit) is never doubled — it's what you're solving for or verifying.

If doubling produces a two-digit number, sum its digits

8 doubled is 16 — instead of using 16, add its digits together: 1 + 6 = 7. This is mathematically identical to subtracting 9 from any doubled value over 9.

Sum every digit in the number

Add up all the digits — the doubled-and-reduced ones and the untouched ones alike — into a single total.

Check divisibility by 10

If the total is evenly divisible by 10, the number is Luhn-valid. If you're generating a check digit rather than validating an existing number, the check digit is whatever value makes the total divisible by 10.

Working Implementations

The Python and JavaScript implementations below were executed directly against known-valid card numbers before publishing (7 test cases, including multiple networks and a deliberately invalid number). The Java, PHP, Ruby, and Go versions are faithful line-for-line translations of that same verified logic — copy-paste ready, not pseudocode.

Python

def is_luhn_valid(number: str) -> bool:
    digits = [int(d) for d in number]
    checksum = 0
    for i, d in enumerate(reversed(digits)):
        if i % 2 == 1:
            d *= 2
            if d > 9:
                d -= 9
        checksum += d
    return checksum % 10 == 0

JavaScript

function isLuhnValid(number) {
  let sum = 0, alt = false;
  for (let i = number.length - 1; i >= 0; i--) {
    let d = parseInt(number[i], 10);
    if (alt) { d *= 2; if (d > 9) d -= 9; }
    sum += d; alt = !alt;
  }
  return sum % 10 === 0;
}

Java

public static boolean isLuhnValid(String number) {
    int sum = 0;
    boolean alt = false;
    for (int i = number.length() - 1; i >= 0; i--) {
        int d = Character.getNumericValue(number.charAt(i));
        if (alt) { d *= 2; if (d > 9) d -= 9; }
        sum += d; alt = !alt;
    }
    return sum % 10 == 0;
}

PHP

function isLuhnValid(string $number): bool {
    $sum = 0;
    $alt = false;
    for ($i = strlen($number) - 1; $i >= 0; $i--) {
        $d = (int) $number[$i];
        if ($alt) { $d *= 2; if ($d > 9) $d -= 9; }
        $sum += $d;
        $alt = !$alt;
    }
    return $sum % 10 === 0;
}

Ruby

def luhn_valid?(number)
  sum = 0
  alt = false
  number.reverse.each_char do |c|
    d = c.to_i
    if alt
      d *= 2
      d -= 9 if d > 9
    end
    sum += d
    alt = !alt
  end
  sum % 10 == 0
end

Go

func isLuhnValid(number string) bool {
    sum := 0
    alt := false
    for i := len(number) - 1; i >= 0; i-- {
        d := int(number[i] - '0')
        if alt {
            d *= 2
            if d > 9 {
                d -= 9
            }
        }
        sum += d
        alt = !alt
    }
    return sum%10 == 0
}

A Formula, Not a Secret

Worth being direct about: the Luhn algorithm is public domain, patented in 1954 and long since expired, and specified formally in ISO/IEC 7812-1. It's designed to catch accidental transposition and single-digit errors — not to prevent fraud. Anyone can implement it, which is exactly why every card issuer does, and why a checksum passing tells you a number is well-formed, not that it belongs to a real, funded account.

Generate test card numbers →

Related Guides

Frequently Asked Questions

Is the Luhn algorithm the same as encryption or fraud detection?

No. It's a simple checksum designed to catch typos and transposition errors, not a security or fraud-prevention mechanism. A number can be perfectly Luhn-valid and still not correspond to any real, issued card.

Do all card networks use the Luhn algorithm?

Yes — Visa, Mastercard, American Express, Discover, and virtually every other major card network use the same Luhn checksum as their final digit, standardized under ISO/IEC 7812-1.

Can I use these code examples in my own project?

Yes — the Luhn algorithm itself is public domain, and these implementations are simple enough that there's no meaningful licensing concern either way.