[Okta Webinar] Learn how to a build a cloud-first strategyRegister Now

x
?
Solved

php credit card validation

Posted on 2005-04-13
5
Medium Priority
?
420 Views
Last Modified: 2013-11-18
Hi there i hope u can help me with this problem. I am using the following php code to validate credit card numbers using the mod10 algorithim but all the credit card numbers are coming back as invalid even if they are valid.

<?php

define("CARD_TYPE_MC", 0);
define("CARD_TYPE_VS", 1);
define("CARD_TYPE_AX", 2);
define("CARD_TYPE_DC", 3);
define("CARD_TYPE_DS", 4);
define("CARD_TYPE_JC", 5);

class CCreditCard
{
// Class Members
var $__ccName = '';
var $__ccType = '';
var $__ccNum = '';
var $__ccExpM = 0;
var $__ccExpY = 0;

// Constructor
function CCreditCard($name, $type, $num, $expm, $expy)
{

// Set member variables
if(!empty($name))
{
$this->__ccName = $name;
}
else
{
die('Must pass name to constructor');
}

// Make sure card type is valid
switch(strtolower($type))
{
  case 'mc':
  case 'mastercard':
  case 'm':
  case '1':
    $this->__ccType = CARD_TYPE_MC;
    break;
  case 'vs':
  case 'visa':
  case 'v':
  case '2':
    $this->__ccType = CARD_TYPE_VS;
    break;
  case 'ax':
  case 'american express':
  case 'a':
  case '3':
    $this->__ccType = CARD_TYPE_AX;
    break;
  case 'dc':
  case 'diners club':
  case '4':
    $this->__ccType = CARD_TYPE_DC;
    break;
  case 'ds':
  case 'discover':
  case '5':
    $this->__ccType = CARD_TYPE_DS;
    break;
  case 'jc':
  case 'jcb':
  case '6':
    $this->__ccType = CARD_TYPE_JC;
    break;
  default:
    die('Invalid type ' . $type . ' passed to constructor');
}

// Don't check the number yet,
// just kill all non numerics
if(!empty($num))
{
  $cardNumber = ereg_replace("[^0-9]", "", $num);

  // Make sure the card number isnt empty
  if(!empty($cardNumber))
  {
    $this->__ccNum = $cardNumber;
  }
  else
  {
    die('Must pass number to constructor');
  }
}
else
{
  die('Must pass number to constructor');
}

if(!is_numeric($expm) || $expm < 1 || $expm > 12)
{
  die('Invalid expiry month of ' . $expm . ' passed to constructor');
}
else
{
  $this->__ccExpM = $expm;
}

// Get the current year
$currentYear = date('Y');
settype($currentYear, 'integer');

if(!is_numeric($expy) || $expy < $currentYear || $expy
> $currentYear + 10)
{
  die('Invalid expiry year of ' . $expy . ' passed to constructor');
}
else
{
  $this->__ccExpY = $expy;
}
}

function Name()
{
  return $this->__ccName;
}

function Type()
{
  switch($this->__ccType)
    {
    case CARD_TYPE_MC:
      return 'mastercard [1]';
      break;
    case CARD_TYPE_VS:
      return 'Visa [2]';
      break;
    case CARD_TYPE_AX:
      return 'Amex [3]';
      break;
    case CARD_TYPE_DC:
      return 'Diners Club [4]';
      break;
    case CARD_TYPE_DS:
      return 'Discover [5]';
      break;
    case CARD_TYPE_JC:
      return 'JCB [6]';
      break;
    default:
      return 'Unknown [-1]';
  }
}

function Number()
{
  return $this->__ccNum;
}

function ExpiryMonth()
{
  return $this->__ccExpM;
}

function ExpiryYear()
{
  return $this->__ccExpY;
}

function SafeNumber($char = 'x', $numToHide = 4)
{
  // Return only part of the number
  if($numToHide < 4)
  {
    $numToHide = 4;
  }

  if($numToHide > 10)
  {
    $numToHide = 10;
  }

  $cardNumber = $this->__ccNum;
  $cardNumber = substr($cardNumber, 0, strlen($cardNumber) - $numToHide);

  for($i = 0; $i < $numToHide; $i++)
  {
    $cardNumber .= $char;
  }

  return $cardNumber;
}

function IsValid()
{
  // Not valid by default
  $validFormat = false;
  $passCheck = false;

// Is the number in the correct format?
switch($this->__ccType)
{
  case CARD_TYPE_MC:
    $validFormat = ereg("^5[1-5][0-9]{14}$", $this->__ccNum);
    break;
case CARD_TYPE_VS:
    $validFormat = ereg("^4[0-9]{12}([0-9]{3})?$", $this->__ccNum);
    break;
case CARD_TYPE_AX:
    $validFormat = ereg("^3[47][0-9]{13}$", $this->__ccNum);
    break;
case CARD_TYPE_DC:
    $validFormat = ereg("^3(0[0-5]|[68][0-9])[0-9]{11}$", $this->__ccNum);
    break;
case CARD_TYPE_DS:
    $validFormat = ereg("^6011[0-9]{12}$", $this->__ccNum);
    break;
case CARD_TYPE_JC:
    $validFormat = ereg("^(3[0-9]{4}|2131|1800)[0-9]{11}$", $this->__ccNum);
    break;
  default:
  // Should never be executed
  $validFormat = false;

// Is the number valid?
$cardNumber = strrev($this->__ccNum);
$numSum = 0;

for($i = 0; $i < strlen($cardNumber); $i++)
{
  $currentNum = substr($cardNumber, $i, 1);

// Double every second digit
if($i % 2 == 1)
{
  $currentNum *= 2;
}

// Add digits of 2-digit numbers together
if($currentNum > 9)
{
  $firstNum = $currentNum % 10;
  $secondNum = ($currentNum - $firstNum) / 10;
  $currentNum = $firstNum + $secondNum;
}

$numSum += $currentNum;
}

// If the total has no remainder it's OK
$passCheck = ($numSum % 10 == 0);

 if($validFormat && $passCheck) return true;
  else return false;
 }
 }
 }
?>





0
Comment
Question by:acslater
  • 2
  • 2
5 Comments
 
LVL 7

Expert Comment

by:Promethyl
ID: 13775074
function validate_cc($argNum)
{
    $cc = str_replace(" ", "", $argNum);
    $prefix = substr($cc, 0, 1);
   
    /* PREFIX TEST */
    switch ($prefix) {
        case "1":
            if (substr($cc, 0, 4) == "1800") {
                $valid = "jcb1";
            } else {
                $valid = false;
            }
            break;
        case "2":
            if (substr($cc, 0, 4) == "2014" || substr($cc, 0, 4) == "2149")    {
                $valid = "enRoute";
            } elseif (substr($cc, 0, 4) == "2131") {
                $valid = "jcb2";
            } else {
                $valid = false;
            }
            break;
        case "3":
            if (substr($cc, 0, 3) > 299 && substr($cc, 0, 3) < 306) {
                $valid = "diners";
            } elseif (substr($cc, 0, 2) == 36 || substr($cc, 0, 2) == 38) {
                $valid = "diners";
            } elseif (substr($cc, 0, 2) == 34 || substr($cc, 0, 2) == 37) {
                $valid = "amex";
            } else {
                $valid = "jcb3";
            }
            break;
        case "4":
            $valid = "visa";
            break;
        case "5":
            if (substr($cc, 0, 2) > 50 && substr($cc, 0, 2) < 56) {
                $valid = "mc";
            }
            break;
        case "6":
            if (substr($cc, 0, 4) == "6011") {
                $valid = "discover";
            } else {
                $valid = false;
            }
            break;
    }

    /* LENGTH TEST */
    if ($valid) {
        switch ($valid) {
            case "mc":
            case "disocver":
            case "jcb3":
                if (strlen($cc) <> 16) {
                    $valid = false;
                }
                break;
            case "amex":
            case "enRoute":
            case "jcb1":
            case "jcb2":
                if (strlen($cc) <> 15) {
                    $valid = false;
                }
                break;
            case "diners":
                if (strlen($cc) <> 14) {
                    $valid = false;
                }
           
                break;
            case "visa":
                if (strlen($cc) != 13 || strlen($cc) != 16)    {
                    $valid = false;
                }
                break;
        }
    }

    /* MOD10 TEST */
    if ($valid)    {
        switch ($valid)    {
            case "mc":
            case "visa":
            case "amex":
            case "diners":
            case "discover":
            case "jcb1":
            case "jcb2":
            case "jcb3":
                for ($i = strlen($cc) - 2; $i >= 0; $i = $i - 2) {
                    $odds[] = (int) $cc{$i};
                    $cc{$i} = "%";
                }
                $cc = str_replace("%", "", $cc);

                foreach ($odds as $key => $value) {
                    $oddnumber = $value * 2;
                    $i = 0;
                    $oddnumber = (string) $oddnumber;
                    do {
                        $total = $total + (int) $oddnumber{$i};
                        $i++;
                    } while ($oddnumber{$i});
                    $numbers[] = (int) $total;
                    unset($total);
                }
                $i = 0;

                while ($i <= (strlen($cc) - 1)) {
                    $numbers[] = (int) $cc{$i};
                    $i++;
                }

                foreach ($numbers as $key => $value) {
                    $total = $total + (int) $value;
                }
               
                if ($total % 10 !== 0)    {
                    $valid = false;
                }
            break;
        }
    }
   
    if ($valid == "jcb1" || $valid == "jcb2" || $valid == "jcb3")
    {
        $valid = "jcb";
    }
    return $valid;
}
0
 

