Developer Toolbox

Regex: Credit card number

Card brands are told apart by their first digits and length. The pattern encodes the common ranges: Visa starts with 4 (13 or 16 digits), Mastercard with 51 to 55 or 2221 to 2720 (16), American Express with 34 or 37 (15), Discover with 6011 or 65 (16).

Open in Regex Tester The pattern and every example below are filled in.

Pattern

^(?:4\d{12}(?:\d{3})?|5[1-5]\d{14}|2(?:22[1-9]|2[3-9]\d|[3-6]\d{2}|7[01]\d|720)\d{12}|3[47]\d{13}|6(?:011|5\d{2})\d{12})$

How it works

^(?:
Start of the string, and of the list of brands.
4\d{12}(?:\d{3})?|
Visa: a 4, then 12 or 15 more digits.
5[1-5]\d{14}|
Mastercard: 51 to 55, then 14 digits.
2(?:22[1-9]|2[3-9]\d|[3-6]\d{2}|7[01]\d|720)\d{12}|
Mastercard: 2221 to 2720, then 12 digits.
3[47]\d{13}|
American Express: 34 or 37, then 13 digits.
6(?:011|5\d{2})\d{12}
Discover: 6011 or 65xx, then 12 digits.
)$
End of the list, and of the string.

Matches

  • 4111111111111111
  • 5500000000000004
  • 2223003122003222
  • 340000000000009
  • 6011000000000004

Doesn't match

  • 4111 1111 1111 1111
  • 1234567812345678
  • 411111111111

In your language

JavaScript
/^(?:4\d{12}(?:\d{3})?|5[1-5]\d{14}|2(?:22[1-9]|2[3-9]\d|[3-6]\d{2}|7[01]\d|720)\d{12}|3[47]\d{13}|6(?:011|5\d{2})\d{12})$/

A literal; new RegExp(source, flags) builds the same from a string.

Python
re.compile(r"^(?:4\d{12}(?:\d{3})?|5[1-5]\d{14}|2(?:22[1-9]|2[3-9]\d|[3-6]\d{2}|7[01]\d|720)\d{12}|3[47]\d{13}|6(?:011|5\d{2})\d{12})$", re.ASCII)

A raw string, so backslashes reach re as written. re.ASCII keeps \d to 0-9, as in JavaScript (Python matches any Unicode digit otherwise). Use re.fullmatch to test a whole string.

Java
Pattern.compile("^(?:4\\d{12}(?:\\d{3})?|5[1-5]\\d{14}|2(?:22[1-9]|2[3-9]\\d|[3-6]\\d{2}|7[01]\\d|720)\\d{12}|3[47]\\d{13}|6(?:011|5\\d{2})\\d{12})$")

A normal string literal, so every backslash is doubled. matcher(s).matches() tests the whole string.

Go
regexp.MustCompile(`^(?:4\d{12}(?:\d{3})?|5[1-5]\d{14}|2(?:22[1-9]|2[3-9]\d|[3-6]\d{2}|7[01]\d|720)\d{12}|3[47]\d{13}|6(?:011|5\d{2})\d{12})$`)

A raw string in backticks. RE2 runs in linear time but has no lookaround and no backreferences.

PHP
preg_match('/^(?:4\d{12}(?:\d{3})?|5[1-5]\d{14}|2(?:22[1-9]|2[3-9]\d|[3-6]\d{2}|7[01]\d|720)\d{12}|3[47]\d{13}|6(?:011|5\d{2})\d{12})$/D', $input)

PCRE with / delimiters inside a single-quoted string. Without the D modifier, $ also matches before a final newline, so it is added to patterns that end in $.

C#
new Regex(@"^(?:4\d{12}(?:\d{3})?|5[1-5]\d{14}|2(?:22[1-9]|2[3-9]\d|[3-6]\d{2}|7[01]\d|720)\d{12}|3[47]\d{13}|6(?:011|5\d{2})\d{12})$", RegexOptions.ECMAScript)

A verbatim string: backslashes stay, a quote is doubled. RegexOptions.ECMAScript keeps \d to 0-9, as in JavaScript. $ also matches before a final newline; to reject one, end the pattern with \z instead.

Common mistakes

  • Strip spaces first

    Cards print the number in groups (4111 1111 1111 1111). Remove spaces and dashes before matching.

  • Brands add ranges and lengths

    Discover also issues numbers from 644 to 649, Visa and Discover issue cards of up to 19 digits, and new ranges keep appearing. Use the pattern as a quick check before the payment provider's, never as the final word.

  • Run the Luhn check

    The last digit is a checksum over the others. A number with the right prefix and length but the wrong last digit is a typo, and the Luhn algorithm catches it in a few lines of code.

  • Never log the number

    Card numbers in logs, analytics or error reports bring those systems into scope of PCI DSS. Let the payment provider's form take the number, and keep only the last four digits.