Skip to content

Instantly share code, notes, and snippets.

@nijikokun
Last active April 5, 2017 13:31
Show Gist options
  • Save nijikokun/8378371 to your computer and use it in GitHub Desktop.
Save nijikokun/8378371 to your computer and use it in GitHub Desktop.
Credit Card Validation & Credit Card Type Check & Credit Card Cleaner & Credit Card Security Code Validator. Framework Agnostic.

credit-card.js

Allows you to easily validate, clean, check type of card based on number, and verify the security code. It's small, has the fastest luhn implementation, works for all major cards without complex expressions, framework agnostic, and doesn't cost your company a small fortune to use like other libraries.

Easily hook it up to any framework, any code, easy to port.

Usage

Type

// Returns an Object containing the company, MII, and IIN details or Boolean(false).
Card('5100 - 0000 - 0000 - 0040').type()

Validation

Card('5100 - 0000 - 0000 - 0040').valid()

Validate CSC for a Credit Card Number

Card('5100 - 0000 - 0000 - 0040').validate_security_number('434');

Validate Expiration Date

Card('5100 - 0000 - 0000 - 0040').validate_expiration('06', '2023');

Clean

Card('5100 - 0000 - 0000 - 0040').clean()

Format (eg. XXXX XXXX XXXX XXXX)

Card('5100 - 0000 - 0000 - 0040').format()

Truncate (Pan Truncation)

Card('5100 - 0000 - 0000 - 0040').truncate()

Card Number Length

Card('5100 - 0000 - 0000 - 0040').length()
var Card = function (value) {
value = String(value).replace(/[^0-9]/g, '');
var system = {
type: function () {
var data = false;
for (var company in Card.iin) {
if (value.match(Card.iin[company].identifier)) {
data = {
company: company,
mii: Card.mii[+value.charAt(0)],
iin: Card.iin[company]
};
break;
}
}
return data;
},
valid: function () {
var type = system.type();
return (
type && type.inactive != true &&
type.iin.lengths.indexOf(value.length) != -1 &&
Card.luhn(value)
);
},
validate_security_number: function (csc, type) {
var type = system.type();
csc = String(csc).replace(/[^0-9]/g, '');
return (
type &&
(type.iin.csc || 3) == csc.length
);
},
validate_expiration: function (month, year) {
month = +month; year = +year;
if (!(month || year))
return false;
var date = new Date()
, now = +date;
date.setFullYear(year);
date.setMonth(--month);
return +date >= now;
},
format: function () {
var index = 0
, pattern = Card.iin["American Express"].prefix.test(value)
? 'XXXX XXXXXX XXXXX'
: 'XXXX XXXX XXXX XXXX';
return pattern.replace(/X/g, function (char) {
return value.charAt(index++) || '';
}).trim();
},
truncate: function () {
var length = value.length - 4
, pattern = system.format(value);
return pattern.replace(/\d/g, function (char) {
return length-- > 0
? 'X'
: char;
});
},
clean: function () {
return value;
},
length: function () {
return value.length;
}
};
return system;
};
Card.luhn = function (value) {
var ca, sum = 0, mul = 0;
var len = value.length;
while (len--) {
ca = parseInt(value.charAt(len),10) << mul;
sum += ca - (ca>9)*9;
mul ^= 1;
};
return (sum%10 === 0) && (sum > 0);
};
/**
* Per-Company IIN Details ([Wikipedia](https://en.wikipedia.org/wiki/Bank_card_number#Issuer_identification_number_.28IIN.29))
* @Type {Object}
*/
Card.iin = {
// Inactive Companies
"Australian Bank Card": { inactive: true, identifier: /^(5610|560221|560222|560223|560224|560225)/, lengths: [16] },
"Diner's Club enRoute": { inactive: true, identifier: /^(2014|2149)/, lengths: [15] },
"Solo": { inactive: true, identifier: /^(6334|6767)/, lengths: [16] },
"Switch": { inactive: true, identifier: /^(4903|4905|4911|4936|564182|633110|6333|6759)/, lengths: [16,18,19] },
// Active Companies
"American Express": { identifier: /^(34|37)/, lengths: [13, 15], csc: 4 },
"MasterCard": { identifier: /^5[0-5]/, lengths: [16] },
"Visa Electron": { identifier: /^(4026|417500|4508|4844|491(3|7))/, lengths: [16] },
"Visa": { identifier: /^4/, lengths: [13, 16] },
"Laser": { identifier: /^(6334|6767)/, lengths: [16,17,18,19] },
"Discover": { identifier: /^6(?:011|22[1-9]|4[4-9]|5)/, lengths: [16] },
"Diner's Club": { identifier: /^(36|30[0-5]|54|55|62|88)/, lengths: [14, 16] },
"JCB": { identifier: /^35(2[89]|[3-8][0-9])/, lengths: [16] },
"Maestro": { identifier: /^(5018|5020|5038|6304|6759|676[1-3])/, lengths: [12, 13, 14, 15, 16, 18, 19] }
};
/**
* Major Industry Identifier ([Wikipedia](https://en.wikipedia.org/wiki/Bank_card_number#Major_Industry_Identifier_.28MII.29))
* @type {Array}
*/
Card.mii = [
"ISO/TC 68 and other future industry assignments",
"Airlines",
"Airlines and other future industry assignments",
"Travel and entertainment and banking/financial",
"Banking and financial",
"Banking and financial",
"Merchandising and banking/financial",
"Petroleum and other future industry assignments",
"Healthcare, telecommunications and other future industry assignments",
"National assignment"
];
var tests = [
{ cards: [ 4444444444444448,4444424444444440,4444414444444441,4012888888881881,4055011111111111,4110144110144115,4114360123456785,4061724061724061 ], type: 'Visa' },
{ cards: [ 5500005555555559,5555555555555557,5454545454545454,5555515555555551,5405222222222226,5478050000000007,5111005111051128,5112345112345114 ], type: 'MasterCard' },
{ cards: [ 371449635398431,343434343434343,371144371144376,341134113411347 ], type: 'American Express' },
{ cards: [ 6011016011016011,6011000990139424,6011000000000004,6011000995500000,6500000000000002 ], type: 'Discover' },
{ cards: [ 36438936438936 ], type: "Diner's Club" },
{ cards: [ 3566003566003566,3528000000000007 ], type: 'JCB' },
];
var failed = 0;
for (var company in tests) {
for (var index in tests[company].cards) {
var card = Card(tests[company].cards[index]);
if (card.type().company != tests[company].type) console.error('Invalid company type found on:', tests[company].type, 'card number', tests[company].cards[index], 'gave', card.type().company), failed += 1;
if (!card.valid()) console.error('Invalid card found on:', tests[company].type, 'card number', tests[company].cards[index]), failed += 1;
}
}
if (failed == 0) console.info('All Tests Passed :)');
else console.info(failed, 'tests failed. :(');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment