Our final validation method checks whether a credit card number could be a valid card number. Note that I say "could be" rather than "is" — just because the number is valid, doesn't mean that the card has been allocated or that it has not been canceled, if it was allocated. Only server-side processing can possibly validate a card number. However, what we can do here is check that the user hasn't made an accidental mistake so that we can get them to rectify any mistakes before we attempt server-side checks.As we'll see shortly, validating a credit card is much more complex than any of the validation methods we have created so far. There are three checks we can perform client-side:

- Check that only numbers or spaces are given in the credit card number — not letters or other characters.
- Check that, for the given card type, the number of digits given is valid and the prefix to the number is valid.
- Use the Luhn formula to check the validity of the entered credit card number. This is a special algorithm that can be applied to most credit card numbers to check that the number would be valid.

Validate.prototype.isValidCreditCardNumber = function(cardNumber, cardType){ var isValid = false; var ccCheckRegExp = /[^\d ]/; isValid = !ccCheckRegExp.test(cardNumber);We'll take this method step by step. Note first of all, that the method takes two parameters — the card number and the card type (if (isValid)

{ var cardNumbersOnly = cardNumber.replace(/ /g,""); var cardNumberLength = cardNumbersOnly.length; var lengthIsValid = false; var prefixIsValid = false; var prefixRegExp;switch(cardType)

{ case "mastercard": lengthIsValid = (cardNumberLength == 16); prefixRegExp = /^5[1-5]/; break;case "visa":

lengthIsValid = (cardNumberLength == 16 cardNumberLength == 13); prefixRegExp = /^4/; break;case "amex":

lengthIsValid = (cardNumberLength == 15); prefixRegExp = /^3(4 7)/; break;default:

prefixRegExp = /^$/; alert("Card type not found"); }prefixIsValid = prefixRegExp.test(cardNumbersOnly);

isValid = prefixIsValid && lengthIsValid; }if (isValid)

{ var numberProduct; var numberProductDigitIndex; var checkSumTotal = 0;for (digitCounter = cardNumberLength - 1;

digitCounter >= 0; digitCounter--) { checkSumTotal += parseInt (cardNumbersOnly.charAt(digitCounter)); digitCounter--; numberProduct = String((cardNumbersOnly.charAt(digitCounter) * 2)); for (var productDigitCounter = 0; productDigitCounter < numberProduct.length; productDigitCounter++) { checkSumTotal += parseInt(numberProduct.charAt(productDigitCounter)); } }isValid = (checkSumTotal % 10 == 0);

}return isValid;

}

`mastercard`

, `amex`

, and `visa`

are valid card types that we will cater for here, though the method could be extended for other card types).The first part of the method checks that only numbers or spaces have been entered:var isValid = false; var ccCheckRegExp = /[^\d ]/; isValid = !ccCheckRegExp.test(cardNumber);The regular expression

`/[^\d ]/`

will match invalid characters (any character that is not a digit or a space). When we test the card number against the regular expression on the third line, as in previous methods, we use the ! character to reverse the logic, so that `isValid`

is set to false if invalid characters are found in the card number.The next part of the method checks that the card has a valid prefix, that is it starts with the correct numbers for that card type, and contains the correct number of digits, again specific to a card type. The prefixes and lengths for some commonly available cards are shown below:Card Type | Prefix | Number of Digits |
---|---|---|

Visa | 4 | 13,16 |

Mastercard | 51-55 | 16 |

American Express | 34,37 | 15 |

