Introduction

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.

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 BaseValidator and CreditCardValidator
classes - an is a relationship - demonstrating inheritance from BaseValidator
to the more specialised CreditCardValidator class. New with the
third incarnation of the control is the AcceptedCardTypes property
which is used to specify what types of card should pass the validation using
the CardType enumeration.

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 ValidateCardType property.

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
is performed.

Luhn's Formula

The 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:

MasterCard

VISA

American Express

Diners Club/Carte Blanche

enRoute

Discover

JCB

Solo*

Switch*

* These are UK only cards from my recollection, but have been tested by myself
and work.

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
.
.
etc.

Add the separate digits of all the productsNow 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 digitsNow 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
control from BaseValidator it's possible to produce a control which
behaves exactly as any other validator for the easiest deployment.

Luhn's implementation

The code for Luhn's formula is in the ValidateCardNumber method
which is implemented as follows:

privatestaticbool ValidateCardNumber( string cardNumber )
{
try
{
// Array to contain individual numbers
System.Collections.ArrayList CheckNumbers = new ArrayList();
// So, get length of card
int CardLength = cardNumber.Length;
// Double the value of alternate digits, starting with the second digit
// from the right, i.e. back to front.
// Loop through starting at the end
for (int i = CardLength-2; i >= 0; i = i - 2)
{
// Now read the contents at each index, this
// can then be stored as an array of integers
// Double the number returned
CheckNumbers.Add( Int32.Parse(cardNumber[i].ToString())*2 );
}
int CheckSum = 0; // Will hold the total sum of all checksum digits
// Second stage, add separate digits of all products
for (int iCount = 0; iCount <= CheckNumbers.Count-1; iCount++)
{
int _count = 0; // will hold the sum of the digits
// determine if current number has more than one digit
if ((int)CheckNumbers[iCount] > 9)
{
int _numLength = ((int)CheckNumbers[iCount]).ToString().Length;
// add count to each digit
for (int x = 0; x < _numLength; x++)
{
_count = _count + Int32.Parse(
((int)CheckNumbers[iCount]).ToString()[x].ToString() );
}
}
else
{
// single digit, just add it by itself
_count = (int)CheckNumbers[iCount];
}
CheckSum = CheckSum + _count; // add sum to the total sum
}
// Stage 3, add the unaffected digits
// Add all the digits that we didn't double still starting from the
// right but this time we'll start from the rightmost number with
// alternating digits
int OriginalSum = 0;
for (int y = CardLength-1; y >= 0; y = y - 2)
{
OriginalSum = OriginalSum + Int32.Parse(cardNumber[y].ToString());
}
// Perform the final calculation, if the sum Mod 10 results in 0 then
// it's valid, otherwise its false.
return (((OriginalSum+CheckSum)%10)==0);
}
catch
{
returnfalse;
}
}

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
for readability.

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
the 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

MasterCard

51-55

16

VISA

4

13 or 16

American Express

34 or 37

15

Diners Club/Carte Blanche

300-305,36,38

14

enRoute

2014,2149

15

Discover

6011

16

JCB

3

16

JCB

2131,1800

15

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.

The 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 AcceptedCardTypes
property (and then stored in the _cardTypes member variable). The
code looks like this:

It's not the prettiest of code, but it effectively performs a RegEx comparison
on the 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
of the cardNumber. This search is performed through the IsMatch
static method of the Regex class.

A further test is also done at the same time to determine whether or not the
card's type exists in the AcceptedCardTypes property:

&& ((_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 IsValidCardType
method will return true.

If the card type is not recognised then as long as Unknown has
been set in _cardTypes then IsValidCardType will return
true - since the user will have specified that an Unknown card
type is an accepted card type. Otherwise, it will return false and will fail.

The _cardTypes variable is set using a property accessor which
is implemented as follows:

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
etc.)

That's the second validation method implemented, all that's left to do is to
produce the BaseValidator derived class that uses the above methods
to validate the text in the associated control.

The BaseValidator requires that we override the EvaluateIsValid
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 ControlPropertiesValid
helper function that determines whether the control specified by the ControlToValidate
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.

The code first finds the ControlToValidate and checks that it
does indeed point to something, and then checks whether it is a TextBox.
If so, it sets the member variable _creditCardTextBox to the TextBox
on the web form. If anything bad happens it returns false.

Finally, EvaluateIsValid

This method is declared as abstract in the BaseValidator class,
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.

The 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,
if the ValidateCardType property is set to false then the card
number is validated against Luhn's formula directly.

protectedoverridebool EvaluateIsValid()
{
if (_validateCardType) // should the length be validated also?
{
// Check the length, if the length is fine then validate the
// card number
if (IsValidCardType(_creditCardTextBox.Text))
return ValidateCardNumber( _creditCardTextBox.Text );
elsereturnfalse; // Invalid length
}
else// Check that the text box contains a valid number using
// the ValidateCardNumber method
return ValidateCardNumber( _creditCardTextBox.Text );
}

Provided the ValidateCardNumber method succeeds, the validation
is considered a success.

Using the Credit Card Validator Control

That's all that's necessary to have the CreditCardValidator control
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/.

The CreditCardValidator offers a few properties that are not taken
from the BaseValidator base type, these are ValidateCardType
and AcceptedCardTypes. The ValidateCardType property
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 ValidationSummary
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.

Conclusion

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.

History

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
renamed IsValidCardType. The card types to be accepted can be
set using the AcceptedCardTypes property, which uses the CardType
enumeration.

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.

Share

About the Author

I graduated with a first class BSc honours degree in a Computer Science/Management Science hybrid from Loughborough University. I live in London and currently work as a .NET Developer in central London.

Glad to hear you enjoyed the article. I haven't been on CodeProject for some time now so it's good to see how others are getting on!

I didn't think the date expiration really belonged to this validator. My opinion of the validators is that you ought to clearly define the responsibility of a given validator. So, the credit card one mentioned in this article is designed to check that a credit card number is valid, and matches the design of a specified type (see the updated version for download at my site -- it's mentioned in another post here).

