In this digital era doing payment on a click has become a child’s play.
Credit or Debit card payments are as common as Signup forms. As an engineer it is important to validate each card for smooth payment.
Even before sending the credit card details to the server for transaction we should check if it is valid. We will use a simple algorithm in javascript which will just validate the card to ensure that it is in a correct format.
Identifying the format
Like any other magnetic strip card, credit or debit card numbers has an identifier format defined under ISO/IEC 7812
Identifiers are usually 13 – 19 digits long and are used for any number of purposes.
The identifier consists of three parts.
- IIN (Issuer identification number):- A six digit identifier number of the institution that issued this number.(For banks it usually starts with 4 or 5) that is why most of the credit cards begins with it.
- Account Number:- An identifier between 6 to 12 numbers long.
- Check Digit:- A single digit to validate the sum of the identifier.
Validating credit card in javascript with Luhn’s algorithm.
Hans peter luhn, a scientist at IBM developed this algorithm to protect against unintentional mistakes in numeric identifiers.
It is a very simple algorithm.
- Start from the last digit of the number.
- Get a double of every alternate digit, if the double value is greater than 9 then take its remainder and add it to 1.
- Create a sum of this doubled values, remainders and each digit then divide it by 10.
- If it is divisible then it is valid.
Following is the implementation of luhn’s algorithm for validation of credit card in javascript.
const validateCardNumber = number => { //Check if the number contains only numeric value //and is of between 13 to 19 digits const regex = new RegExp("^[0-9]{13,19}$"); if (!regex.test(number)){ return false; } return luhnCheck(number); } const luhnCheck = val => { let checksum = 0; // running checksum total let j = 1; // takes value of 1 or 2 // Process each digit one by one starting from the last for (let i = val.length - 1; i >= 0; i--) { let calc = 0; // Extract the next digit and multiply by 1 or 2 on alternative digits. calc = Number(val.charAt(i)) * j; // If the result is in two digits add 1 to the checksum total if (calc > 9) { checksum = checksum + 1; calc = calc - 10; } // Add the units element to the checksum total checksum = checksum + calc; // Switch the value of j if (j == 1) { j = 2; } else { j = 1; } } //Check if it is divisible by 10 or not. return (checksum % 10) == 0; }
Input: console.log(validateCardNumber("0123765443210190")); Output: true
Remember this algorithm only validates the format. You will still need to check on the server if credit card with this number exists or not.
Determining the Credit Card Issuer.
Each company which issues the card has a unique identification through which we can determine that this credit card belongs to this particular organisation.
For example this is the specific format of each organization.
Company | Format |
---|---|
American Express | 3493 7003 8656 069 |
Carte Blanche | 3000 0000 0000 04 |
Discover | 6011 0000 0000 0004 |
Diners Club | 3852 0000 0232 37 |
enRoute | 2014 0000 0000 009 |
JCB | 3530 111333300000 |
MasterCard | 5500 0000 0000 0004 |
Solo | 6334 0000 0000 0004 |
Switch | 4903 0100 0000 0009 |
Visa | 4111 1111 1111 1111 |
Laser | 6304 1000 0000 0008 |
Now by understanding this format we will create a function for validation of credit card in javascript and determine its type.
const checkCreditCard = cardnumber => { //Error messages const ccErrors = []; ccErrors [0] = "Unknown card type"; ccErrors [1] = "No card number provided"; ccErrors [2] = "Credit card number is in invalid format"; ccErrors [3] = "Credit card number is invalid"; ccErrors [4] = "Credit card number has an inappropriate number of digits"; ccErrors [5] = "Warning! This credit card number is associated with a scam attempt"; //Response format const response = (success, message = null, type = null) => ({ message, success, type }); // Define the cards we support. You may add additional card types as follows. // Name: As in the selection box of the form - must be same as user's // Length: List of possible valid lengths of the card number for the card // prefixes: List of possible prefixes for the card // checkdigit: Boolean to say whether there is a check digit const cards = []; cards [0] = {name: "Visa", length: "13,16", prefixes: "4", checkdigit: true}; cards [1] = {name: "MasterCard", length: "16", prefixes: "51,52,53,54,55", checkdigit: true}; cards [2] = {name: "DinersClub", length: "14,16", prefixes: "36,38,54,55", checkdigit: true}; cards [3] = {name: "CarteBlanche", length: "14", prefixes: "300,301,302,303,304,305", checkdigit: true}; cards [4] = {name: "AmEx", length: "15", prefixes: "34,37", checkdigit: true}; cards [5] = {name: "Discover", length: "16", prefixes: "6011,622,64,65", checkdigit: true}; cards [6] = {name: "JCB", length: "16", prefixes: "35", checkdigit: true}; cards [7] = {name: "enRoute", length: "15", prefixes: "2014,2149", checkdigit: true}; cards [8] = {name: "Solo", length: "16,18,19", prefixes: "6334,6767", checkdigit: true}; cards [9] = {name: "Switch", length: "16,18,19", prefixes: "4903,4905,4911,4936,564182,633110,6333,6759", checkdigit: true}; cards [10] = {name: "Maestro", length: "12,13,14,15,16,18,19", prefixes: "5018,5020,5038,6304,6759,6761,6762,6763", checkdigit: true}; cards [11] = {name: "VisaElectron", length: "16", prefixes: "4026,417500,4508,4844,4913,4917", checkdigit: true}; cards [12] = {name: "LaserCard", length: "16,17,18,19", prefixes: "6304,6706,6771,6709", checkdigit: true}; // Ensure that the user has provided a credit card number if (cardnumber.length == 0) { return response(false, ccErrors[1]); } // Now remove any spaces from the credit card number // Update this if there are any other special characters like - cardnumber = cardnumber.replace (/\s/g, ""); // Validate the format of the credit card // luhn's algorithm if(!validateCardNumber(cardnumber)){ return response(false, ccErrors[2]); } // Check it's not a spam number if (cardnumber == '5490997771092064') { return response(false, ccErrors[5]); } // The following are the card-specific checks we undertake. let lengthValid = false; let prefixValid = false; let cardCompany = ""; // Check if card belongs to any organization for(let i = 0; i < cards.length; i++){ const prefix = cards[i].prefixes.split(","); for (let j = 0; j < prefix.length; j++) { const exp = new RegExp ("^" + prefix[j]); if (exp.test (cardnumber)) { prefixValid = true; } } if(prefixValid){ const lengths = cards[i].length.split(","); // Now see if its of valid length; for (let j=0; j < lengths.length; j++) { if (cardnumber.length == lengths[j]) { lengthValid = true; } } } if(lengthValid && prefixValid){ cardCompany = cards[i].name; return response(true, null, cardCompany); } } // If it isn't a valid prefix there's no point at looking at the length if (!prefixValid) { return response(false, ccErrors[3]); } // See if all is OK by seeing if the length was valid if (!lengthValid) { return response(false, ccErrors[4]); }; // The credit card is in the required format. return response(true, null, cardCompany); }
Input: console.log(checkCreditCard("4111 1111 1111 1111")); console.log(checkCreditCard("3400 0000 0000 009")); Output: /* Object { message: null, success: true, type: "Visa" } Object { message: null, success: true, type: "AmEx" } */