How to Validate a Credit Card Number (2026)

Credit card number validation is essential for any payment form, QA testing workflow, or data quality pipeline. This guide covers the complete validation pipeline: the Luhn algorithm (mod-10 checksum), card brand detection by BIN/IIN prefix, length validation, and practical code examples in JavaScript and Python.

Try it now: CardWise Luhn Validator performs all three validation steps (Luhn check, brand detection, BIN lookup) in your browser — no data uploaded.

Three Layers of Credit Card Validation

Validating a credit card number is not a single check. There are three independent layers, each catching different types of errors:

LayerWhat It ChecksWhat It Catches
1. Luhn AlgorithmMod-10 checksum of the full numberTypos, single-digit errors, adjacent transpositions
2. Brand + LengthPrefix and length match the card brand rulesWrong-format numbers, truncated input
3. BIN/IIN LookupFirst 6-8 digits identify the issuing bankInvalid issuer, suspicious patterns
Important: None of these layers confirm that the card account is real or active. Only a payment processor (via a $0 or $1 authorization hold) can verify the account. Luhn validation is for data quality, not fraud prevention.

The Luhn Algorithm — Step by Step

The Luhn algorithm (also called "mod-10" or "modulus 10") was invented by IBM scientist Hans Peter Luhn in 1954. It is used in every credit card number worldwide, plus IMEI, ICCID, and other identification numbers.

How it works:

  1. Starting from the rightmost digit (the check digit), moving left, double every second digit.
  2. If doubling produces a number greater than 9, subtract 9 (or add the two digits together: e.g., 8 × 2 = 16 → 1 + 6 = 7).
  3. Sum all the digits (both doubled and non-doubled).
  4. If the total modulo 10 equals 0, the number is valid.

Worked example: 4532 0151 1283 0366

Number:  4  5  3  2  0  1  5  1  1  2  8  3  0  3  6  6
Position: 1  2  3  4  5  6  7  8  9  10 11 12 13 14 15 16
Doubling: 8  5  6  2  0  1  10 1  2  2  16 3  0  3  12 6
Adjusted: 8  5  6  2  0  1  1  1  2  2  7  3  0  3  3  6
                                                          ---
Sum = 8+5+6+2+0+1+1+1+2+2+7+3+0+3+3+6 = 50
50 mod 10 = 0 → VALID

Card Brand Detection by Prefix

Each card brand has a specific prefix (BIN/IIN range) and expected length. You should check these before running the Luhn algorithm to catch obviously wrong formats:

BrandPrefix (BIN)LengthExample
Visa413, 16, or 194xxx xxxx xxxx xxxx
Mastercard51-55, 2221-2720165xxx xxxx xxxx xxxx
American Express34, 37153xxx xxxxxx xxxxx
Discover6011, 65, 644-64916-196xxx xxxx xxxx xxxx
JCB3528-358916-1935xx xxxx xxxx xxxx
Diners Club300-305, 36, 38, 3914-193xxx xxxxxx xxxx
UnionPay6216-1962xx xxxx xxxx xxxx

JavaScript Implementation

function luhnCheck(cardNumber) {
  // Remove spaces and dashes
  const digits = cardNumber.replace(/[\s-]/g, '');
  if (!/^\d+$/.test(digits)) return false;

  let sum = 0;
  let isEven = false;

  // Process from right to left
  for (let i = digits.length - 1; i >= 0; i--) {
    let digit = parseInt(digits[i], 10);

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

    sum += digit;
    isEven = !isEven;
  }

  return sum % 10 === 0;
}

function detectBrand(cardNumber) {
  const num = cardNumber.replace(/[\s-]/g, '');
  if (/^4/.test(num)) return 'Visa';
  if (/^(5[1-5]|2[2-7]\d{2})/.test(num)) return 'Mastercard';
  if (/^3[47]/.test(num)) return 'American Express';
  if (/^(6011|65|64[4-9])/.test(num)) return 'Discover';
  if (/^35(2[89]|[3-8]\d)/.test(num)) return 'JCB';
  if (/^3[0-5]|36|38|39/.test(num)) return 'Diners Club';
  if (/^62/.test(num)) return 'UnionPay';
  return 'Unknown';
}

// Usage
console.log(luhnCheck('4532 0151 1283 0366')); // true
console.log(detectBrand('4532 0151 1283 0366')); // 'Visa'

Python Implementation

def luhn_check(card_number: str) -> bool:
    """Validate a number using the Luhn algorithm."""
    digits = card_number.replace(' ', '').replace('-', '')
    if not digits.isdigit():
        return False

    total = 0
    is_even = False

    for digit in reversed(digits):
        d = int(digit)
        if is_even:
            d *= 2
            if d > 9:
                d -= 9
        total += d
        is_even = not is_even

    return total % 10 == 0

def detect_brand(card_number: str) -> str:
    """Detect card brand from BIN prefix."""
    num = card_number.replace(' ', '').replace('-', '')
    if num.startswith('4'):
        return 'Visa'
    if num[:2] in {'51','52','53','54','55'} or (2221 <= int(num[:4]) <= 2720):
        return 'Mastercard'
    if num[:2] in {'34', '37'}:
        return 'American Express'
    if num.startswith(('6011', '65', '644', '645', '646', '647', '648', '649')):
        return 'Discover'
    if num.startswith('62'):
        return 'UnionPay'
    return 'Unknown'

# Usage
print(luhn_check('4532 0151 1283 0366'))  # True
print(detect_brand('4532 0151 1283 0366')) # Visa

Form Validation Best Practices

When building a credit card input form, follow these UX and security guidelines:

Pro tip: For QA testing, use the CardWise Luhn Validator's "Generate Check Digit" mode to create valid test card numbers without using real card data.

What BIN/IIN Lookup Adds

BIN (Bank Identification Number, first 6 digits) or IIN (Issuer Identification Number, first 8 digits) lookup goes beyond structural validation — it identifies the issuing bank, card type (debit/credit), country, and card level (platinum, gold, business, etc.).

Common use cases for BIN lookup:

Frequently Asked Questions

Does a valid Luhn check mean the credit card is real?

No. The Luhn algorithm only verifies that the number is structurally well-formed. It does not confirm the account exists, is active, or has funds. Only a payment processor can verify the account through an authorization request. Luhn validation is for catching typos and data entry errors, not for fraud prevention.

Can I generate test credit card numbers?

Yes. Enter any partial number (without the last digit) in the CardWise Luhn Validator and select "Generate Check Digit" mode. The tool will compute the correct Luhn check digit and append it. This is useful for QA testing of payment forms without using real card data. Most payment processors also provide designated test card numbers (e.g., Stripe's 4242 4242 4242 4242).

What is the difference between Luhn validation and a $0 authorization?

Luhn validation is a client-side mathematical check that costs nothing and catches typos. A $0 (or $1) authorization is a real request to the card network and issuing bank that confirms the account exists and is in good standing. It requires PCI compliance and a payment processor account. Use Luhn for form validation and $0 authorization for account verification.

Is it safe to enter my credit card number in an online validator?

Only if the validator is client-side. CardWise Luhn Validator processes everything in your browser — your card number never leaves your device. Many other online validators send your input to a remote server. Always check for a privacy/client-side statement before entering sensitive numbers.