The Authorize.Net Developer Blog

Posts from Authorize.Net employees, community members and experts about integrating with the Authorize.Net Payment Gateway: sample code, tutorials, and problem-solving techniques, just to name a few.

Validating Credit Card Information Part 1 of 3 - Credit Card Numbers

by Expert ‎11-08-2010 09:55 AM - edited ‎10-20-2011 10:02 AM (147,803 Views)

When visiting many tutorials demonstrating how to integrate payment gateways, or any payment service API for that matter, you will see examples of how to submit the user supplied data (which is assumed to be submitted by a form) to the payment gateway's API and how to receive the data back to tell if the transaction was approved or not. Authorize.Net's sample code is a perfect example of that as are tutorials I have written myself.

 

Unfortunately, while helpful in demonstrating how an API works, it fails to demonstrate a real world implementation of the API or that sample code. What the authors of those tutorials and sample code forget is that many people who are searching for pre-existing code and tutorials are doing so because they are not experienced enough in their programming language of choice to be able to implement the API on their own. This also usually indicates a lack of experience in the proper handling of user submitted data. Sending unvalidated user submitted data to an API, or a database, is a recipe for disaster if a malicious user comes along and decides they want to do bad things to a website.

 

In the case of payment gateway integrations there is another drawback. Fees are always incurred when processing an electronic payment. The merchant account provider almost always has a per transaction fee which is incurred even if a transaction is declined for any reason. Most payment gateways include a per transaction fee as well. This means if bad information is sent to the payment gateway for processing the merchant may be charged two transaction fees for a transaction that won't be approved. Even worse, declined transactions mean their customer is less likely to complete their purchase. No merchant wants to incur extra fees or lose any customers.

 

How Do I Validate?

 

Fortunately we can validate a user's input before sending it off to the payment gateway for processing. Not only does this offer the merchant an opportunity to save on processing fees and reduce the opportunity for malicious attacks to succeed, but it can enhance the user experience whenever they accidentally provide incorrect or incomplete data. Not only is it faster to catch errors ourselves before submitting the transaction to the payment gateway but it offers us an opportunity to explain their errors in a very user-friendly way to help encourage them to complete the payment process.

 

There are lots of pieces of information we can validate and they will vary by application. So we're going to stick to the basics and cover the important ones: credit card number, expiration date, and CVV numbers. All of our examples will be in PHP but they should be easily translated into other programming languages as well (if you do feel free to post it in the comments). In this blog post we're going to start off with credit card numbers. For the sake of keeping this manageable we're going to assume we are accepting only the four major credit cards: Visa, MasterCard, Discover Card, and American Express. These four credit cards make up the overwhelming vast majority of credit card purchases in the United States and make sense as a starting point. If you wish to add any other cards the code below is easily modified to do so.

 

Visa, MasterCard, and Discover Card account numbers are always 16 digits long (some Visa card account numbers were 13 digits in length but they are no longer in use) and American Express account numbers are 15 digits long. American Express cards always start with a "3" (actually, they always start with a "34" or "37"), Visa cards always start with a "4", MasterCards always start with a "5" (actually they always start with a "51-54"), and Discover Cards always start with a "6" (actually, they always start with a "6011"). Since we're only working with these four cards we only have to concern ourselves with the first digit of their account numbers since we can be 100% sure they will be consistent and accurately represent the proper credit card.

 

Let's See Some Code!

 

Using this information we can inspect a credit card number submitted by a user and see if it is the correct number of digits. We'll know how many digits to look for based on the first digit of the card number. Here's a PHP example of how we can do this:

 

 

// This function will take a credit card number and check to make sure it
// contains the right amount of digits
function validateCreditcard_number($credit_card_number)
{
    // Get the first digit
    $firstnumber = substr($card_number, 0, 1);
    // Make sure it is the correct amount of digits. Account for dashes being present.
    switch ($firstnumber)
    {
        case 3:
            if (!preg_match('/^3\d{3}[ \-]?\d{6}[ \-]?\d{5}$/', $card_number))
            {
                return 'This is not a valid American Express card number';
            }
            break;
        case 4:
            if (!preg_match('/^4\d{3}[ \-]?\d{4}[ \-]?\d{4}[ \-]?\d{4}$/', $card_number))
            {
                return 'This is not a valid Visa card number';
            }
            break;
        case 5:
            if (!preg_match('/^5\d{3}[ \-]?\d{4}[ \-]?\d{4}[ \-]?\d{4}$/', $card_number))
            {
                return 'This is not a valid MasterCard card number';
            }
            break;
        case 6:
            if (!preg_match('/^6011[ \-]?\d{4}[ \-]?\d{4}[ \-]?\d{4}$/', $card_number))
            {
                return 'This is not a valid Discover card number';
            }
            break;
        default:
            return 'This is not a valid credit card number';
    }
    return 'This is a valid credit card number';
}

