This is a collection of JavaScript and jQuery snippets to validate credit card details without requiring any library.
JavaScript
/*
* Display error message based on current element's data attributes
*/
function cgToggleError(element, status) {
var errorMessage = $(element).data('validation-error-msg'),
errorContainer = $(element).data('validation-error-msg-container');
$(element).removeClass().addClass(status);
if (status === 'valid') {
$(errorContainer).html(errorMessage).hide();
} else if (status === 'invalid') {
$(errorContainer).html(errorMessage).show();
}
}
/*
* Format a date as MM/YY
*/
function cgFormatExpiryDate(e) {
var inputChar = String.fromCharCode(event.keyCode);
var code = event.keyCode;
var allowedKeys = [8];
if (allowedKeys.indexOf(code) !== -1) {
return;
}
event.target.value = event.target.value.replace(
/^([1-9]\/|[2-9])$/g, '0$1/' // 3 > 03/
).replace(
/^(0[1-9]|1[0-2])$/g, '$1/' // 11 > 11/
).replace(
/^([0-1])([3-9])$/g, '0$1/$2' // 13 > 01/3
).replace(
/^(0?[1-9]|1[0-2])([0-9]{2})$/g, '$1/$2' // 141 > 01/41
).replace(
/^([0]+)\/|[0]+$/g, '0' // 0/ > 0 and 00 > 0
).replace(
/[^\d\/]|^[\/]*$/g, '' // To allow only digits and `/`
).replace(
/\/\//g, '/' // Prevent entering more than 1 `/`
);
}
/*
* Check if date element is valid and add a visual hint
*/
function cgDateValidate(whatDate) {
var currVal = whatDate;
if (currVal === '') {
return false;
}
var rxDatePattern = /^(\d{1,2})(\/|-)(\d{1,2})(\/|-)(\d{4})$/;
var dtArray = currVal.match(rxDatePattern);
if (dtArray == null) {
return false;
}
// Check for dd/mm/yyyy format
var dtDay = dtArray[1],
dtMonth= dtArray[3],
dtYear = dtArray[5];
if (dtMonth < 1 || dtMonth > 12) {
return false;
} else if (dtDay < 1 || dtDay> 31) {
return false;
} else if ((dtMonth==4 || dtMonth==6 || dtMonth==9 || dtMonth==11) && dtDay ==31) {
return false;
} else if (dtMonth == 2) {
var isleap = (dtYear % 4 == 0 && (dtYear % 100 != 0 || dtYear % 400 == 0));
if (dtDay> 29 || (dtDay ==29 && !isleap)) {
return false;
}
}
return true;
}
/*
* Credit card expiry date formatting (real-time)
*/
$(document).on('keyup blur', '.cardExpiry', function(event) {
var currentDate = new Date();
var currentMonth = ("0" + (currentDate.getMonth() + 1)).slice(-2);
var currentYear = String(currentDate.getFullYear()).slice(-2);
var cardExpiryArray = $('.cardExpiry').val().split('/');
var userMonth = cardExpiryArray[0],
userYear = cardExpiryArray[1];
if ($('.cardExpiry').val().length !== 5) {
cgToggleError($(this), 'invalid');
} else if (userYear < currentYear) {
cgToggleError($(this), 'invalid');
} else if (userYear <= currentYear && userMonth < currentMonth) { cgToggleError($(this), 'invalid'); } else if (userMonth > 12) {
cgToggleError($(this), 'invalid');
} else {
cgToggleError($(this), 'valid');
}
cgFormatExpiryDate(event);
});
/*
* Credit card CVV disallow letters (real-time)
*/
$(document).on('keyup', '.cardCVV', function(event) {
event.target.value = event.target.value.replace(/[^\d\/]|^[\/]*$/g, '');
});
/*
* Credit card CVV length check
*/
$(document).on('blur', '.cardCVV', function(e) {
if ($('#cardCVV').val().length < 3) {
cgToggleError($(this), 'invalid');
}
});
/*
* Credit card validation
*/
function cgCheckLuhn(input) {
var sum = 0,
numdigits = input.length;
var parity = numdigits % 2;
for (var i=0; i < numdigits; i++) { var digit = parseInt(input.charAt(i)); if (i % 2 == parity) { digit *= 2; } if (digit > 9) {
digit -= 9;
}
sum += digit;
}
return (sum % 10) == 0;
}
function cgDetectCard(input) {
var typeTest = 'u',
ltest1 = 16,
ltest2 = 16;
ltest3 = 'none';
if (/^4/.test(input)) {
typeTest = 'v';
ltest1 = 13;
ltest3 = 'VISA';
} else if (/^5[1-5]/.test(input)) {
typeTest = 'm';
ltest3 = 'MASTERCARD';
} else if (/^6(011|4[4-9]|5)/.test(input)) {
typeTest = 'd';
ltest3 = 'VISADEBIT';
}
return [typeTest,ltest1,ltest2,ltest3];
}
/*
* Credit card Luhn validation (real-time)
*/
$(document).on('keyup', '.cardNumber', function() {
var val = this.value,
val = val.replace(/[^0-9]/g, ''),
detected = cgDetectCard(val),
errorClass = 'invalid',
luhnCheck = cgCheckLuhn(val),
valueCheck = (val.length == detected[1] || val.length == detected[2]);
if ($('body').hasClass('inline-ab')) {
cgToggleError($(this), 'invalid');
}
if (luhnCheck && valueCheck) {
errorClass = 'valid';
$('#cardType').val(detected[3]);
} else if (valueCheck || val.length > detected[2]) {
errorClass = 'invalid';
}
if ($('body').hasClass('inline-ab')) {
cgToggleError($(this), errorClass);
cgToggleError($(this), 'cc ' + detected[0] + ' ' + errorClass);
}
$(this).addClass('cc ' + detected[0] + ' ' + errorClass);
});
/*
* Credit card digit formatting (real-time)
*/
$(document).on('keypress change blur', '.cardNumber', function() {
$(this).val(function(index, value) {
return value.replace(/[^a-z0-9]+/gi, '').replace(/(.{4})/g, '$1 ').trim();
});
});
$(document).on('copy cut paste', '.cardNumber', function() {
setTimeout(function() {
$('.cardNumber').trigger('change');
});
});
HTML
<div class='credit-card-validation'>
<p>
<label>Card Number</label>
<input type="tel" name="cardNumber" class="cardNumber" maxlength="16" placeholder="0000 0000 0000 0000" data-validation-type="custom" data-validation-error-msg="Please enter a valid card number" data-validation-error-msg-container="#cardnumber-error-dialog">
<div id="cardnumber-error-dialog" class="field-error"></div>
</p>
<p>
<label>Expiry Date</label>
<input type="text" name="cardExpiry" maxlength="5" class="cardExpiry" placeholder="mm/yy" data-validation-type="alphanumeric" data-validation-error-msg="Please enter a valid card expiry" data-validation-error-msg-container="#cardexpiry-error-dialog">
<div id="cardexpiry-error-dialog" class="field-error"></div>
</p>
<p>
<label>CVV Code</label>
<input type="text" name="cardCVV" maxlength="3" class="cardCVV" data-validation-type="numeric" data-validation-error-msg="Please enter a valid CVV number" data-validation-error-msg-container="#cardcvv-error-dialog">
<div id="cardcvv-error-dialog" class="field-error"></div>
</p>
</div>
Note that this validation snippet comes from a custom solution implemented for a custom project. You will need CSS styling for all classes mentioned above. Feel free to customise it as you see fit and, if you wish, share the code.
Find more JavaScript tutorials, code snippets and samples here or more jQuery tutorials, code snippets and samples here.
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, this are incomplete on will falsly ID most Switch cards as Visa. |
Matches | 6334500000000003 |
Non-Matches | 6011000000000004 |