Last active
March 31, 2020 20:15
-
-
Save lyquix-owner/08db5d81fbae3138eb1c9545dd871275 to your computer and use it in GitHub Desktop.
JavaScript credit card validator - checks length, prefix numbers, and checksum number
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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