Author Comment

by:acslater
ID: 13775206
thanks for that put could you tell me exactly where to put that code in terms of my code
0
 
LVL 7

Expert Comment

by:Promethyl
ID: 13775245
Anywhere. <G>

Functions can be defined before or after the invocation. If possible, I'd prefer to put it at the top, but for readability, if you want to put it at the bottom of your program, or elsewhere, be my guest.

To invoke:
$cctype = validate_cc('5nnn1nnn1nnn1nnn'); // where the n's are the appropriate card number to test.

if (!$cctype) {
     echo "Please reenter your credit card number."; $carderror=1;
}
0
 
LVL 6

Expert Comment

by:alextr2003fr
ID: 13775781
1) notice that this cold is made of older versions of php, so it had to be rewriten
2) luhn (mod 10) should never be used only by itself it prove the credit card validity, you have to also check :
- you have to check the logo on it
- if the card can be debited
- if the card is not blacklisted
3) here is your correct code
NB: I am not responsable for it's use or anything, it is just an example.
corrected : $_POST variables, switch declaration had an error with wrong { }, added values to the form



<?php



define("CARD_TYPE_MC", 0);
define("CARD_TYPE_VS", 1);
define("CARD_TYPE_AX", 2);
define("CARD_TYPE_DC", 3);
define("CARD_TYPE_DS", 4);
define("CARD_TYPE_JC", 5);

