Skip to content

Instantly share code, notes, and snippets.

@lyquix-owner
Last active March 31, 2020 20:15
Show Gist options
  • Save lyquix-owner/08db5d81fbae3138eb1c9545dd871275 to your computer and use it in GitHub Desktop.
Save lyquix-owner/08db5d81fbae3138eb1c9545dd871275 to your computer and use it in GitHub Desktop.
JavaScript credit card validator - checks length, prefix numbers, and checksum number
function creditCardValidator(cc) {
/**
* Credit card validator
* Based on: https://www.freeformatter.com/credit-card-number-generator-validator.html
* and this: https://en.wikipedia.org/wiki/Payment_card_number
*/
// Remove all non-numeric characters
cc = cc.toString().replace(/\D/g,'');
// Structure of response object
var res = {
cc: cc,
valid: false,
type: null
};
// Validation rules
var rules = {
'American Express': {
prefix: [34, 37],
length: [15]
},
'Diners Club - Carte Blanche': {
prefix: [[300, 305]],
length: [14]
},
'Diners Club - International': {
prefix: [36],
length: [14]
},
'Diners Club - USA & Canada': {
prefix: [54],
length: [16]
},
'Discover': {
prefix: [6011, [622126, 622925], [644, 649], 65],
length: [16, 19]
},
'InstaPayment': {
prefix: [[637, 639]],
length: [16]
},
'JCB': {
prefix: [[3528, 3589]],
length: [16, 19]
},
'Maestro': {
prefix: [5018, 5020, 5038, 5893, 6304, 6759, 6761, 6762, 6763],
length: [16, 19]
},
'MasterCard': {
prefix: [[51, 55], [222100, 272099]],
length: [16]
},
'Visa': {
prefix: [4],
length: [13, 16, 19]
},
'Visa Electron': {
prefix: [4026, 417500, 4508, 4844, 4913, 4917],
length: [16]
}
}
// Validate against rules
var validPrefix = false;
var validLength = false;
Object.keys(rules).forEach(function(type) {
if(validPrefix && validLength) return;
// Check prefixes
var prefix = rules[type].prefix;
validPrefix = false;
prefix.forEach(function(p) {
// If prefix is an array check lower and upper ends
if(p instanceof Array) {
if(p[0] <= cc.substr(0, p[0].toString().length) && p[1] >= cc.substr(0, p[1].toString().length)) validPrefix = true;
}
// If prefix is a number
else if(typeof p == 'number') {
if(p == cc.substr(0, p.toString().length)) validPrefix = true;
}
});
if(!validPrefix) return;
// Check length
var length = rules[type].length;
validLength = false;
length.forEach(function(l) {
// If length is an array check lower and upper ends
if(l instanceof Array) {
if(l[0] <= cc.length && l[1] >= cc.length) validLength = true;
}
// If length is a number
else if(typeof l == 'number') {
if(l == cc.length) validLength = true;
}
});
if(validPrefix && validLength) res.type = type;
});
if(!(validPrefix && validLength)) return res;
// Validate checksum using Luhn formula
var ccCheckDigit = cc.substr(cc.length - 1); // Extract last digit
var ccDataDigits = cc.substr(0, cc.length - 1).split('').map((d) => parseInt(d)).reverse(); // Convert digits to array, and reverse order
ccDataDigits = ccDataDigits.map(function(d, i){
// Odd digits: multiply by 2, then subtract 9 if > 9
if(i % 2 === 0) {
d = d * 2;
return d > 9 ? d - 9 : d;
}
// Keep even digits as they are
else return d;
});
// Add all digits
ccDataDigits = ccDataDigits.reduce((acc, d) => acc + d);
// Calculate check digit
ccDataDigits = ccDataDigits % 10;
if(ccDataDigits !== 0) ccDataDigits = 10 - ccDataDigits;
if(ccDataDigits == ccCheckDigit) res.valid = true;
return res;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment