Online courses in ASP.NET MVC, ASP.NET Core, and Design Patterns conducted by Bipin Joshi. Read more...
Learn ASP.NET MVC, ASP.NET Core, and Design Patterns through our online training programs. Courses conducted by Bipin Joshi on weekends. Read more details here.

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.




Bipin Joshi is a software consultant, trainer, author and a yogi having 21+ years of experience in software development. He conducts online courses in ASP.NET MVC / Core, jQuery, AngularJS, and Design Patterns. He is a published author and has authored or co-authored books for Apress and Wrox press. Having embraced Yoga way of life he also teaches Ajapa Meditation to interested individuals. To know more about him click here.

Get connected : Twitter  Facebook  Google+  LinkedIn

Posted On : 20 Sep 2004



Tags : ASP.NET Web Forms Server Controls Custom Controls Security