if (isValid) { var cardNumbersOnly = cardNumber.replace(/ /g,""); var cardNumberLength = cardNumbersOnly.length; var lengthIsValid = false; var prefixIsValid = false; var prefixRegExp;The

`if`

statement checks whether the previous validation (that the card number only contained numbers or spaces) found the number to be valid, and only proceeds with the next check if it did. Then, using a regular expression (a space between the `/`

regular expression delimiters, together with the global flag `g`

) and the String object's `replace()`

method, we strip out the spaces.Variable `cardNumberLength`

is set to the length of the string (the number of digits in the string). The variables `lengthIsValid`

and `prefixIsValid`

will store Boolean values indicating the validity of the length and prefix checks that we do next.We now need to check the `cardType`

parameter of the method and, from that, decide what the prefix to the card number should be and how long the number should be.The `switch`

statement checks the card type to see which, if any, of the known card types is found:switch(cardType) { case "mastercard": lengthIsValid = (cardNumberLength == 16); prefixRegExp = /^5[1-5]/; break;In eachcase "visa":

lengthIsValid = (cardNumberLength == 16 cardNumberLength == 13); prefixRegExp = /^4/; break;case "amex":

lengthIsValid = (cardNumberLength == 15); prefixRegExp = /^3(4 7)/; break;default:

prefixRegExp = /^$/; alert("Card type not found"); }

`case`

statement, we set `lengthIsValid`

to the Boolean returned by the logical expression that checks for the correct card length. Then we create a regular expression that will check for the correct prefix for that card. In the `default`

case, we create a regular expression that matches nothing, and warn the customer that their card type hasn't been found. If we want to allow different cards, for example diners club, then our `switch`

statement simply needs an extra case adding to match the new type of card's parameters, that is size and start digits.We next check the prefix using the regular expression set in the relevant `case`

statement, and then set `isValid`

to the results of the logical addition `prefixIsValid`

`lengthIsValid`

, which will be true only if both these values are true:prefixIsValid = prefixRegExp.test(cardNumbersOnly); isValid = prefixIsValid && lengthIsValid;OK, that's the two easy checks done. Now we have the part of the method that checks the card number using the

- Start with the second digit from last in the card number. Moving backwards towards the first digit in the number, double each alternate digit.

In our example, we would double the bold numbers in**4**2**2**1**3**4**5**6**1**2**4**3**1**2**3**7 to give us:

`(4x2), (2x2), (3x2), (5x2), (1x2), (4x2), (1x2), (3x2)`

which is:

`8, 4, 6, 10, 2, 8, 2, 6`

- Take the results of the doubling, add each of the individual digits in each doubled number together, and then add to the running total.

In our example we have:

`8 + 4 + 6 + (1 + 0) + 2 + 8 + 2 + 6 = 37`

- Add all the non-doubled digits from the credit card number together.

In our example we have:

`2 + 1 + 4 + 6 + 2 + 3 + 2 + 7 = 27`

- Add the values calculated in step 2 and step 3 together.

In our example:

`37 + 27 = 64`

- Take the value calculated in step 4 and calculate the remainder when it is divided by 10. If the remainder is zero, then it's a valid number, otherwise it's invalid.

In our example:

`64 / 10 = 6`

with remainder 4.

So, our example number is not a valid card number, as the remainder is not 0.

`isValid`

is `true`

(that is, if all other checks so far proved satisfactory). Then, after our variable declarations, we have the main for loop that will go through the credit card number a digit at a time starting with the last digit and moving to the first.if (isValid) { var numberProduct; var numberProductDigitIndex; var checkSumTotal = 0;In thisfor (digitCounter = cardNumberLength - 1;

digitCounter > 0; digitCounter--) {

`for`

loop we do a number of things. Firstly we add a digit's value to the running total, `checkSumTotal`

.checkSumTotal += parseInt (cardNumbersOnly.charAt(digitCounter));Then we move to the digit that is one nearer the start of the card number string. We multiply this next digit by 2.

digitCounter--; numberProduct = String((cardNumbersOnly.charAt(digitCounter) * 2));We take the digits forming the results of the product calculation and in the inner

`for`

loop we add these digits to the running total.for (var productDigitCounter = 0; productDigitCounter < numberProduct.length; productDigitCounter++) { checkSumTotal += parseInt(numberProduct.charAt(productDigitCounter)); }Our outer

`for`

loop continues by moving to the next digit to the left, and iterates until we reach the first digit in the credit card number string.If we think back to the explanation of the Luhn formula, our approach is out of step in that we are not doing step 1, then step 2, and so on, but instead are merging steps 1-4 and processing the number on a digit by digit basis, keeping a running total. It amounts to exactly the same thing, but reduces the number of loops required.Let's summarize the steps in our outer and inner `for`

loops:- Extract the character whose position is specified by
`digitCounter`

, from the string in`cardNumbersOnly`

. - Add this number to our running total, which is kept in variable
`checkSumTotal`

. - Decrement the character counter,
`digitCounter`

. This now refers to the next digit to the left in our string`cardNumbersOnly`

. - Extract the next digit, convert it to a number, and then double it. This is all done on one line and this product is stored in variable
`numberProduct`

. - Convert the product calculated in step 4 to a string.
- Loop through each digit in the product string, convert it to an integer, and add to the running total. This is our inner
`for`

loop. For example, if the character extracted in step 4 was 8, its product would be 16, and so we'd add 1 and then 6 to our running total. - Decrement the
`digitCounter`

and move to step 1.

Character Extracted | What happens to it | What happens to running total | Running total Value |
---|---|---|---|

7 | Added to running total | Running total (0) + character extracted (7) | 7 |

8 | Doubled (16), then each digit in the product added to running total | Running total (7) + first digit in product (1) + second digit in product (6) | 14 |

2 | Added to running total | Running total (14) + character extracted (2) | 16 |

1 | Doubled (2), then each digit in the product added to running total | Running total (16) + first digit in product (2) | 18 |

3 | Added to running total | Running total (18) + character extracted (3) | 21 |

4 | Doubled (8), then each digit in the product added to running total | Running total (21) + first digit in product (8) | 29 |

2 | Added to running total | Running total (29) + character extracted (2) | 31 |

1 | Doubled (2), then each digit in the product added to running total | Running total (31) + first digit in product (2) | 33 |

6 | Added to running total | Running total (33) + character extracted (6) | 39 |

5 | Doubled (10), then each digit in the product added to running total | Running total (39) + first digit in product (1) + second digit in product(0) | 40 |

4 | Added to running total | Running total (40) + character extracted (4) | 44 |

3 | Doubled (6), then each digit in the product added to running total | Running total (44) + first digit in product (6) | 50 |

1 | Added to running total | Running total (50) + character extracted (1) | 51 |

2 | Doubled (4), then each digit in the product added to running total | Running total (51) + first digit in product (4) | 55 |

2 | Added to running total | Running total (55) + character extracted (2) | 57 |

4 | Doubled (8), then each digit in the product added to running total | Running total (57) + first digit in product (8) | 65 |

`isValid`

to the result of the Boolean expression comparing the remainder to zero — if `true`

it's valid, if `false`

it's invalid. In our example, we have 65, the modulus 10 value of which is 5, so the example number is invalid — you didn't really think I'd give my credit card number out now did you?