Developing Custom Control for Credit Card Validations
Introduction
In almost all of the web sites which have e-commerce features, you need to
validate the credit card number entered by the end user. When it comes to
validation you have two options - either perform client side validation using
JavaScript or perform server side validation via VB.NET or C# code. If you are
like me you can develop your own custom control that can be reused across
multiple projects. In this article we will see how to create a custom server
control that extends the ASP.NET validation mechanism and allows you to perform
client as well as server side validation.
How credit card numbers are validated?
In order to validate a credit card number you need to perform following
checks on it:
- The credit card type and starting digits. Credit card providers have
certain standard digits with which the card number begins. For example VISA
card begin with 4.
- The credit card type and length of the card number. Credit card
providers also have some standard length for their card numbers. For example
VISA card numbers are either 13 or 16 digits.
- The credit card number also needs to pass Luhn's check. Luhn's check is
nothing but an algorithm that can be used to decide whether the credit card
number is valid or no.
A credit card number matching all of the above conditions can be treated as a
legitimate card number. Of course the aim of building these validations is to
reduce the load on the payment gateway system as well as to avoid server post
backs.
Creating your own validation control
While creating ASP.NET custom server controls you generally inherit your
class from WebControl base class. However, we need to develop a custom control
that will take part in page validation process. To achieve this .NET framework
provides a class called BaseValidator. This class provides basic infrastructure
to write your own validation control that will participate in client as well as
server side validation process just like any other built-in validation controls.
Public Class CreditCardValidator
Inherits BaseValidator
End Class
BaseValidator is an abstract class and once you inherit from it you must
override following two methods:
Protected Overrides
Function ControlPropertiesValid() As Boolean
Protected Overrides
Function EvaluateIsValid() As Boolean
The first method s intended to get object reference to the control specified
by ControlToValidate property. The second method is the core method that decides
whether the control value is valid or invalid. Of course this method performs
only server side validation.
Putting server side validation
Now that we are familiarize with our custom validator let's code the server
side functionality first.
Public Class CreditCardValidator
Inherits BaseValidator
Private myTextBox As TextBox
Private objcardtype As CardTypes
Public Property CardType() As CardTypes
Get
Return objcardtype
End Get
Set(ByVal Value As CardTypes)
objcardtype = Value
End Set
End Property
Protected Overrides
Function ControlPropertiesValid() As Boolean
Dim ctrl As Control
ctrl = FindControl(ControlToValidate)
If Not ctrl Is Nothing Then
myTextBox = ctrl
Return True
Else
Return False
End If
End Function
Protected Overrides
Function EvaluateIsValid() As Boolean
If IsValidForLuhnLogic(myTextBox.Text)
And
IsValidForCardType(myTextBox.Text, CardType) Then
Return True
Else
Return False
End If
End Function
Private Function
IsValidForLuhnLogic
(ByVal cardnumber As String) As Boolean
Dim strCCNumber As String
strCCNumber = cardnumber
Dim strRev As String
Dim strCh As String
Dim intNumber As Integer
Dim strNumberFinal As String
Dim intSum As Integer
Dim validLuhn As Boolean
Dim intTemp As Integer
strRev = StrReverse(strCCNumber)
Try
For intTemp = 1 To Len(strRev)
strCh = Mid(strRev, intTemp, 1)
intNumber = CInt(strCh)
If intTemp Mod 2 = 0 Then
intNumber = intNumber * 2
If intNumber > 9 Then
intNumber = intNumber - 9
End If
End If
strNumberFinal = strNumberFinal & intNumber
Next intTemp
For intTemp = 1 To Len(strNumberFinal)
intSum = intSum + Mid(strNumberFinal, intTemp, 1)
Next intTemp
If intSum Mod 10 = 0 Then
validLuhn = True
Else
validLuhn = False
End If
Catch
validLuhn = False
End Try
Return validLuhn
End Function
Private Function
IsValidForCardType(ByVal cardnumber As String,
ByVal cardtyp As CardTypes) As Boolean
Dim validType As Boolean = False
Select Case cardtyp
Case CardTypes.VISA
If (Regex.IsMatch(cardnumber, "^(4)"))
And (cardnumber.Length = 16
Or cardnumber.Length = 13) Then
validType = True
End If
Case CardTypes.MASTERCARD
If (Regex.IsMatch(cardnumber, "^(5[1-5])"))
And cardnumber.Length = 16 Then
validType = True
End If
Case CardTypes.AMERICANEXPRESS
If (Regex.IsMatch(cardnumber, "^3(4|7)"))
And cardnumber.Length = 15 Then
validType = True
End If
Case Else
validType = False
End Select
Return validType
End Function
End Class
Public Enum CardTypes
VISA
MASTERCARD
AMERICANEXPRESS
End Enum
- We created a class called CreditCardValidator that inherits from
BaseValidator class
- We override ControlPropertiesValid method and try to get hold of the
TextBox as indicated by ControlToVaidate property. We do this via
FindControl() method.
- We also created a property called CardType which is of type CardTypes -
an enumeration defined by us. User of our control will set this property
based on some web form input.
- We have created two helper functions - IsValidForLuhnLogic and
IsValidForCardType. The first function returns true or false based on
whether the credit card number passes the Luhn logic check. Here, we will
not go into details of how this logic works but you can get these details
from many public web sites. The second function returns true or false based
on whether the card number satisfies requirements of length and start
digits.
- Finally we override EvaluateIsValid method that returns true if
both of the above functions are true. Otherwise it returns false.
Putting client side validation
Now our control is capable of validating the credit card numbers on the
server side. But to reduce network traffic it would be nice if we plug in the
logic to perform same validation on client side also. That's what we will do
next.
To enable client side support we need to override yet another method called
AddAttributesToRender. The validation controls get rendered as SPAN tags in the
browser. In this method we need to emit a client side attribute called
evaluationfunction that points to the client side JavaScript function performing
the validation.
Protected Overrides
Sub AddAttributesToRender(ByVal writer
As System.Web.UI.HtmlTextWriter)
MyBase.AddAttributesToRender(writer)
writer.AddAttribute("evaluationfunction",
"ValidateCreditCard")
writer.AddAttribute("cardtype", CardType.ToString)
End Sub
Here, we are assuming that there will be a client side function called
ValidateCreditCard that will validate the credit card for us. There are three
ways to provide this function:
- Write it in the HEAD section of the web form
- Write it in a .js file and use it in the SRC attribute of SCRIPT tag
- Manually emit the entire JavaScript code in the OnPreRender overridden
method of the control
In our example I will use the second approach.
Here is the client side function:
function ValidateCreditCard(val)
{
var cardNumber=
document.getElementById(val.controltovalidate).value;
var cardType=val.cardtype;
var isValid = false;
var ccCheckRegExp = /[^\d ]/;
isValid = !ccCheckRegExp.test(cardNumber);
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 "AMERICANEXPRESS":
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;
}
We will not discuss this function in detail here but it should be fairly easy
to follow if you have followed the server side code. Note how the parameter val
of this function provides access to the emitted attributes in object based
manner.
That's it. Now, compile the control project and you are ready to use it on
any of your web forms.
Developing a test web form
In order to test the control create a new ASP.NET web application and add a
markup like this to its .aspx file:
<%@ Register TagPrefix="cc1"
Namespace="CreditCardValidatorVB"
Assembly="CreditCardValidatorVB" %>
...
...
<script src="ccvalidation.js"></script>
<cc1:CreditCardValidator
id="CreditCardValidator1"
runat="server"
ErrorMessage="This is error message"
ControlToValidate="TextBox1">
This is error text
</cc1:CreditCardValidator>
Note that I have put the client validation function in a file called
ccvalidation.js. If you set EnableClientScript property to True then the
validation will be performed on the server side else post back will happen and
server side validation will occur. Now run the test web form and see your
control in action!
Summary
Credit card validation is a commonly required on almost all the e-commerce
sites. Using ASP.NET validation control framework you can develop your own
validation control that validates the credit card on client side as well as on
the server side.