In the examples in this book, we have used the CreditCardValidation.dll library to build XML Web services that validate credit card numbers. We have taken this approach to concentrate on the subject of each chapter and to simplify the projects that you have created.
In this appendix, we explain how credit card numbers are validated and detail the code statements of the CreditCardValidation.dll library. Reading this appendix is not essential for you to understand the rest of this book. We provide this information for the curious-minded reader.
You might notice that the code we list in this appendix is not optimized; we have striven for clarity over performance. Unlike the rest of this book, we do not provide step-by-step instructions for building this library. Visual Studio .NET projects containing all of the code are included in the sample code, which, if you followed the sample code download and installation steps in the Introduction, can be found at C:XMLWebServicesSBSProjectsAppA
Validating a credit card number is the process of ensuring that a number meets the criteria used by a card issuer for generating numbers. Validation does not include any of the following, which are usually performed as part of a sales transaction:
Ensuring that a provider has issued the credit card number
Ensuring that the card holder has sufficient credit to enter into a transaction
Ensuring that the card provider has not cancelled the card
Ensuring that the card has not expired
Validating card numbers is a two-stage process. First the card number is checked to ensure it has the correct prefix and length for the card type, and then a simple formula (known as the LUHN formula) is applied to the number to ensure that the number is not simply a random sequence of digits. The CreditCardValidation.dll library validates numbers for VISA, MasterCard, and American Express (AMEX) cards.
Each type of card number has a different prefix and length requirement, as illustrated in the following table. Checking that the number conforms to these requirements is the first step in validating a card number. For example, all VISA card numbers start with the digit 4 and contain either 13 or 16 digits.
Credit Card Issuer | Valid Prefixes | Valid Lengths |
---|---|---|
VISA | 4 | 13, 16 |
MasterCard | 51, 52, 53, 54, 55 | 16 |
AMEX | 34, 37 | 15 |
The LUHN formula is a simple checksum that can be applied to the types of credit cards that we support; applying the formula to valid card numbers will always result in a number that ends with a zero. To demonstrate how the LUHN formula is applied to credit card numbers, we will walk though the process using the VISA card number 4921 8352 2155 2042.
Reverse the digits of the credit card number.
Reversing the digits of the example number gives us: 2402 5512 2538 1294
Add the odd-numbered digits together.
By odd-numbered, we mean those digits that are at the first, third, fifth (and so on) position in the reversed-order number, which gives us the following sum:
2 + 0 + 5 + 1 + 2 + 3 + 1 + 9 = 23
Double the value of the even-numbered digits in the reversed-order number, and then add them together.
If the value of the doubled number is less than 9, we simply add that number to the total for this step. For example, the number 1 is doubled to 2.
For numbers that produce a doubled value that is more than 9, we add the value of each digit to the total for this step. For example, the digit 9 is doubled to 18, so we add 1 and 8 to the total. This gives us the following sum for our example number. We have indicated where we are adding individual digits in parenthesis: 8 + 4 + (1 + 0) + 4 + (1 + 0) + (1 + 6) + 4 + 8 = 37
Add the totals from the previous two steps; if the card number is valid, the result will end with a zero.
For our example, we add the result from steps 2 and 3 to get a total of 60. Because this number ends with a zero, the card number is valid. By checking the prefix and length requirements and applying the LUHN formula, we are able to determine that this is a valid VISA card number.
The CreditCardValidator library contains four classes. The main class, named Validator, contains the methods that support validating credit card numbers. The other three classes are all exceptions, which are thrown to indicate problems with the card number that is being validated.
We use the exception classes to indicate that the card number does not conform to the prefix and length requirements or that it contains illegal characters. For our purposes, legal characters are the digits 0 to 9 and white space, allowing us to process card numbers in the form 4921835221552042 as well as 4921 8352 2155 2042, which is the way that numbers are usually printed on credit cards. The code statements for the three exceptions are listed below.
The CCInvalidPrefixException is thrown when the card number does not match the prefix requirement for the card type—for example, when a VISA card number does not start with the digit 4.
Example A-1. C#
namespace XMLWebServicesStepByStep.CreditCardValidator { // Exception thrown when the candidate number // starts with an invalid prefix public sealed class CCInvalidPrefixException : System.ApplicationException { // Create a new instance of this exception // The message that describes the error public CCInvalidPrefixException(string p_message) : base(p_message) { // do nothing - rely on the base class } } }
The CCInvalidLengthException is thrown when the card number does not match the length requirement for the card type—for example, when a MasterCard card number does not contain 16 digits.
Example A-3. C#
namespace XMLWebServicesStepByStep.CreditCardValidator { // Exception thrown when the candidate number // contains the wrong number of characters public sealed class CCInvalidLengthException : System.ApplicationException { // Create a new instance of this exception // The message that describes the error public CCInvalidLengthException(string p_message) : base(p_message) { // do nothing - rely on the base class } } }
The CCIllegalCharacterException is thrown when the card number contains a character that is neither white space nor the digits 0 to 9.
Example A-5. C#
namespace XMLWebServicesStepByStep.CreditCardValidator { // Exception thrown when the candidate number // contains an illegal character public sealed class CCIllegalCharacterException : System.ApplicationException { // Create a new instance of this exception // The message that describes the error public CCIllegalCharacterException(string p_message) : base(p_message) { // do nothing - rely on the base class } } }
The Validator class contains the methods that are used to validate credit card numbers. Three methods are public (one for each type of card that we support), and two are private utility methods. Each method is described and listed in the following sections.
We process card numbers as strings of characters. The formatCreditCardNumber method is a private method that checks for illegal characters and removes any white space. Although we allow card numbers to contain spaces and tabs, we remove them to simplify the validation process. If an illegal character is found, a CCIllegalCharacterException is thrown. Otherwise, the method returns a string containing only the numeric characters from the card number string.
Example A-7. C#
private string formatCreditCardNumber(string p_card_number) { // define a string builder to use in composing the response StringBuilder x_builder = new StringBuilder(); // iterate through the characters and check them for validity foreach (char x_char in p_card_number.ToCharArray()) { if (Char.IsDigit(x_char)) { // append this to the result x_builder.Append(x_char); } else if (Char.IsWhiteSpace(x_char)) { // ignore this character - we will allow the // user to supply credit card numbers that // contain whitespace } else { // if the character is not a digit and is not // whitespace, then it is illegal for our purposes throw new CCIllegalCharacterException("Character "" + x_char + "" cannot be processed."); } } // return the contents of the string builder return x_builder.ToString(); }
Example A-8. Visual Basic .NET
Private Function _ formatCreditCardNumber(ByVal p_card_number As String) As String ’define a string builder to compose the result Dim x_string_builder As New StringBuilder() ’ iterate through the characters of the card Dim x_char As Char For Each x_char In p_card_number.ToCharArray() If (Char.IsDigit(x_char)) Then ’ add this number to the string builder x_string_builder.Append(x_char) ElseIf (Char.IsWhiteSpace(x_char)) Then ’ ignore this character - we will allow the ’ user to supply credit card numbers that ’ contain whitespace Else ’ if the character is not a digit and is not ’ whitespace, then it is illegal for our purposes Dim x_msg As String = String.Format _ ("Character {0} cannot be processed", x_char) Throw New CCIllegalCharacterException(x_msg) End If Next Return x_string_builder.ToString() End Function
The private checkLUHN method applies the LUHN formula to the card number, which is passed to the method as a string argument. This method works on the basis that any whitespace characters have already been removed from the argument string using the formatCreditCardNumber method. The checkLUHN method returns true if the LUHN formula results in a valid card number and false otherwise.
Example A-9. C#
private bool checkLUHN(string p_card_number) { // reverse the credit card number char[] x_chars = p_card_number.ToCharArray(); Array.Reverse(x_chars); p_card_number = new String(x_chars); // define an int to hold the result int x_total = 0; for (int i = 0; i < p_card_number.Length; i+= 2) { // simply add the first digit to the total x_total += Int32.Parse(new string(p_card_number[i], 1)); // we need to double the value of the even digit, if // there is one. Note: we need to check the length because // AMEX uses an odd number of characters (13) if (p_card_number.Length > i + 1) { // get an integer value that is twice that of the // next digit int x_second = Int32.Parse( new string(p_card_number[i + 1], 1)) * 2; // if the value is >= 10, we need to add the value // of the tens and units together, so that 15 is // treated as 1 + 5 = 6. if (x_second > 9) { x_total += x_second/10 + (x_second % 10); } else { // the digit value is 9 or less, and so // can be directly added to the total x_total += x_second; } } } // end of for loop // check that the total is divisible by 10 if (x_total % 10 == 0) { // the total is divisible by 10 without // leaving a remainder - this is a valid // result from the LUHN formula return true; } else { // the number leaves a remainder when divided // by 10 - this is not a valid result from the // LUHN formula return false; } }
Example A-10. Visual Basic .NET
Private Function checkLUHN(ByVal p_card_number As String) _ As Boolean Dim x_arr As Char() = p_card_number.ToCharArray() ’ reverse the contents of the array Array.Reverse(x_arr) ’ reformulate the string p_card_number = New String(x_arr) ’ define the total for the checksum Dim x_total As Integer ’define the integer for the loop Dim i As Integer For i = 0 To p_card_number.Length - 1 Step 2 x_total += Integer.Parse( _ New String(p_card_number.Chars(i), 1)) If (p_card_number.Length > i + 1) Then Dim x_second As Integer x_second = Integer.Parse( _ New String(p_card_number.Chars(i + 1), 1)) * 2 ’ if the value is >= 10, we need to add the value ’ of the tens and units together, so that 15 ’ is treated as 1 + 5 = 6 If (x_second > 9) Then x_total += Int(x_second / 10) + x_second Mod 10 Else ’ simply add the value to the total x_total += x_second End If End If Next i If (x_total Mod 10 = 0) Then ’ the credit card number is valid Return True Else ’ the credit card number is not valid Return False End If End Function
These three public methods are used to validate a card number. Each method calls the formatCreditCardNumber method, checks the length and prefix requirements for the type of card that the method supports, and then calls the checkLUHN method to validate the number itself. If the number does not conform to the prefix or length requirements, a CCInvalidPrefixException or CCInvalidLengthException will be thrown; otherwise the result of the checkLUHN method will be returned to the caller.
Example A-11. C#
public bool ValidateAMEX(string p_card_number) { // format the number to remove whitespace p_card_number = formatCreditCardNumber(p_card_number); // AMEX cards are always 15 digits long // and start with either "34" or "37" if (p_card_number.Length == 15) { // the card number contains the correct // number of digits if (p_card_number.StartsWith("34") || p_card_number.StartsWith("37")) { // the card has the required prefix // - we can move on to check the number return checkLUHN(p_card_number); } else { // the card does not have a valid prefix throw new CCInvalidPrefixException( "AMEX card numbers " + "are prefixed with either "34" or "37"."); } } else { // the card does not have the required // number of digits throw new CCInvalidLengthException( "AMEX cards contain 15 digits."); } } public bool ValidateVisa(string p_card_number) { // format the number to remove whitespace p_card_number = formatCreditCardNumber(p_card_number); // VISA cards are either 13 or 16 digits long and // start with "4" if (p_card_number.Length == 13 || p_card_number.Length == 16) { if (p_card_number.StartsWith("4")) { // the card has the required prefix // - we can move on to check the number itself return checkLUHN(p_card_number); } else { // the card number does not have a valid prefix throw new CCInvalidPrefixException( "VISA cards are prefixed with "4"."); } } else { // the card does not have the required number of digits throw new CCInvalidLengthException( "VISA card numbers contain either 13 or 16 digits."); } } public bool ValidateMasterCard(string p_card_number) { // format the number to remove whitespace p_card_number = formatCreditCardNumber(p_card_number); // MasterCard numbers are 16 digits long and start // with the range 51-55 if (p_card_number.Length == 16) { // the card number has the required number of digits int x_second_digit = Int32.Parse(p_card_number[1].ToString()); if (p_card_number.StartsWith("5") && x_second_digit >= 1 && x_second_digit <= 5) { return checkLUHN(p_card_number); } else { // the card does not have a valid prefix throw new CCInvalidPrefixException( "MasterCard numbers start with 51 through " + "55 inclusive."); } } else { // the card number does not have the right number of digits throw new CCInvalidLengthException( "MasterCard numbers contain 16 digits"); } }
Example A-12. Visual Basic .NET
Public Function ValidateAMEX(ByVal p_card_number As String) _ As Boolean ’ format the number to remove whitespace and check for ’ illegal chars p_card_number = formatCreditCardNumber(p_card_number) ’AMEX cards are always 15 digits long and start with ’ either "34" or "37" If (p_card_number.Length = 15) Then ’ this number is the right length for AMEX If (p_card_number.StartsWith("34") Or _ p_card_number.StartsWith("37")) Then ’ this card is the right length and has a valid prefix ’ - we can move on to check the validity ’ of the number itself Return checkLUHN(p_card_number) Else ’ this card number does not have the correct prefix, ’ and so is not a valid AMEX card Throw New CCInvalidPrefixException( _ "AMEX card numbers are prefixed with either" + _ "34 or 37.") End If Else ’ this number has the wrong number of digits Throw New CCInvalidLengthException( _ "AMEX cards contain 15 digits.") End If End Function Public Function ValidateVisa(ByVal p_card_number As String) _ As Boolean ’ format the number to remove whitespace and check for ’ illegal chars p_card_number = formatCreditCardNumber(p_card_number) ’ VISA cards are either 13 or 16 digits long and start with "4" If (p_card_number.Length = 13 _ Or p_card_number.Length = 16) Then If (p_card_number.StartsWith("4")) Then ’ the number has a valid prefix ’ - we can now move on to check the validity ’ of the number itselt Return checkLUHN(p_card_number) Else ’ this card number does not have a valid prefix Throw New CCInvalidPrefixException( _ "VISA cards are prefixed with 4.") End If Else ’ this card number does not have the right number of digits Throw New CCInvalidLengthException( _ "VISA card numbers contain either 13 or 16 digits.") End If End Function Public Function ValidateMasterCard(ByVal p_card_number As String) _ As Boolean ’ format the number to remove whitespace and check for ’ illegal chars p_card_number = formatCreditCardNumber(p_card_number) ’ MasterCard numbers are 16 digits long and ’ start with the range 51-55 If (p_card_number.Length = 16) Then Dim x_second_digit As Integer = Val(p_card_number.Chars(1)) If (p_card_number.StartsWith("5") _ And x_second_digit >= 1 And x_second_digit <= 5) Then ’ the number has a valid prefix ’ - we can now move on to check the number itself Return checkLUHN(p_card_number) Else ’ the number does not have the required prefix Throw New CCInvalidPrefixException( _ "MasterCard numbers start with 51 through " + _ "55 inclusive.") End If Else ’ this card number does not have the right number of digits Throw New CCInvalidLengthException( _ "MasterCard numbers contain 16 digits") End If End Function
In this appendix, we have described credit card number validation code that we have used throughout this book to build XML Web services. It is not essential to understand all of the code statements and techniques used in the example. We have treated this functionality as a backdrop to create XML Web services that you can use and test.
3.147.13.192