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).
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
reas written.re.ASCIIkeeps\dto0-9, as in JavaScript (Python matches any Unicode digit otherwise). Usere.fullmatchto 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 theDmodifier,$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.ECMAScriptkeeps\dto0-9, as in JavaScript.$also matches before a final newline; to reject one, end the pattern with\zinstead.
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.