// Get the user submitted credit card number
$credit_card_number = trim($POST['credit_card_number'];

// Let's see if this works:
echo validateCreditcard_number('4111-1111-1111-1111'); // This is a valid credit card number
echo validateCreditcard_number('5558-545f-1234');      // This is not a valid MasterCard card number
echo validateCreditcard_number('9876-5432-1012-3456'); // This is not a valid credit card number

 

 

Isn't There Some Credit Card Secret Sauce?

 

But what happens if some submits a fake credit card number that starts with a four and is sixteen digits long? Won't that pass the test? Yes, it will. Fortunately there is more we can do to validate a credit card to help prevent the smarter bad guys from abusing the system. We can use the Luhn Algorithm. The Luhn Algorithm is a check sum that verifies a number, in our case a credit card number, is valid and not a random number. All credit cards issued today are based on a modulus 10 algorithm and will pass the Luhn Algorithm which checks for exactly that. So, a made up credit card number will fail the Luhn Algorithm while a valid one will pass.

 

We'll add the Luhn Algorithm to our validateCreditcard_number() function to help weed out made up credit card numbers:

 

 

// This function will take a credit card number and check to make sure it
// contains the right amount of digits and uses the Luhn Algorithm to
// weed out made up numbers
function validateCreditcard_number($credit_card_number)
{
    // Get the first digit
    $firstnumber = substr($credit_card_number, 0, 1);
    // Make sure it is the correct amount of digits. Account for dashes being present.
    switch ($firstnumber)
    {
        case 3:
            if (!preg_match('/^3\d{3}[ \-]?\d{6}[ \-]?\d{5}$/', $credit_card_number))
            {
                return 'This is not a valid American Express card number';
            }
            break;
        case 4:
            if (!preg_match('/^4\d{3}[ \-]?\d{4}[ \-]?\d{4}[ \-]?\d{4}$/', $credit_card_number))
            {
                return 'This is not a valid Visa card number';
            }
            break;
        case 5:
            if (!preg_match('/^5\d{3}[ \-]?\d{4}[ \-]?\d{4}[ \-]?\d{4}$/', $credit_card_number))
            {
                return 'This is not a valid MasterCard card number';
            }
            break;
        case 6:
            if (!preg_match('/^6011[ \-]?\d{4}[ \-]?\d{4}[ \-]?\d{4}$/', $credit_card_number))
            {
                return 'This is not a valid Discover card number';
            }
            break;
        default:
            return 'This is not a valid credit card number';
    }
    // Here's where we use the Luhn Algorithm
    $credit_card_number = str_replace('-', '', $credit_card_number);
    $map = array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
                0, 2, 4, 6, 8, 1, 3, 5, 7, 9);
    $sum = 0;
    $last = strlen($credit_card_number) - 1;
    for ($i = 0; $i <= $last; $i++)
    {
        $sum += $map[$credit_card_number[$last - $i] + ($i & 1) * 10];
    }
    if ($sum % 10 != 0)
    {
        return 'This is not a valid credit card number';
    }
    // If we made it this far the credit card number is in a valid format
    return 'This is a valid credit card number';
}
echo validateCreditcard_number('4111-1111-1111-1111'); // This is a valid credit card number
echo validateCreditcard_number('4111-1111-1111-1112'); // This is not a valid credit card number
echo validateCreditcard_number('5558-545f-1234');      // This is not a valid MasterCard card number
echo validateCreditcard_number('9876-5432-1012-3456'); // This is not a valid credit card number

 

 

I have a valid credit card. Right?

 

If a credit card number passes our tests then it can be considered in a valid format. Please note: this only means the number is in a valid format and does not in any way indicate if the credit card is stolen or not, or whether the transaction will be approved or not. To determine if a credit card (or purchase in general) is possibly fraudulent other checks, both before and after the transaction is processed, need to be done (and will be the subject of a future blog post). To determine is a transaction will be approved you must process the transaction. This validation only checks to see if a credit card number is in a valid format and not obviously incorrect. This will make reporting the error to the user faster and reduce the number of unnecessary transactions the merchant has to pay for.

 

What Next?

 

Verifying the credit card number is valid before we send it off to be processed is a very handy thing to do. However, it's not the only thing we need to verify before processing a payment. We should also make sure the expiration date not only hasn't passed, but we need to make sure it is in a format Authorize.Net expects. We'll see how that's done in Part Two of this three part series.

 

 

 

---------------------------------------------------------------------------------------------------


John Conde is a certified Authorize.Net developer