Validating credit card numbers at the point of entry catches typos before an expensive payment gateway call is made. In this tutorial you will learn how to use Java regular expressions to check card number format for the major card networks, and then apply the Luhn algorithm — the checksum standard built into every valid card number — to confirm the number is mathematically plausible. The combination of regex format check + Luhn checksum is the industry-standard client-side validation used by checkout pages worldwide.
What Regex Can and Cannot Do
A regular expression can verify that a card number has the correct length and prefix for a given network. It cannot verify whether the account actually exists or has funds — that requires a network call to the card issuer. Always pair regex with the Luhn checksum for a complete client-side check.
Card Network Patterns
| Network | Starts With | Length |
|---|---|---|
| Visa | 4 | 13 or 16 |
| Mastercard | 51–55, 2221–2720 | 16 |
| American Express | 34, 37 | 15 |
| Discover | 6011, 622126–622925, 644–649, 65 | 16 |
| Diners Club | 300–305, 36, 38 | 14 |
Java Implementation
import java.util.regex.Pattern;
/**
* Utility for client-side credit card validation.
* Combines regex format check with Luhn checksum verification.
*/
public class CreditCardValidator {
// ---------- Regex Patterns (spaces and dashes stripped before matching) ----------
// Visa: starts with 4, 13 or 16 digits
private static final Pattern VISA =
Pattern.compile("^4[0-9]{12}([0-9]{3})?$");
// Mastercard: starts with 51-55 OR 2221-2720, always 16 digits
private static final Pattern MASTERCARD =
Pattern.compile("^(5[1-5][0-9]{14}|2(2[2-9][1-9]|[3-6][0-9]{2}|7[01][0-9]|720)[0-9]{12})$");
// American Express: starts with 34 or 37, 15 digits
private static final Pattern AMEX =
Pattern.compile("^3[47][0-9]{13}$");
// Discover: starts with 6011, 65, or 644-649, 16 digits
private static final Pattern DISCOVER =
Pattern.compile("^6(011[0-9]{12}|5[0-9]{14}|4[4-9][0-9]{13})$");
// ---------- Luhn Algorithm ----------
/**
* Returns true when the digit sequence passes the Luhn checksum.
* Works on any numeric string (spaces and dashes already removed).
*/
static boolean luhnCheck(String number) {
int sum = 0;
boolean doubleIt = false;
// Iterate from right to left
for (int i = number.length() - 1; i >= 0; i--) {
int digit = number.charAt(i) - '0';
if (doubleIt) {
digit *= 2;
if (digit > 9) digit -= 9; // same as summing the two digits
}
sum += digit;
doubleIt = !doubleIt;
}
return sum % 10 == 0;
}
// ---------- Public API ----------
/** Identifies the card network, or returns UNKNOWN. */
public static String detectNetwork(String rawNumber) {
String n = rawNumber.replaceAll("[ -]", ""); // remove spaces and dashes
if (VISA.matcher(n).matches()) return "Visa";
if (MASTERCARD.matcher(n).matches()) return "Mastercard";
if (AMEX.matcher(n).matches()) return "American Express";
if (DISCOVER.matcher(n).matches()) return "Discover";
return "Unknown";
}
/** Returns true when the number has a valid format AND a valid Luhn checksum. */
public static boolean isValid(String rawNumber) {
String n = rawNumber.replaceAll("[ -]", "");
if (n.isEmpty() || !n.matches("[0-9]+")) return false;
boolean knownNetwork = !detectNetwork(rawNumber).equals("Unknown");
return knownNetwork && luhnCheck(n);
}
// ---------- Demo ----------
public static void main(String[] args) {
String[] testCards = {
"4532015112830366", // Valid Visa
"4532015112830367", // Invalid Luhn
"5425233430109903", // Valid Mastercard
"374251018720955", // Valid Amex
"6011111111111117", // Valid Discover
"1234567890123456", // Unknown network
"4532 0151 1283 0366", // Valid Visa with spaces
};
System.out.printf("%-30s %-18s %s%n", "Card Number", "Network", "Valid?");
System.out.println("-".repeat(65));
for (String card : testCards) {
System.out.printf("%-30s %-18s %s%n",
card, detectNetwork(card), isValid(card) ? "YES" : "NO");
}
}
}
How the Code Works
- Normalise input —
replaceAll("[ -]", "")strips spaces and dashes so both4532015112830366and4532 0151 1283 0366are treated identically. - Network detection — each
Patternencodes the prefix rules and exact length for its network. The patterns are compiled once asstatic finalfields for performance. - Luhn algorithm — starting from the rightmost digit, every second digit is doubled; if the doubled value exceeds 9, subtract 9. Sum all digits. A valid card number yields a sum divisible by 10.
- Combined validation —
isValid()requires both a recognised network pattern and a passing Luhn checksum. A number can satisfy one condition without the other (e.g. correct prefix but wrong check digit).
Sample Output
Card Number Network Valid?
-----------------------------------------------------------------
4532015112830366 Visa YES
4532015112830367 Visa NO
5425233430109903 Mastercard YES
374251018720955 American Express YES
6011111111111117 Discover YES
1234567890123456 Unknown NO
4532 0151 1283 0366 Visa YES
JUnit Test Cases
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class CreditCardValidatorTest {
@Test void visaValid() { assertTrue(CreditCardValidator.isValid("4532015112830366")); }
@Test void visaInvalidLuhn() { assertFalse(CreditCardValidator.isValid("4532015112830367")); }
@Test void mastercardValid() { assertTrue(CreditCardValidator.isValid("5425233430109903")); }
@Test void amexValid() { assertTrue(CreditCardValidator.isValid("374251018720955")); }
@Test void discoverValid() { assertTrue(CreditCardValidator.isValid("6011111111111117")); }
@Test void unknownNetwork() { assertFalse(CreditCardValidator.isValid("1234567890123456")); }
@Test void visaWithSpaces() { assertTrue(CreditCardValidator.isValid("4532 0151 1283 0366")); }
@Test void emptyString() { assertFalse(CreditCardValidator.isValid("")); }
@Test void detectVisa() { assertEquals("Visa", CreditCardValidator.detectNetwork("4532015112830366")); }
@Test void detectAmex() { assertEquals("American Express", CreditCardValidator.detectNetwork("374251018720955")); }
}
Security Reminder
Client-side validation improves user experience but must never replace server-side validation. Never log or store raw card numbers in your application — pass them directly to a PCI-compliant payment gateway (Stripe, Braintree, Adyen) and let the gateway handle tokenisation and storage.
See Also
Conclusion
Effective credit card validation combines a per-network regex (to check prefix and length) with the Luhn algorithm (to verify the check digit). The CreditCardValidator class above handles all four major networks, accepts numbers with spaces or dashes, and is fully testable. Remember: this is client-side convenience validation only — always route actual card data through a PCI-compliant payment processor.