A while ago I started working on converting an eCommerce payment gateway's
(DataCash) COM server to a native .NET
assembly using their XML API. Once I had got a basic version working I decided
to produce a simple web form to test it out, and so opened it up for all comers
(and received some very generous donations from CP members -- thanks guys :).
As part of this web form I wanted to include support to check that users had
entered a card number, expiration date etc., and then wanted to extend it further
to include support for checking that the card number was valid before issuing
a request to the payment gateway's server. This is the result, a drop-in replacement
for any of the other validation controls.
Incidentally, you can see a demo of the validator in use (as well as the card
payment gateway assembly) at the following address: https://ssl500.securepod.com/oobaloo/DataCash/,
besides this you may also be interested in the everything
you ever wanted to know about CC's guide.
Before getting into any of the implementation details here is a simple UML
class diagram to show the rough layout of the Control.
The diagram is missing information about parameter types since its not essential
to understanding the model. For those who are not familiar with UML, it shows
a specialisation relationship between the
classes - an is a relationship - demonstrating inheritance from
to the more specialised
CreditCardValidator class. New with the
third incarnation of the control is the
which is used to specify what types of card should pass the validation using
The control includes support for validating card numbers in two ways. Firstly,
through checking the card number using Luhn's formula, the details of which
are included in the next part of the article. Secondly, the card type itself
is examined, and the length is checked. The card type can be determined through
a prefix and each type has a specified length, by examining these an additional
level of control can be added - the types of card to accept. The method that
implements this is
IsValidCardType, and whether this is used during
the validation is set by the
The main way the card number is going to be validated is through Luhn's formula,
so firstly a little bit of background information and a demo of how the validation
CreditCardValidator control will perform a check on the contents
of the textbox using Luhn's formula which is used to validate card numbers.
It can be used to check against a number of cards, including the following:
- American Express
- Diners Club/Carte Blanche
* These are UK only cards from my recollection, but have been tested by myself
Information on the history
of the formula can be found on WebOpedia but so you don't have to read that
here is a summary of how
it is performed:
- Double the value of alternating digits
The first step is to double each of the alternating digits in the number.
But the trick is to start with the second digit from the right and work backwards.
Say we have a credit card number 1234 5678 1234 5670. We'll start with the
rightmost number 7, double it, and then do the same for every other digit.
1234 5678 1234 5670
This will give us the following values.
7 x 2 = 14
5 x 2 = 10
3 x 2 = 6
Add the separate digits of all the products
Now we'll the separate digits of all the products, and come up with
a final sum.
(1 + 4) + (1 + 0) + 6 + 2 + (1 + 4) + (1 + 0) + 6 + 2 = 28
Be sure to add the digits, not just the number.
- Add the unaffected digits
Now we'll go back to the original number and add all the digits that
we didn't double. We'll still start from the right, but this time we'll start
from the rightmost number.
1234 5678 1234 5670
0 + 6 + 4 + 2 + 8 + 6 + 4 + 2 = 32
Add the results and divide by 10
Finally, we'll add both the results and divide the answer by 10.
28 + 32 = 60
60 is evenly divided by 10, so the credit card number is well formed and
ready for further processing.
This will be converted into a method which will perform all of the steps listed
above on the contents of the specified textbox. By deriving the new validator
BaseValidator it's possible to produce a control which
behaves exactly as any other validator for the easiest deployment.
The code for Luhn's formula is in the
which is implemented as follows:
private static bool ValidateCardNumber( string cardNumber )
System.Collections.ArrayList CheckNumbers = new ArrayList();
int CardLength = cardNumber.Length;
for (int i = CardLength-2; i >= 0; i = i - 2)
CheckNumbers.Add( Int32.Parse(cardNumber[i].ToString())*2 );
int CheckSum = 0;
for (int iCount = 0; iCount <= CheckNumbers.Count-1; iCount++)
int _count = 0;
if ((int)CheckNumbers[iCount] > 9)
int _numLength = ((int)CheckNumbers[iCount]).ToString().Length;
for (int x = 0; x < _numLength; x++)
_count = _count + Int32.Parse(
_count = (int)CheckNumbers[iCount];
CheckSum = CheckSum + _count;
int OriginalSum = 0;
for (int y = CardLength-1; y >= 0; y = y - 2)
OriginalSum = OriginalSum + Int32.Parse(cardNumber[y].ToString());
The code includes comments that explains how it works, however, here is a summary.
- Build an
ArrayList which will contain the alternating digits
taken in step 1. This is so that the original values can be used again in
step 2 but without looping back through the number. This was primarily done
- Once the list has been created, a sum is created of the individual digits
if the number is greater than 9 (i.e. has more than one digit).
- The original digits that were untouched are added together, these create
OriginalSum variable. This is then added to the number created
as a result of steps 1 and 2, and the value is divided by 10 and the result
tested against 0 which provides the return value for the function.
If an exception is thrown throughout the code, then false is returned.
Card Type Validation
Each of the card types mentioned above can be tested for a given length based
upon the numerical prefix. The prefixes and lengths are in the table below:
|Card Type||Prefix||Number Length|
|VISA||4||13 or 16|
|American Express||34 or 37||15|
|Diners Club/Carte Blanche||300-305,36,38||14|
These types can be put into an enumeration. This will allow us to include a
property that users can set specifying which types to accept, and then test
the property during the validation to determine which types should be accepted.
public enum CardType
MasterCard = 0x0001,
VISA = 0x0002,
Amex = 0x0004,
DinersClub = 0x0008,
enRoute = 0x0010,
Discover = 0x0020,
JCB = 0x0040,
Unknown = 0x0080,
All = CardType.Amex | CardType.DinersClub |
CardType.Discover | CardType.Discover |
CardType.enRoute | CardType.JCB |
CardType.MasterCard | CardType.VISA
CardType (which is an instance of an Int32-based enumerated
type) will be used as a set of bit flags - each bit reflects a single card type.
So, 0...0001 is MasterCard, 0...0010 is VISA. By using a set of bit flags it
will be possible to set a variable to more than one card type, and be able to
determine which ones are to be supported.
This card type check is to be performed alongside the length check (ensuring
that the card number matches the card type's expected length) and for this check
we will use a Regular Expression using the .NET Framework's Regex class. Regular
Expressions let you perform pattern matches, and can be extremely powerful.
For more details on Regular Expressions take a look at .NET
Framework Regular Expressions on MSDN, and if you only want to include this
kind of validation you can use the Regular
Expression validation control.
The card type check also includes support for the end user to specify which
card types should pass the validation, this is set through the
property (and then stored in the
_cardTypes member variable). The
code looks like this:
public bool IsValidCardType( string cardNumber )
if ( (Regex.IsMatch(cardNumber,"^(34|37)"))
&& ((_cardTypes & CardType.Amex)!=0) )
else if ( (Regex.IsMatch(cardNumber,"^(51|52|53|54|55)")) &&
((_cardTypes & CardType.MasterCard)!=0) )
else if ( (Regex.IsMatch(cardNumber,"^(4)")) &&
((_cardTypes & CardType.VISA)!=0) )
else if ( (Regex.IsMatch(cardNumber,"^(300|301|302|303|304|305|36|38)")) &&
((_cardTypes & CardType.DinersClub)!=0) )
else if ( (Regex.IsMatch(cardNumber,"^(2014|2149)")) &&
((_cardTypes & CardType.DinersClub)!=0) )
else if ( (Regex.IsMatch(cardNumber,"^(6011)")) &&
((_cardTypes & CardType.Discover)!=0) )
else if ( (Regex.IsMatch(cardNumber,"^(3)")) &&
((_cardTypes & CardType.JCB)!=0) )
else if ( (Regex.IsMatch(cardNumber,"^(2131|1800)")) &&
((_cardTypes & CardType.JCB)!=0) )
if ( (_cardTypes & CardType.Unknown)!=0 )
It's not the prettiest of code, but it effectively performs a RegEx comparison
cardNumber for each possible card type. The regular expression
is very simple, and searches for any of the numbers separated by the pipe character.
So, for the AMEX type it searches for either 34 or 37. Because the string is
prefixed by the epsilon (^) character, the search is performed at the start
cardNumber. This search is performed through the
static method of the
A further test is also done at the same time to determine whether or not the
card's type exists in the
&& ((_cardTypes & CardType.Amex)!=0)
Provided both the tests return true (i.e. the prefix is matched, and the card
type exists in the
_cardTypes member variable) a length test is
performed and provided the card number is a valid length then the
method will return true.
If the card type is not recognised then as long as
been set in
IsValidCardType will return
true - since the user will have specified that an
type is an accepted card type. Otherwise, it will return false and will fail.
_cardTypes variable is set using a property accessor which
is implemented as follows:
public string AcceptedCardTypes
_cardTypes = (Etier.CardType)
Enum.Parse(typeof(Etier.CardType), value, false );
This enables users to specify the card types using a string (e.g. "Amex,
VISA, Unknown"), as opposed to having to programmatically set it via. the
OnLoad event (e.g.
AcceptedCardTypes = CardType.VISA | CardType.Amex
That's the second validation method implemented, all that's left to do is to
BaseValidator derived class that uses the above methods
to validate the text in the associated control.
The Validation Control's implementation
That is the majority of the code written, all that's left to do is produce
a class dervied from
override the necessary functions. Incidentally, I used Cenk
Civici's article for a "ListControl SelectedItem Validator" as
the main source for this.
BaseValidator requires that we override the
method, which suprisingly enough, is the function that determines whether or
not the associated control has valid content -- in our case whether or not the
associated text box has a valid credit card number entered. Following Cenk Cevi's
article I also included an implementation of the
helper function that determines whether the control specified by the
property is a valid control - thus ensuring that it is a textbox we're checking.
Since most controls have a text property it's probably not a major issue but
it would be strange to be validating the text property of a button etc., so
as an extra precaution I included it.
protected override bool ControlPropertiesValid()
Control ctrl = FindControl(ControlToValidate);
if ( null != ctrl )
if (ctrl is System.Web.UI.WebControls.TextBox)
_creditCardTextBox = (System.Web.UI.WebControls.TextBox) ctrl;
return ( null != _creditCardTextBox );
The code first finds the
ControlToValidate and checks that it
does indeed point to something, and then checks whether it is a
If so, it sets the member variable
_creditCardTextBox to the
on the web form. If anything bad happens it returns
This method is declared as abstract in the
and so has to be implemented by our derived class. It is also the method that
is called to check that the contents of the associated control is valid.
CreditCardValidator control includes two additional properties,
one of which is
ValidateCardType which can be used to set whether
or not the card type should also be checked. If it is to be checked, then the
length is checked before the number is evaluated against Luhn's formula. However,
ValidateCardType property is set to false then the card
number is validated against Luhn's formula directly.
protected override bool EvaluateIsValid()
return ValidateCardNumber( _creditCardTextBox.Text );
return ValidateCardNumber( _creditCardTextBox.Text );
ValidateCardNumber method succeeds, the validation
is considered a success.
Using the Credit Card Validator Control
That's all that's necessary to have the
finished. Now for a quick example of how it can be used in a real web form (the
full code for this is included as a download at the top of the page).
The first that needs to be done is to include the declaration at the top of
the aspx page that imports the assembly and maps the namespace to a prefix.
This also requires that the assembly's DLL file is copied to the bin directory
for the application. The location of this directory depends upon the setup of
your application, but assuming there's a virtual directory off the root called
/CreditCard/ then your bin directory would be /CreditCard/bin/.
<%@ Register TagPrefix="etier" Namespace="Etier" Assembly="CreditCardValidator" %>
This allows you to then add the control to the page in the form of
For example, the following code would bind the validator control to a
ErrorMessage="Please enter a valid credit card number"
AcceptedCardTypes="Amex, VISA, MasterCard"
CreditCardValidator offers a few properties that are not taken
BaseValidator base type, these are
sets whether or not the card type should be checked. This will mean that a length
test is performed, as well as specifying which card types should be accepted.
The AcceptedCardTypes property can be set using the CardType enumeration. In
the above code the accepted types are Amex, VISA, and MasterCard. If unrecognised
card types are also to be accepted then you can include "Unknown"
in the list. If you want all the recognised types accepted then you can use
"All". Thus, to accept anything you should use "Unknown, All".
Since I chose to have the error message displayed in a
control, I set the
Display property to
none to ensure
that it wasn't displayed in-line. Otherwise, the error message would be put
at the location of this control.
ASP.NET provides a huge amount of functionality built in, and I can't remember
how many times I've produced custom code to do the very thing that Validation
controls provide. This, coupled with the Object-Oriented nature of .NET, means
that you can extend the existing .NET offerings into something you can use time
and again. Now that xcopy deployment is a real option with web applications,
dropping in support for your own custom validation controls can be done in a
fraction of the time than with traditional ASP development.
This concludes my second article, and I hope its of use to people. It doesn't
really show anything new, but does roll up some important functionality into
a re-usable control that can be deployed and used extremely easily.
Things that are missing/Ideas for improvement
At the moment, all validation is taken care of on the server-side. This is
fine since the aim was to prevent requests being issued to the payment gateway
with incorrect details - especially since there's likely to be a delay of a
few seconds. However, since the validation algorithm is not a secret and is
relatively simple to implement, a client side version could be implemented to
check the value before the data is posted back to the form and thus saving round-trips.
If anyone decides to extend this to include client side scripting, or has any
comments or questions then it'd be great to hear from you.
- Update: 24/08/2002 - I have added support for specifying
the card types that should be accepted by the control. The changes were made
to the implementation of the
IsValidLength method which has been
IsValidCardType. The card types to be accepted can be
set using the
AcceptedCardTypes property, which uses the
- Update: 21/08/2002 - shortly after posting the first version,
I included support for validating the length of the number. The article below
shows the new updated version only.