by Jim Karabatsos - GUI Computing
If your application needs to input credit card numbers, then you may want to have your input validated to ensure that the operator is alerted when an invalid credit card number is entered.
All credit cards use a simple but effective checksum algorithm that ensures that data is input correctly. This scheme is not foolproof (not by a long shot) but it does protect against the most common types of errors that are encountered during entry of these numbers. In particular, it will correctly detect a wrong digit, or a transposed pair of digits. If the checksum is correct, you have a 90% degree of confidence that the data was entered correctly.
Not all credit card numbers are the same length. Most cards use 16 digits but some use more or less; for example, American Express uses 15 digits. The algorithm caters for different-length card numbers.
Let's look at the code for a validation function - which is, as usual, available for download. This function accepts a credit card number as it might be entered into an edit control, so it uses a string parameter. It returns a boolean result, True for a valid card number and False otherwise.
Public Function IsValidCreditCardNumber(ByVal sCardNo As String) As Boolean Const MAX_DIGITS = 20 ' actually don't know any card using more than 16 digits Dim anDigits(1 To MAX_DIGITS) As Byte Dim nDigits As Long Dim ofsCurrentDigit As Long Dim ofsCurrentCharacter As Long Dim CurrentCharacter As String Dim Multiplier As Long Dim CheckSum As Long Dim DigitValue As Long Dim ValidDigits As String If Len(Trim$(sCardNo)) < 1 Then Result = False GoTo Exit_Point End If ValidDigits = "0123456789" For ofsCurrentCharacter = 1 To Len(sCardNo) CurrentCharacter = Mid$(sCardNo, ofsCurrentCharacter, 1) If InStr(1, ValidDigits, CurrentCharacter, vbBinaryCompare) Then nDigits = nDigits + 1 If nDigits > MAX_DIGITS Then Result = False Goto Exit_Point End If anDigits(nDigits) = Val(CurrentCharacter) End If Next ofsCurrentCharacter CheckSum = anDigits(nDigits) For ofsCurrentDigit = nDigits - 1 To 1 Step -1 If Multiplier = 2 Then Multiplier = 1 Else Multiplier = 2 End If DigitValue = anDigits(ofsCurrentDigit) * Multiplier CheckSum = CheckSum + DigitValue If DigitValue > 9 Then CheckSum = CheckSum - 9 End If Next ofsCurrentDigit Result = ((CheckSum Mod 10) = 0) Exit_Point: IsValidCreditCardNumber = Result Exit Function End Function
The function needs to strip any non-digit characters from the input string. Many people, when asked to input a credit card number, will input spaces or hyphens to match the spacing on the card. The spaces are not significant -- logically, a credit card number is just a string of digits. The validation logic presented here will strip all non-numerics to obtain the credit card number. You may prefer to enforce a more rigid scheme where you will reject any non-numeric input, or accept only digits, spaces and hyphens.
As it has to examine each character anyway, the function uses the opportunity to convert each valid character into an integral value (a Byte, actually) and stores them into an array. It also counts how many digits were found, because it needs to walk through the number backwards so it needs to know where to start from.
When the string has been parsed into digits, we are ready to start. The LAST digit is the checksum. It is generated by the software at the credit card company to make the sum come out right. We start of by initialising the Checksum to this digit. If the card is valid, the checksum will end up being an exact multiple of 10.
We derive the checksum by starting with the value of the last digit, unchanged. We then work BACKWARDS from the digit immediately before the checksum to the first digit. For each digit, we work out a value, which is the digit multiplied by either 2 or 1. We alternate the multiplier, starting with 2 for the digit before the checksum, then 1 for the digit before that, then back to 2 and so on. We take the value of the digit and multiply it by the multiplier. If the result is > 9, we subtract 9. Then we add it into the Checksum. This continues back to the first digit.
When we are done, we simply check whether the checksum is an exact multiple of 10 (using the MOD operator). If it is, then the card number is valid; otherwise it is not.
You can use the function above as-is, but if you want to understand, we can work through this example:
|Card Number :||1||2||3||4||5||6||7||8||9||0||1||2||3||4||5||6||Multiplier:||2||1||2||1||2||1||2||1||2||1||2||1||2||1||2||Value:||2||2||6||4||1||6||5||8||9||0||2||2||6||4||1||<- Sum = 58|
The value is worked out by multiplying the digit by the multiplier. If the multpilication results in a two-digit number, then you subtract 9 to get the value (as we did for the digits highlighted with asterisks). Note that we started the multiplier with 2 at the last non-checksum digit, so that for this 16-digit number the multiplier for the first digit is also 2. For 15-digit numbers like American Express, the multiplier for the first digit is 1, so it is important to always start at the end as shown.
If we add all the values, we come up with 58. Adding in the check digit (6) gives us 64, which is NOT evenly divisible by 10, meaning that this is not a valid credit card number. The check digit would need to be a 2 in order for this card number to be valid.
The prize for the first correct answer only is a box of smarties: The algorithm, as presented, is correct and in accurately models the way the algorithm works. However, it can be somewhat simplified if we make one small and valid generalisation. Can you see it? The performance benefits would be all but non-existent, so don't stress it.