class CCreditCard
{
// Class Members
var $__ccName = '';
var $__ccType = '';
var $__ccNum = '';
var $__ccExpM = 0;
var $__ccExpY = 0;

// Constructor
function CCreditCard($name, $type, $num, $expm, $expy)
{

// Set member variables
if(!empty($name))
{
$this->__ccName = $name;
}
else
{
die('Must pass name to constructor');
}

// Make sure card type is valid
switch(strtolower($type))
{
  case 'mc':
  case 'mastercard':
  case 'm':
  case '1':
    $this->__ccType = CARD_TYPE_MC;
    break;
  case 'vs':
  case 'visa':
  case 'v':
  case '2':
    $this->__ccType = CARD_TYPE_VS;
    break;
  case 'ax':
  case 'american express':
  case 'a':
  case '3':
    $this->__ccType = CARD_TYPE_AX;
    break;
  case 'dc':
  case 'diners club':
  case '4':
    $this->__ccType = CARD_TYPE_DC;
    break;
  case 'ds':
  case 'discover':
  case '5':
    $this->__ccType = CARD_TYPE_DS;
    break;
  case 'jc':
  case 'jcb':
  case '6':
    $this->__ccType = CARD_TYPE_JC;
    break;
  default:
    die('Invalid type ' . $type . ' passed to constructor');
}

// Don't check the number yet,
// just kill all non numerics
if(!empty($num))
{
  $cardNumber = ereg_replace("[^0-9]", "", $num);
  // Make sure the card number isnt empty
  if(!empty($cardNumber))
  {
    $this->__ccNum = $cardNumber;
  }
  else
  {
    die('Must pass number to constructor');
  }
}
else
{
  die('Must pass number to constructor');
}

if(!is_numeric($expm) || $expm < 1 || $expm > 12)
{
  die('Invalid expiry month of ' . $expm . ' passed to constructor');
}
else
{
  $this->__ccExpM = $expm;
}

// Get the current year
$currentYear = date('Y');
settype($currentYear, 'integer');


if((!is_numeric($expy)) || ($expy < $currentYear) || ($expy > $currentYear + 10))
{
  die('Invalid expiry year of ' . $expy . ' passed to constructor');
}
else
{
  $this->__ccExpY = $expy;
}
}

function Name()
{
  return $this->__ccName;
}

function Type()
{
  switch($this->__ccType)
    {
    case CARD_TYPE_MC:
      return 'Mastercard [1]';
      break;
    case CARD_TYPE_VS:
      return 'Visa [2]';
      break;
    case CARD_TYPE_AX:
      return 'Amex [3]';
      break;
    case CARD_TYPE_DC:
      return 'Diners Club [4]';
      break;
    case CARD_TYPE_DS:
      return 'Discover [5]';
      break;
    case CARD_TYPE_JC:
      return 'JCB [6]';
      break;
    default:
      return 'Unknown [-1]';
  }
}

function Number()
{
  return $this->__ccNum;
}

function ExpiryMonth()
{
  return $this->__ccExpM;
}

function ExpiryYear()
{
  return $this->__ccExpY;
}

function SafeNumber($char = 'x', $numToHide = 4)
{
  // Return only part of the number
  if($numToHide < 4)
  {
    $numToHide = 4;
  }

  if($numToHide > 10)
  {
    $numToHide = 10;
  }

  $cardNumber = $this->__ccNum;
  $cardNumber = substr($cardNumber, 0, strlen($cardNumber) - $numToHide);

  for($i = 0; $i < $numToHide; $i++)
  {
    $cardNumber .= $char;
  }

  return $cardNumber;
}

function IsValid()
{
  // Not valid by default
  $validFormat = false;
  $passCheck = false;
// Is the number in the correct format?
switch($this->__ccType)
{
  case CARD_TYPE_MC:
    $validFormat = ereg("^5[1-5][0-9]{14}$", $this->__ccNum);
    break;
case CARD_TYPE_VS:
    $validFormat = ereg("^4[0-9]{12}([0-9]{3})?$", $this->__ccNum);
    break;
case CARD_TYPE_AX:
    $validFormat = ereg("^3[47][0-9]{13}$", $this->__ccNum);
    break;
case CARD_TYPE_DC:
    $validFormat = ereg("^3(0[0-5]|[68][0-9])[0-9]{11}$", $this->__ccNum);
    break;
case CARD_TYPE_DS:
    $validFormat = ereg("^6011[0-9]{12}$", $this->__ccNum);
    break;
case CARD_TYPE_JC:
    $validFormat = ereg("^(3[0-9]{4}|2131|1800)[0-9]{11}$", $this->__ccNum);
    break;
  default:
  // Should never be executed
  $validFormat = false;
}
// Is the number valid?
$cardNumber = strrev($this->__ccNum);
$numSum = 0;

for($i = 0; $i < strlen($cardNumber); $i++)
{
  $currentNum = substr($cardNumber, $i, 1);

// Double every second digit
if($i % 2 == 1)
{
  $currentNum *= 2;
}

// Add digits of 2-digit numbers together
if($currentNum > 9)
{
  $firstNum = $currentNum % 10;
  $secondNum = ($currentNum - $firstNum) / 10;
  $currentNum = $firstNum + $secondNum;
}

$numSum += $currentNum;
}

// If the total has no remainder it's OK
$passCheck = ($numSum % 10 == 0);
echo '<h2>validform</h2>'.$validFormat.'<br>';
 if($validFormat && $passCheck) {return true;} else {return false;}
 }
 }
