Previous Page
Next Page

Hack 25. Validate Credit Card Numbers

Validate credit card numbers without submitting and refreshing the entire web page.

Entering a credit card number on a web page has become commonplace. This hack verifies the entered credit card number, then submits it to the server component only if the number is valid. Nothing else changes on the page except for a user message, which notifies the user of any error conditions or that the credit card has passed muster and has been sent to the server to be processed. (Although we won't discuss them here, as in "Validate Email Syntax" [Hack #23], the server component then implements its own credit card validation routines.)

The server connection will likely be initiated over Secure Sockets Layer (SSL), such as with the HTTPS protocol, and be involved with an e-commerce component that further verifies the purchase information with a merchant bank. This hack, however, just verifies the number, generates a message, and makes an HTTP request using Ajax techniques.

Figure 3-4 shows what the web page looks like.

Figure 3-4. Enter a credit card number for verification


This is the web page code. It imports two JavaScript files, http_request.js and cc.js:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
    <script type="text/javascript" src="js/http_request.js"></script>
    <script type="text/javascript" src="js/cc.js"></script>
    <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
    <title>Enter credit card number</title>
</head>
<body>
<h3>Please enter your payment information</h3>
<div id="message"></div>
<p>
    [Name and billing address appear here]
</p>
<p>
    Credit card type:
</p>
<form action="javascript:void%200">
    <p>
        <select name="cctype">
            <option value="Choose one...">Choose one...</option>
            <option value="Mastercard">Mastercard</option>
            <option value="Visa">Visa</option>
            <option value="American Express">American Express</option>
            <option value="Discover">Discover</option>
        </select>
    </p>
    <p>
        Credit card number (#### #### #### #### or no spaces): 
        <input type="text" name="cc" size="16" maxlength="19" />
    </p>
    <p>Expiration date:  
        <select name="exp_month">
            <option>January</option>
            <option>February</option>
            <option>March</option>
            <!etc...-->
        </select>
        <select name="exp_year">
            <option>2005</option>
            <option>2006</option>
            <option>2007</option>
            <!etc...-->
        </select>
    </p>
    <p>
        Card Security code: 
        <input type="text" name="scode" size="4" maxlength="4" />
</p>
    <p>
        <button type="submit" name="submit" value="Submit">Submit</button>
    </p>
</form>
</body>
</html>

The user chooses a credit card type (e.g., "Mastercard"); enters the card number, expiration date, and card security code (CSC); and clicks the Submit button. However, instead of having the page dissolve and the values depart immediately for the server, the application verifies a few conditions first. The JavaScript makes sure that the fields are not blank and contain the required minimum number of characters (such as three for the CSC), and then it verifies the card number using the Luhn formula or algorithm.

The Luhn forumla is a well-known algorithm used to verify ID numbers like credit card numbers. See http://en.wikipedia.org/wiki/Luhn_formula for details.


If one of these checks fails, the hack displays an error message in red. Figure 3-5 shows one of these messages.

Figure 3-5. Time to reenter the credit card number


If the credit card number is verified and everything else has been correctly entered, the hack uses XMLHttpRequest to send this information to a server.

We are not strictly making a secure connection in this hack, but a real application would not send any purchase information unencrypted over a network. In addition, it is worth mentioning a second time that the main job of validating a credit card rests with the processing server component(s).


A message in blue notifies the user that the organization is processing the credit card.

Verifying the Card Number

cc.js contains the code for responding to the user's button click, as well as for verifying the information and generating a user message. http_request.js (see "Use Your Own Library for XMLHttpRequest" [Hack #3]) creates and calls the methods of XMLHttpRequest. Here is the code contained in cc.js:

var finalCnumber;

window.onload=function(  ){
    document.forms[0].onsubmit=function(  ){
        verify(this.cc.value,this.scode.value,this.cctype.value,
               this.exp_month.value+" "+this.exp_year.value);
        return false;
    };
}

//credit card number, security code, credit card type, and expiration date
function verify(ccard,secure_code,cctype,ccexp){
    if(secure_code.length < 3) {
        eMsg("Please enter a valid value for the security code.","red");
        return;}
    if(cctype=="Choose one...") {
        eMsg("Please enter a valid value for the credit card type.","red");
        return;}
    if (! clientsideVerify(ccard)) {
        eMsg("Please enter a valid value for the credit card.","red");}
    else{
        eMsg("Please wait while we process the credit card.","blue");
        ccard=remDashSpace(ccard);
        url="http://www.parkerriver.com/s/verify?cc="+
            encodeURIComponent(ccard)+"&scode="+
            encodeURIComponent(secure_code)+"&type="+
            encodeURIComponent(cctype)+"&exp="+
            encodeURIComponent(ccexp);
        httpRequest("GET",url,true,handleCheck);
    }
}
/* Check whether the credit card entry is null, is not lengthy enough,
or contains any letters. Remove any dashes or spaces from the entry, 
then run the Luhn algorithm on the resulting number. */
function clientsideVerify(ccVal){
    if(ccVal == null || ccVal.length < 13 ||
       ccVal.search(/[a-zA-Z]+/) != -1){ return false; }
    ccVal=remDashSpace(ccVal);
    return (applyLuhn(ccVal) % 10) == 0;

}
//http://en.wikipedia.org/wiki/Luhn_formula
function applyLuhn(cc){
    //reverse the String
    var rev = reverse(cc);
    //get array of character Strings
    var revArr = rev.split("");
    var total = 0;
    var tmp = 0;
    //add up the numbers
    for(var i = 0; i < revArr.length; i++){
        if((i % 2) > 0){
            tmp = revArr[i]*2;
            tmp= (tmp < 9 ? tmp : (tmp - 9) );
            total += tmp;
        }   else {
            total += Number(revArr[i]);
        }
    }//end for
    return total;
}
//event handler for XMLHttpRequest
function handleCheck(  ){
    var sTag,answer,xmlReturnVal;
    if(request.readyState == 4){
        if(request.status == 200){
            //implement Document object in DOM
            xmlReturnVal = request.responseXML;
            sTag = xmlReturnVal.getElementsByTagName("cc_status")[0];
            answer= sTag.childNodes[0].data;
            if(answer=="okay"){ 
                eMsg("Your purchase information has"+
                     " been submtted to our online store.","blue"); }
            else { 
                eMsg("There was a problem with processing "+
                     "the credit card.","red"); }
        } else {
            alert("A problem occurred with communicating "+
                  "between the XMLHttpRequest object and the server program.
            ");
        }
    }//end outer if
}
/* Utility functions:
reverse a string. */
function reverse(str){

    var sArray = str.split("");
    var newS="";
    for(var i = sArray.length-1; i >= 0; i--){
        newS += sArray[i];
    }
    return newS;
}
//generate a styled message
function eMsg(msg,sColor){
    var div = document.getElementById("message");
    div.style.color=sColor;
    div.style.fontSize="0.9em";
    //remove old messages
    if(div.hasChildNodes(  )){
        div.removeChild(div.firstChild);
    }
    div.appendChild(document.createTextNode(msg));

}
//remove dashes or spaces
function remDashSpace(_number){
    number = _number.replace(/-/g,"");
    number = _number.replace(/ /g,"");
    return _number;
}

There is a lot of functionality to absorb here, so first we will discuss the button click. When the browser completes loading the web page, this event is captured by the code window.onload. This event handler is a sensible place to set up other event handlers, because the browser is guaranteed to have finished loading any other HTML tags that might be used by these handlers. Next, the code sets up an event handler for when the user submits the form:

document.forms[0].onsubmit=function(  ){
    verify(this.cc.value,this.scode.value,this.cctype.value,
           this.exp_month.value+" "+this.exp_year.value);
    return false;
};

The form's onsubmit event handler points to a function that calls verify( ), then returns false, which effectively cancels the browser's form submission. We are using the request object to send the form values only after verifying that the submissions are valid. Let's look at the verify( ) function:

function verify(ccard,secure_code,cctype,ccexp){
    if(secure_code.length < 3) {
        eMsg("Please enter a valid value for the security code.","red");
        return;}
    if(cctype=="Choose one...") {
        eMsg("Please enter a valid value for the credit card type.","red");
        return;}
    if (! clientsideVerify(ccard)) {
        eMsg("Please enter a valid value for the credit card.","red");}
    else {
        eMsg("Please wait while we process the credit card.","blue");
        ccard=remDashSpace(ccard);
        url="http://www.parkerriver.com/s/verify?cc="+
            encodeURIComponent(ccard)+"&scode="+
            encodeURIComponent(secure_code)+"&type="+
            encodeURIComponent(cctype)+"&exp="+
            encodeURIComponent(ccexp);
        httpRequest("GET",url,true,handleCheck);
    }
}

This function includes a number of common-sense checks before it validates the credit card number using another function, clientsideVerify( ). If the latter function returns true, the code builds a URL for the server component and then uses XMLHttpRequest to send the card information.

The httpRequest( ) function is responsible for setting up XMLHttpRequest and connecting with the server. Again, this function takes four parameters:

  • The type of request, as in GET or POST

  • The URL or server web address

  • A Boolean indicating whether the request is asynchronous or not

  • The name of a function or a function literal that handles the server response

The function name should be passed in without the following parentheses, as in handleCheck. It can also be a function literal, as in

httpRequest("GET",url,true,function(  ){ //...});


The httpRequest( ) code appears in the file http_request.js (see "Use Your Own Library for XMLHttpRequest" [Hack #3]).

Shooting the Luhn

The clientsideVerify( ) function verifies that the credit card number is at least 13 characters long and does not contain any letters. If the credit card number passes these checks, the code removes any spaces or dashes from the string and calls a function that uses the Luhn formula:

function clientsideVerify(ccVal){
    if(ccVal == null || ccVal.length < 13 ||
       ccVal.search(/[a-zA-Z]+/) != -1){ return false; }
    ccVal=remDashSpace(ccVal);
    return (applyLuhn(ccVal) % 10) == 0;

}

Here is the code for the applyLuhn( ) function:

function applyLuhn(cc){
    //reverse the String
    var rev = reverse(cc);
    //get array of character Strings
    var revArr = rev.split("");
    var total = 0;
    var tmp = 0;
    //add up the numbers
    for(var i = 0; i < revArr.length; i++){
        if((i % 2) > 0){
            tmp = revArr[i]*2;
            tmp= (tmp < 9 ? tmp : (tmp - 9) );
            total += tmp;
        } else {
            total += Number(revArr[i]);
        }
    }//end for
    return total;
}

Information on the Luhn formula or algorithm is easily found on the Web, so we will not take up a lot of space describing it here.

This function takes a string of numbers, applies the formula to the numbers, and returns the sum to clientsideVerify( ). If the total can be evenly divided by 10, the credit card number is valid. Here is the piece of code from clientsideVerify( ) that makes this determination:

return (applyLuhn(ccVal) % 10) == 0;//returns true or false

The server component returns a bit of XML indicating success or failure, mimicking the processing of a purchase order (as in <cc_status>okay</cc_status>). The handleResponse( ) function generates a user message from this return value:

xmlReturnVal = request.responseXML;
sTag = xmlReturnVal.getElementsByTagName("cc_status")[0];
answer= sTag.childNodes[0].data;
if(answer=="okay"){ 
  eMsg("Your purchase information has"+
  " been submtted to our online store.","blue");  }

The eMsg( ) function is responsible for generating a styled user message in red, in the event of an error in handling the purchase information, or in blue otherwise. However, the entire process takes place backstage; the web page never refreshes, and only small parts of the user interface change as the user interacts with the application.


Previous Page
Next Page