PHP Tip: Validating a Credit Card

The following is tip #30 from Wicked Cool PHP by WIlliam Steinmetz with Brian Ward. Reprinted with permission.

Here’s a brief overview of how online credit card transactions work. First, you need to find a merchant solution (an online provider, such as Authorize.net or Secpay.com) that provides you with a merchant account. This account is like a bank account, except that it allows you to process charges for credit card transactions. The merchant provider typically charges a per-transaction fee for each credit card action.

If you have a physical store that accepts credit cards, you almost certainly have a merchant solution. However, not all merchant solutions offer online transactions. The ones that do offer online transactions give you access to a payment gateway, a secure server for processing credit card charges. Usually, the transactions occur via an XML datastream. You can use cURL to exchange XML with the payment gateway (see Chapter 11 of Wicked Cool PHP for more details).

However, you can do some preliminary form validation work before talking to the payment gateway to save on transactions and transaction fees and possibly speed things for the user if they typed their credit card number incorrectly. It turns out that you can weed out completely incorrect credit card numbers with an easy algorithm. Furthermore, you can even determine a credit card type from a valid number. Keep in mind, though, that passing these tests is no guarantee that a card isn’t stolen or canceled or that it belongs to a different person.


<?php
function validate_cc_number($cc_number) {
   /* Validate; return value is card type if valid. */
   $false = false;
   $card_type = "";
   $card_regexes = array(
      "/^4d{12}(ddd){0,1}$/" => "visa",
      "/^5[12345]d{14}$/"       => "mastercard",
      "/^3[47]d{13}$/"          => "amex",
      "/^6011d{12}$/"           => "discover",
      "/^30[012345]d{11}$/"     => "diners",
      "/^3[68]d{12}$/"          => "diners",
   );

   foreach ($card_regexes as $regex => $type) {
       if (preg_match($regex, $cc_number)) {
           $card_type = $type;
           break;
       }
   }

   if (!$card_type) {
       return $false;
   }

   /*  mod 10 checksum algorithm  */
   $revcode = strrev($cc_number);
   $checksum = 0;

   for ($i = 0; $i < strlen($revcode); $i++) {
       $current_num = intval($revcode[$i]);
       if($i & 1) {  /* Odd  position */
          $current_num *= 2;
       }
       /* Split digits and add. */
           $checksum += $current_num % 10; if
       ($current_num >  9) {
           $checksum += 1;
       }
   }

   if ($checksum % 10 == 0) {
       return $card_type;
   } else {
       return $false;
   }
}

?>

This function has two main stages. The first determines card type, and the second determines whether the card checksum is correct. If the card passes both tests, the return value is the card type as a string. If a card is invalid, you get false (you can change this return value to whatever you like with the $false variable).

The first stage is where the big trick comes in, where we determine the card type and confirm the prefix in one quick step. Credit card numbers follow a certain format. For example, all Visas start with 4 and have 13 or 16 digits, all MasterCards start with 51 through 55 and have 16 digits, and all American Express cards start with 34 or 37 and have 15 digits. These rules are easily expressed in a few regular expressions, and because they are unique rules, we can map the regular expressions to card types in an array called $card_regexes. To check for a valid format, we just cycle through the regular expressions until one matches. When we get a match, we set $card_type and move to the next stage. If no expressions match, we return failure.

The checksum test for the credit card number uses a mod 10 algorithm, a reasonably simple-to-implement check that does the following:


  • It starts with a checksum value of 0.

  • It runs through the credit card number digit-by-digit from right to left.

  • If the current digit has an odd index (that is, every other digit, starting at index 0), the digit is doubled. If the value of the doubled digit is over 9, the two numbers are added together and added to the checksum (so an 8 becomes 16, which becomes 1 + 6, which becomes 7). Otherwise the current (doubled if on an odd index) digit is added to the checksum.

  • After running through all the digits, the final checksum must be divisible by 10. If not, the number fails the test.

There are several ways to code this algorithm; the implementation here is on the compact side, but easy enough to follow.

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read