A naive credit card number validator

Of course, validating a credit card is not something we can do in a browser.
If we did, we could get valid card numbers that don’t match any account.

The purpose of this post is then not to validate the card with the issuer, but to help the user to make sure that he did not miss a digit when he typed the credit card number.

We can also do a best effort by trying to guess the card type by just looking at the six first digits of the card number.

type CardType = ‘MASTER_CARD’ | ‘VISA’ | ‘AMERICAN_EXPRESS’ |
‘JCB’ | ‘DINERS_CLUB_INTERNATIONAL’ | ‘DISCOVER’ | ‘unknown’
type MajorIndustryIdentifier =
‘ISO/TC 68’ | ‘Airlines’ | ‘Travel and entertainment and banking/financial’ |
‘Banking and financial’ | ‘Merchandising and banking/financial’ |
‘Petroleum’ | ‘Healthcare and telecommunications’ | ‘National assignment’ |
‘unknown’
type CreditCardGuessedInformation = {
complete: boolean, valid: boolean,
cardType?: CardType, majorIndustryIdentifier?: MajorIndustryIdentifier
}
type CreditCardNumber = string
export const guessInformationFrom:
(creditCardNumber: CreditCardNumber) => CreditCardGuessedInformation =
(creditCardNumber: CreditCardNumber) => ({
complete: getFullType(creditCardNumber) !== ‘unknown’,
cardType: getFullType(creditCardNumber) !== ‘unknown’ ?
getFullType(creditCardNumber) : getCreditCardType(creditCardNumber),
majorIndustryIdentifier: getMajorIndustryIdentifier(creditCardNumber),
valid: validate(creditCardNumber)
})
const getCreditCardType: (number: CreditCardNumber) => CardType = (number) =>
/^5[1-5]/.test(number) ? ‘MASTER_CARD’ :
/^4/.test(number) ? ‘VISA’ :
/^3[47]/.test(number) ? ‘AMERICAN_EXPRESS’ :
/^(?:2131|1800|35)/.test(number) ? ‘JCB’ :
/^3(?:0[0-5]|[68])/.test(number) ? ‘DINERS_CLUB_INTERNATIONAL’ :
/^6(?:011|5)/.test(number) ? ‘DISCOVER’ : ‘unknown’
const getFullType: (number: CreditCardNumber) => CardType = (number) =>
/^5[1-5][0-9]{14}$/.test(number) ? ‘MASTER_CARD’ :
/^4[0-9]{12}(?:[0-9]{3})?$/.test(number) ? ‘VISA’ :
/^3[47][0-9]{13}$/.test(number) ? ‘AMERICAN_EXPRESS’ :
/^(?:2131|1800|35\d{3})\d{11}$/.test(number) ? ‘JCB’ :
/^3(?:0[0-5]|[68][0-9])[0-9]{11}$/.test(number) ? ‘DINERS_CLUB_INTERNATIONAL’ :
/^6(?:011|5[0-9]{2})[0-9]{12}$/.test(number) ? ‘DISCOVER’ : ‘unknown’
const validate: (number: CreditCardNumber) => boolean = (number) => {
const checksum = parseInt(number.charAt(number.length 1))
const digits = number.substring(0, number.length 1);
const mod9 = [0, 1, 2, 3, 4, 4, 3, 2, 1, 0];
let sum = 0;
for (let i = 0; i < digits.length; i++) sum += parseInt(digits.substring(i, i + 1));
for (let i = digits.length 1; i >= 0; i -= 2) {
sum += mod9[parseInt(digits.substring(i, i + 1))]
}
return 10 sum % 10 === checksum
}
const getMajorIndustryIdentifier: (number: CreditCardNumber) => MajorIndustryIdentifier = (number) => {
switch (parseInt(number.charAt(0))) {
case 0:
return ‘ISO/TC 68’;
case 1:
case 2:
return ‘Airlines’;
case 3:
return ‘Travel and entertainment and banking/financial’;
case 4:
case 5:
return ‘Banking and financial’;
case 6:
return ‘Merchandising and banking/financial’;
case 7:
return ‘Petroleum’;
case 8:
return ‘Healthcare and telecommunications’;
case 9:
return ‘National assignment’;
default:
return ‘unknown’
}
}
For example, guessInformationFrom(“4444444444444448”) will return this following data :
{ “complete”: true, “valid”: true, “cardType”: “VISA”, “majorIndustryIdentifier” : “Banking and financial”}
For example, guessInformationFrom(“535697”) will return that :
{ “complete”: false, “valid”: false”cardType”: “MASTER_CARD”, “majorIndustryIdentifier” : “Banking and financial”}
“valid” will only be true if the Luhn algorithm is satisfied using the input.
Don’t hesitate if you want to write your own dedicated validator, you can just start from that one or simply take a look at the algorithm.
Enjoy and see you in 2020.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.