In my opinion it's nicer to have the date validation split into a different validator, after all, it would be a different text control etc. that would contain the date. Maybe I'll write a date drop down control with an associated validator for this kind of thing Haven't done any ASP.NET coding for some time now!

--
Paul"Put the key of despair into the lock of apathy. Turn the knob of mediocrity slowly and open the gates of despondency - welcome to a day in the average office."
- David Brent, from "The Office"

I've actually already added code to display a drop-down for valid card types, its just that the project isn't available on CodeProject yet. I posted a link to my website with the code a while ago, it should be listed further down.

Effectively, because the new project had so many changes, it ended up being implemented quite differently from this article's solution, adding designer support + client-side JavaScript. I didn't want to upload it until I had an article that explained it, but haven't had time to write it.

Feel free to download the new code and try that out, as I said, it supports a drop-down of card-types out-of-the-box so to speak, and the code is relatively easy to follow.

Let me know how it goes,

Regards,
Paul

--
Paul"Put the key of despair into the lock of apathy. Turn the knob of mediocrity slowly and open the gates of despondency - welcome to a day in the average office."
- David Brent, from "The Office"

The above link opens the thread where I posted the URL to the new archives. Again, the stuff on my site (and here) doesn't reflect this since I haven't written a new article for it all. It's quite a large undertaking and I just don't have time at the moment. Sorry.

As I said, download the new DLLs and feel free to email me if you have any questions or problems.

Paul

--
Paul"Put the key of despair into the lock of apathy. Turn the knob of mediocrity slowly and open the gates of despondency - welcome to a day in the average office."
- David Brent, from "The Office"

I included the CC validator under my credit card information user control and kept getting an error in the javascript. The problem is the following: Since the CreditCardNumber text box resides in UC, the way the CC Validator tries to retrieve the value of the control to validate pointed to "CreditCardValidator1" and not to "_CreditCardUC1_CreditCardValidator".
My way around it was to move the validator from the ascx user control to the aspx page. But this makes the error display outside my UC, instead of right next to the CreditCardNumber text box.Do you think there is a way to fix this? How do we make it so that the javascript knows where the CC validation control is?

