PHP Tip: Validating a Credit Card

by William Steinmetz

Learn how to make sure the credit card you're accepting isn't fraudulent.

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.

function validate_cc_number($cc_number) {
   /* Validate; return value is card type if valid. */
   $false = false;
   $card_type = "";
   $card_regexes = array(
      "/^4\d{12}(\d\d\d){0,1}$/" => "visa",
      "/^5[12345]\d{14}$/"       => "mastercard",
      "/^3[47]\d{13}$/"          => "amex",
      "/^6011\d{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;

   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.

Using the Script

Just feed a string with a number to validate_cc_number() and check the return value. The only thing you should be careful about is nondigits in the string; you should take care of this with preg_replace() before running the function. Here is a snippet that runs the function on several test numbers:

$nums = array(
   "3721 0000 0000 000",
   "5500 0000 0000 0004",
   "4111 1111 1111 1111",
   "4222 2222 22222",

foreach ($nums as $num) {
   /* Remove all non-digits in card number. */
   $num = ereg_replace("[^0-9]", "", $num);

   $t = validate_cc_number($num);
   if ($t) {
      print "$num valid (type: $t).\n";
   } else {
      print "$num invalid.\n";

Hacking the Script

You can add other major credit cards if you know their format. An excellent resource for other cards is http://www.sitepoint.com/print/card-validation-class-php.

[wcphp_cov.jpg] Wicked Cool PHP
Real-World Scripts That Solve Difficult Problems
by William Steinmetz with Brian Ward
ISBN-10 1-59327-173-5
ISBN-13 978-1-59327-173-2
This article was originally published on Wednesday Jun 18th 2008
Mobile Site | Full Site