Overview
This guide covers the following validation aspects:
- Card Number Validation using the Luhn Algorithm
- Card Type Detection (Visa, MasterCard, etc.)
- Expiry Date Formatting and Validation
- CVV Validation
- Real-time Input Formatting
🧪 Implementation
1. Card Number Validation (Luhn Algorithm)
The Luhn Algorithm is a simple checksum formula used to validate identification numbers, such as credit card numbers.
function isValidCardNumber(number) {
const sanitized = number.replace(/\D/g, '');
let sum = 0;
let shouldDouble = false;
for (let i = sanitized.length - 1; i >= 0; i--) {
let digit = parseInt(sanitized.charAt(i), 10);
if (shouldDouble) {
digit *= 2;
if (digit > 9) digit -= 9;
}
sum += digit;
shouldDouble = !shouldDouble;
}
return sum % 10 === 0;
}
2. Card Type Detection
Detecting the card type helps in providing specific validations and UI cues.
function getCardType(number) {
const sanitized = number.replace(/\D/g, '');
const cardPatterns = {
Visa: /^4[0-9]{12}(?:[0-9]{3})?$/,
MasterCard: /^5[1-5][0-9]{14}$/,
AmEx: /^3[47][0-9]{13}$/,
Discover: /^6(?:011|5[0-9]{2})[0-9]{12}$/
};
for (const [card, pattern] of Object.entries(cardPatterns)) {
if (pattern.test(sanitized)) {
return card;
}
}
return 'Unknown';
}
3. Expiry Date Formatting and Validation
Ensures the expiry date is in MM/YY format and not in the past.
function formatExpiryDate(input) {
const sanitized = input.replace(/\D/g, '').slice(0, 4);
if (sanitized.length >= 3) {
return sanitized.slice(0, 2) + '/' + sanitized.slice(2);
}
return sanitized;
}
function isValidExpiryDate(expiry) {
const [month, year] = expiry.split('/').map(Number);
if (!month || !year || month < 1 || month > 12) return false;
const currentDate = new Date();
const currentYear = currentDate.getFullYear() % 100;
const currentMonth = currentDate.getMonth() + 1;
return year > currentYear || (year === currentYear && month >= currentMonth);
}
4. CVV Validation
Validates the CVV based on card type.
function isValidCVV(cvv, cardType) {
const sanitized = cvv.replace(/\D/g, '');
if (cardType === 'AmEx') {
return /^\d{4}$/.test(sanitized);
}
return /^\d{3}$/.test(sanitized);
}
5. Real-time Input Formatting
Formats the card number input in real-time for better readability.
function formatCardNumber(input) {
const sanitized = input.replace(/\D/g, '').slice(0, 16);
return sanitized.replace(/(.{4})/g, '$1 ').trim();
}
🧪 Example Usage
Here’s how you can integrate the above functions into your form:
<form id="payment-form">
<label>
Card Number:
<input type="text" id="card-number" maxlength="19" placeholder="1234 5678 9012 3456" />
</label>
<label>
Expiry Date:
<input type="text" id="expiry-date" maxlength="5" placeholder="MM/YY" />
</label>
<label>
CVV:
<input type="text" id="cvv" maxlength="4" placeholder="123" />
</label>
<button type="submit">Submit</button>
</form>
document.getElementById('card-number').addEventListener('input', (e) => {
e.target.value = formatCardNumber(e.target.value);
});
document.getElementById('expiry-date').addEventListener('input', (e) => {
e.target.value = formatExpiryDate(e.target.value);
});
document.getElementById('payment-form').addEventListener('submit', (e) => {
e.preventDefault();
const cardNumber = document.getElementById('card-number').value;
const expiryDate = document.getElementById('expiry-date').value;
const cvv = document.getElementById('cvv').value;
const cardType = getCardType(cardNumber);
if (!isValidCardNumber(cardNumber)) {
alert('Invalid card number.');
return;
}
if (!isValidExpiryDate(expiryDate)) {
alert('Invalid expiry date.');
return;
}
if (!isValidCVV(cvv, cardType)) {
alert('Invalid CVV.');
return;
}
alert('Payment details are valid!');
});
Regular Expressions for the most used debit/credit cards
Title | Visa Credit Card |
---|---|
Expression | (^4\d{12}$)|(^4[0-8]\d{14}$)|(^(49)[^013]\d{13}$)|(^(49030)[0-1]\d{10}$)|(^(49033)[0-4]\d{10}$)|(^(49110)[^12]\d{10}$)|(^(49117)[0-3]\d{10}$)|(^(49118)[^0-2]\d{10}$)|(^(493)[^6]\d{12}$) |
Description | Matches Visa Credit Card types 13 or 16 digits starting with 4 including Visa Credit Card, Visa ATM only, Visa Electron and Visa Delta. Safe for international and will NOT match “^(49)” Switch debit cards. |
Matches | 4111111111111111 |
Non-Matches | 4903020000000008 |
Title | Maestro Credit Card |
Expression | (^(5[0678])\d{11,18}$) |(^(6[^0357])\d{11,18}$) |(^(601)[^1]\d{9,16}$) |(^(6011)\d{9,11}$) |(^(6011)\d{13,16}$) |(^(65)\d{11,13}$) |(^(65)\d{15,18}$) |(^(633)[^34](\d{9,16}$)) |(^(6333)[0-4](\d{8,10}$)) |(^(6333)[0-4](\d{12}$)) |(^(6333)[0-4](\d{15}$)) |(^(6333)[5-9](\d{8,10}$)) |(^(6333)[5-9](\d{12}$)) |(^(6333)[5-9](\d{15}$)) |(^(6334)[0-4](\d{8,10}$)) |(^(6334)[0-4](\d{12}$)) |(^(6334)[0-4](\d{15}$)) |(^(67)[^(59)](\d{9,16}$)) |(^(6759)](\d{9,11}$)) |(^(6759)](\d{13}$)) |(^(6759)](\d{16}$)) |(^(67)[^(67)](\d{9,16}$)) |(^(6767)](\d{9,11}$)) |(^(6767)](\d{13}$)) |(^(6767)](\d{16}$)) |
Description | Maestro ONLY card matching – begins with “50” or “56-58” or “6” & 13 to 20 total digits. Overlap of Discover, Solo and Switch is handled and NOT matched. |
Matches | 5600000000000003 |
Non-Matches | 6011000000000004 |
Title | MasterCard Credit Card |
Expression | ^5[1-5]\d{14}$ |
Description | Matches MasterCard credit cards. International safe, no overlap with other cards. |
Matches | 5100000000000008 |
Non-Matches | 201400000000009 |
Title | Discover Credit Card |
Expression | (^(6011)\d{12}$)|(^(65)\d{14}$) |
Description | Discover Credit Card matching. International safe, no overlap with other cards. |
Matches | 6011000000000004 |
Non-Matches | 4508750000000009 |
Title | AMEX Credit Card |
Expression | (^3[47])((\d{11}$)|(\d{13}$)) |
Description | AMEX Credit Card match. International safe. |
Matches | 340000000000009 |
Non-Matches | 4508750000000009 |
Title | Solo Credit card |
Expression | (^(6334)[5-9](\d{11}$|\d{13,14}$)) |(^(6767)(\d{12}$|\d{14,15}$)) |
Description | Solo Credit Card match. International safe. |
Matches | 6334500000000003 |
Non-Matches | 3528000000000007 |
Title | Switch Credit Card |
Expression | (^(49030)[2-9](\d{10}$|\d{12,13}$)) |(^(49033)[5-9](\d{10}$|\d{12,13}$)) |(^(49110)[1-2](\d{10}$|\d{12,13}$)) |(^(49117)[4-9](\d{10}$|\d{12,13}$)) |(^(49118)[0-2](\d{10}$|\d{12,13}$)) |(^(4936)(\d{12}$|\d{14,15}$)) |(^(564182)(\d{11}$|\d{13,14}$)) |(^(6333)[0-4](\d{11}$|\d{13,14}$)) |(^(6759)(\d{12}$|\d{14,15}$)) |
Description | Switch Credit Card match. International safe. |
Matches | 4903020000000008 |
Non-Matches | 4111111111111111 |
Title | JCB credit card |
Expression | (^(352)[8-9](\d{11}$|\d{12}$))|(^(35)[3-8](\d{12}$|\d{13}$)) |
Description | JCB Credit Card match. International safe. |
Matches | 3528000000000007 |
Non-Matches | 4508750000000009 |
Title | Dinner credit card |
Expression | (^(30)[0-5]\d{11}$)|(^(36)\d{12}$)|(^(38[0-8])\d{11}$) |
Description | Diners Credit Card match. International safe. |
Matches | 30000000000004 |
Non-Matches | 3528000000000007 |
Title | Cart Blanche Credit Card |
Expression | ^(389)[0-9]{11}$ |
Description | Cart Blanche Credit Card match. International safe. |
Matches | 38900000000007 |
Non-Matches | 30000000000004 |
Title | EnRoute Credit Card |
Expression | (^(2014)|^(2149))\d{11}$ |
Description | EnRoute Credit Card match. international safe. |
Matches | 201400000000009 |
Non-Matches | 38900000000007 |
Title | UK Debit Cards |
Expression | (^(5[0678])\d{11,18}$)|(^(6[^05])\d{11,18}$)|(^(601)[^1]\d{9,16}$)|(^(6011)\d{9,11}$)|(^(6011)\d{13,16}$)|(^(65)\d{11,13}$)|(^(65)\d{15,18}$)|(^(49030)[2-9](\d{10}$|\d{12,13}$))|(^(49033)[5-9](\d{10}$|\d{12,13}$))|(^(49110)[1-2](\d{10}$|\d{12,13}$))|(^(49117)[4-9](\d{10}$|\d{12,13}$))|(^(49118)[0-2](\d{10}$|\d{12,13}$))|(^(4936)(\d{12}$|\d{14,15}$)) |
Description | Matches any of Solo, Switch, or Maestro. International safe. Will not match on Discover, Visa or Mastercard. For example; Most recommendations for matching Visa are that they start with “4”. If outside the US, these are incomplete on will falsely ID most Switch cards as Visa. |
Matches | 6334500000000003 |
Non-Matches | 6011000000000004 |
Find more JavaScript tutorials, code snippets and samples here.