I've absolutely no idea how to find the control, maybe there's some way to retrieve it's absolute ID. I'll try and take a look at it some time (although I don't know when because I'm really, really busy at the moment... still, feels like I've been really busy since November last year!), I haven't been able to do any spare-time coding for a while now. This update was my last big one.

Anyway, glad to hear people are using the code, I'll try and take a look at MSDN and see if I can find any clues.

Regards,
Paul

--
Paul"Put the key of despair into the lock of apathy. Turn the knob of mediocrity slowly and open the gates of despondency - welcome to a day in the average office."
- David Brent, from "The Office"

To get the ID correctly change the top of RegisterClientScript in CreditCardValidator.cs code to the following. Note: val.controltovalidate will have the correct dynamic named control ID that you are looking for. When ASP.net renders it uses a SPAN tag that contains the renamed ID of the control so your client side script can work. I had to dig around to find this one a while back. Hope this helps.

Dynamically subclassing the control on a .ascx usercontrol causes some difficulties and the script seems to always run. Here's my thinking on this. RequiredFieldValidators are for checking if a field is empty and displays an Error of something like "Credit Card Required" In the case where the card was invalid then the Error message from the custom validator should fire saying something like "The Credit Card Provided is invalid" Two different messages and two different sets of functionality as their respective definitions should provide. It might not be necessary but Id have a RequiredFieldValidator and the CreditCardValidator both there to serve their respective purposes (http://www.eraserver.net/robertlair/tutorial_validators.aspx)

I changed the JavaScript in a way that I think it fixes the dynamic subclassing of the component on a .ascx usercontrol. Notice that I added val to the function call as this is done automatically for you and handles those nasty re-naming problems that occur on dynamically subclassed controls like from id="credit_card" to id="billingpage__customCC_ctl13" per below. I also added a quick check for a zero length string and returning true for the error message for the CC being invalid doesn't fire so I could leave it up to a RequiredFieldValidator component.

One other bit of a change that I made. I was using a <INPUT TYPE=TEXT runat=server> HtmlControl and couldn't seem to get the thing working so I dug in and noticed that it was asuming a TextBox control so I updated it to be able to handle the above by adding the following.

Whoops I'm a dolt and a spammer I guess, But this contorl is pretty darn COOLIO. Put and ID=somename into the INPUT field above and note that I changed the data type of _creditCardNumberTextBox to string and updated the code within CheckBoxRequiredFieldValidator.cs to reflect this field being changed to a string.

If anyone has any problems let me know, and I'll try and get the new article written. Because the control has so many new bits its quite a different beast. The code is fairly well commented, but feel free to email or post here with any questions.

--
Paul"Put the key of despair into the lock of apathy. Turn the knob of mediocrity slowly and open the gates of despondency - welcome to a day in the average office."
- David Brent, from "The Office"

I see you are using the Global file for designer support. can you provide an example for VB.NET so I have all the configurations correct for design support. So far I'm not having much luck. It's not that big of a deal because the controls work fine. It just displays as an error control in design. It would be nice to get your designer solution to work with my VB.NET project.:cool

Not only do I (strongly) dislike the VB.NET syntax, I don't have a great deal of spare time any more.

The whole thing about .NET is that the only difference between languages is the syntax (on the whole). Certainly everything I've done in this control could also be done in VB.NET just with some re-writing.

Not only that, but there's little point in translating it into VB.NET, you can just use the C# assembly in any .NET project.

Why would a VB.NET version be needed?

--
Paul"Put the key of despair into the lock of apathy. Turn the knob of mediocrity slowly and open the gates of despondency - welcome to a day in the average office."
- David Brent, from "The Office"

I'm re-working the validator at the moment to add a few things, one of which is the ability to add new card types into the application's XML configuration file - allowing developers to add new types by adding in the regular expression.

The original code included a set of supported card types, that could then be chosen to be accepted by setting a property. The question I have is how this is being used. Do most applications just support a standard set of types, do you only accept certain ones for certain transactions etc.

Basically, the XML config file contains the settings necessary to support a given card type, would it be preferable to have another property allowing developers to set which of those should be accepted? Should it just be whatever's in the XML file should work?

All comments would be appreciated. I've also now got VB.NET installed so I'll look into why that's being particularly problematic. Has anyone used it in a VB.NET project successfully?

The new version is pretty complete, so hopefully should have an updated article up in a week or so.

--
Paul"Put the key of despair into the lock of apathy. Turn the knob of mediocrity slowly and open the gates of despondency - welcome to a day in the average office."
- David Brent, from "The Office"

It's up to the owner of the commercial web site to decide what types of cards he/she accepts. Your control should be able to check any known type, but also have a property where the user can specify which of these card types they accept.

If it is possible to avoid the regular expressions in the XML config file I think you will be able to avoid "strange" problems. If you give them a way to store significant/unique string sections matching the start of the credit card number series, that would be much easier for an end user to implement. Internally you may use these string parts in a regular expression if you like. An alternative could just be to use a loop of cardNumber.StartsWitch(identifier) checks.

That should give you one property with start sequences: Sequence = "301,302,303,304,305";
Another property specifying the expected length: Digits = 16;
Of course a property specifying the card type/brand: Name = "JCB";

Those properties (XML attributes or elements) would be repeated per card type. The section for accepted cards could either be a single setting (XML element) with a string: "JCB, Mastercard, VISA"; or it could any number of elements under a parent element:

Well, there's a million ways to do this. My two points here is that they should have a separate property for accepted cards, and secondly the configuration file should be very easy to maintain for the user of the control.

This then determines the card types that would be accepted within the application.

My question is to whether the web.config should just be overridden with a new config per each web application directory (by removing/adding lines). Alternatively, adding another property that then sets which of these card types should be accepted. So the web.config contains all the types, and then accepted types are set per validation control as a property (like the old validator).

I've converted all the old card types to regular expressions that can also be used in JavaScript -- providing a central place for both server and client-side support.

--
Paul"Put the key of despair into the lock of apathy. Turn the knob of mediocrity slowly and open the gates of despondency - welcome to a day in the average office."
- David Brent, from "The Office"

I would definitely add a separate parameter indicating what cards are accepted (per site). This allows you to compile a long list of known cards for the user, and they won't have to remove lines from the list if they're not accepting all the available cards (most won't).

Why don't you add one more attribute specifying the expected length of the credit card number?

The expected length is taken care of in the regular expression. So, (for example) VISA it'll look for a number that begins with 4 and is followed by 15 more numbers. The main reason for doing this is so that the client-side support is done in the same way as with server-side.

I'll add another parameter for the accepted types to allow people to override the web.config, and if there isn't any explicit setting it will just default to the web.config, i.e. accept everything that's been set.

--
Paul"Put the key of despair into the lock of apathy. Turn the knob of mediocrity slowly and open the gates of despondency - welcome to a day in the average office."
- David Brent, from "The Office"