?>


<?php
//corrected
if(!isset($_POST['submit']))
{
?>

  <h2>Validate Credit Card</h2>
  <form name="frmCC" action="" method="post">
  Numbers used only for example!<br>
  Cardholders name: <input type="text" name="ccName" value="Card Holder"><br>
  Card number: <input type="text" name="ccNum" value="5500 0000 0000 0004"><br>
  Card type: <select name="ccType">
  <option value="1">Mastercard</option>
  <option value="2">Visa</option>
  <option value="3">Amex</option>
  <option value="4">Diners</option>
  <option value="5">Discover</option>
  <option value="6">JCB</option>
  </select><br>

  Expiry Date: <select name="ccExpM">

  <?php

    for($i = 1; $i < 13; $i++)
    { echo '<option>' . $i . '</option>'; }

  ?>

  </select>

  <select name="ccExpY">

  <?php

    for($i = 2006; $i < 2010; $i++)
    { echo '<option value="'.$i.'">' . $i . '</option>'; }

  ?>

  </select><br><br>

  <input type="submit" name="submit" value="Validate">
  </form>

  <?

  }
  else
  {
  // Check if the card is valid
  //corrected
  $cc = new CCreditCard($_POST['ccName'], $_POST['ccType'], $_POST['ccNum'], $_POST['ccExpM'], $_POST['ccExpY']);

  ?>
  <h2>Validation Results</h2>
  <b>Name: </b><?=$cc->Name(); ?><br>
  <b>Number: </b><?=$cc->SafeNumber('x', 6); ?><br>
  <b>Type: </b><?=$cc->Type(); ?><br>
  <b>Expires: </b><?=$cc->ExpiryMonth() . '/' .
  $cc->ExpiryYear(); ?><br><br>

  <?php

    echo '<font color="blue" size="2"><b>';
    if($cc->IsValid()) {echo 'VALID CARD';} else {echo 'INVALID CARD';}
    echo '</b></font>';
  }
?>
0
 
LVL 6

Accepted Solution

by:
alextr2003fr earned 2000 total points
ID: 13775815
you can remove the line 247 : echo '<h2>validform</h2>'.$validFormat.'<br>';
I used it to test the results there was a bug somewhere in the switch
0

Featured Post

Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Businesses who process credit card payments have to adhere to PCI Compliance standards. Here’s why that’s important.
Many old projects have bad code, but the budget doesn't exist to rewrite the codebase. You can update this code to be safer by introducing contemporary input validation, sanitation, and safer database queries.
Explain concepts important to validation of email addresses with regular expressions. Applies to most languages/tools that uses regular expressions. Consider email address RFCs: Look at HTML5 form input element (with type=email) regex pattern: T…
This video explains how to create simple products associated to Magento configurable product and offers fast way of their generation with Store Manager for Magento tool.
Suggested Courses
Course of the Month19 days, 2 hours left to enroll

834 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question