* @link http://www.terraaccess.com Terra Access * * @package TA_AuthNet * @version 1.3 * * @link http://php.inc.ru/main.php?view=tutorials&t=p_cc The PHP Guy Article * @access public */ //Credit Card Types define ( "CC_AMERICAN_EXPRESS", 1 ); define ( "CC_DINERS_CLUB", 2 ); define ( "CC_DISCOVER", 3 ); define ( "CC_JB", 4 ); define ( "CC_MASTER_CARD", 5 ); define ( "CC_VISA", 6 ); //Success Codes define ( "CC_SUCCESS", 0 ); //Error Codes define ( "ERR_FAILURE", -1 ); define ( "ERR_INVALID_EXPIRATION", -2 ); define ( "ERR_INVALID_LENGTH", -3 ); define ( "ERR_INVALID_MOD10", -4 ); define ( "ERR_INVALID_PREFIX", -5 ); define ( "ERR_NOT_NUMERIC", -6 ); define ( "ERR_UNKNOWN", -7 ); class Validate_Credit_Card { /** * Validate_Credit_Card::$error_text * * Converts Error Codes to Readable Text * * @var array * @access public */ var $error_text = array ( ERR_INVALID_MOD10 => "Failed the Mod10 test." ,ERR_INVALID_PREFIX => "Card Number Has Invalid Prefix" ,ERR_INVALID_LENGTH => "Card Number is not the Right Length" ,ERR_NOT_NUMERIC => "Card Number Must be all Numbers" ,ERR_INVALID_EXPIRATION => "Invalid Expiration Date" ); /** * Validate_Credit_Card::$credit_card_name * * Converts Credit Card defines to Readable Text * * @var array * @access public */ var $credit_card_name = array ( CC_AMERICAN_EXPRESS => "American Express" ,CC_DINERS_CLUB => "Diners Club" ,CC_DISCOVER => "Discover" ,CC_JB => "JB" ,CC_MASTER_CARD => "Master Card" ,CC_VISA => "Visa" ); /** * Validate_Credit_Card::$cards_prefix * * Stores Valid Prefixes for Credit Cards * * @var array * @access public */ var $cards_prefix = array ( CC_AMERICAN_EXPRESS => array ( 34, 37 ) ,CC_DINERS_CLUB => array ( 300, 301, 302, 303, 304, 305, 36, 38 ) ,CC_DISCOVER => array ( 6011 ) ,CC_JB => array ( 3, 1800, 2131 ) ,CC_MASTER_CARD => array ( 51, 52, 53, 54, 55 ) ,CC_VISA => array ( 4 ) ); /** * Validate_Credit_Card::$cards_length * * Stores Valid Lengths for Credit Cards * * @var array * @access public */ var $cards_length = array ( CC_AMERICAN_EXPRESS => array ( 15 ) ,CC_DINERS_CLUB => array ( 14 ) ,CC_DISCOVER => array ( 16 ) ,CC_JB => array ( 15, 16 ) ,CC_MASTER_CARD => array ( 16 ) ,CC_VISA => array ( 13, 16 ) ); /** * Validate_Credit_Card::$cc_type * * Stores Credit Card Type for last Number ran * * @var integer * @access public */ var $cc_type; /** * Validate_Credit_Card::Validate_Credit_Card() * * Constructor * */ function Validate_Credit_Card () { $this->cc_type = ERR_UNKNOWN; } /** * Validate_Credit_Card::is_valid_card() * * Validates credit card number and expiration date * * @param string $card_number * @param string $exp_month * @param string $exp_year * @return integer CC_SUCCESS or error code * * @see is_valid_number() * @see is_valid_expiration() * * @access public */ function is_valid_card ( $card_number, $exp_month, $exp_year ) { $ret = $this->is_valid_number ( $card_number ); if ( $ret != CC_SUCCESS ) { return $ret; } $ret = $this->is_valid_expiration ( $exp_month, $exp_year ); if ($ret != CC_SUCCESS ) { return $ret; } return CC_SUCCESS; } /** * Validate_Credit_Card::is_valid_number() * * Calls internal functions to validate card number * * @param string $card_number Credit Card Number * @param integer $card_type Credit Card Company as defines above * @return integer ERR_UNKNOWN CC_SUCCESS CC_FAILURE * * @see get_card_type() * @see mod10() * @see match_prefix() * @see match_lenght() * @see $cc_type * @access public */ function is_valid_number ( $card_number, $card_type = ERR_UNKNOWN ) { //if $card_type is unknown then try and get it if ( $card_type == ERR_UNKNOWN ) { $card_type = $this->get_card_type ( $card_number ); //if card type is still unknown then //we have a bad credit card number if ( $card_type == ERR_UNKNOWN ) { return ERR_UNKNOWN; } } //set the global variable to the credit card //type we are now working on $this->cc_type = $card_type; //check the mod10 value of the credit card number if ( $this->mod10 ( $card_number ) != CC_SUCCESS ) { return ERR_INVALID_MOD10; } //ensure the prefix is valid for the type of card //this must be done before checking the credit card //number length as it requires a card type to check //against if ( $this->match_prefix ( $card_number ) != $card_type ) { return ERR_INVALID_PREFIX; } //ensure the length of the number is valid for the type of card if ( $this->match_length ( $card_number, $card_type ) != $card_type ) { return ERR_INVALID_LENGTH; } //passed all tests return CC_SUCCESS; } /** * Validate_Credit_Card::is_valid_expiration() * * Attempts to validate the expiration date * of the credit card. Accepts number parameters only. * $year can be in either 4 digit format (2003) or * 2 digit format (03) * * @param string $month 2 (01) or 1 (1) digit format * @param string $year 2 (03) or 4 (2003) digit format * @return integer ERR_INVALID_EXPIRATION or CC_SUCCESS * * @access public * */ function is_valid_expiration ( $month, $year ) { //if the month or year are not numeric...skipit if ( ( !is_numeric ( $month ) ) || ( !is_numeric ( $year ) ) ) { return ERR_INVALID_EXPIRATION; } //set the current year to the year format sent in //and convert to integer //if length is 2 (03) or 4 (2003) else...skipit if ( strlen ( $year ) == 4 ) { $current_year = (integer) date ( 'Y' ); } elseif ( strlen ( $year ) == 2 ) { $current_year = (integer) date ( 'y' ); }else{ return ERR_INVALID_EXPIRATION; } //convert the curent month to integer $current_month = (integer) date ( 'm' ); //valid values for month are 1 thru 12 if ( ( $month < 1 ) || ( $month > 12 ) ) { return ERR_INVALID_EXPIRATION; } //if the year passed in is before the current year of //more than 10 years out...skipit if ( ( $year < $current_year ) || ( $year > ( $current_year + 10 ) ) ) { return ERR_INVALID_EXPIRATION; } //if the year passed in is the same as the current year //and the month is before the current month...skipit if ( ( $year == $current_year ) && ( $month < $current_month ) ) { return ERR_INVALID_EXPIRATION; } //passed all tests, expiration date is ok return CC_SUCCESS; } /** * Validate_Credit_Card::mod10() * * Uses mod10 alogorithm as described in The PHP Guy article * Steps: * 1. Reverse the number * 2. Multiply the even placed digits by 2 * 3. If the result is more than one digit then * add the digits together * 4. Add the odd placed digits in the credit card number together * 5. Add the result of #3 and #4 above * 6. If the modulas 10 of the result equals 0 then * the number is valid * * * @param string $card_number Credit Card Number * @return integer ERR_NOT_NUMERIC ERR_INVALID_MOD10 CC_SUCCESS * * @access public */ function mod10 ( $card_number ) { $digit_array = array (); $cnt = 0; //Check to make sure card number is numeric:)) if ( !is_numeric ( $card_number ) ) { return ERR_NOT_NUMERIC; } //Reverse the card number $card_temp = strrev ( $card_number ); //Multiple every other number by 2 then ( even placement ) //Add the digits and place in an array for ( $i = 1; $i <= strlen ( $card_temp ) - 1; $i = $i + 2 ) { //multiply every other digit by 2 $t = substr ( $card_temp, $i, 1 ); $t = $t * 2; //if there are more than one digit in the //result of multipling by two ex: 7 * 2 = 14 //then add the two digits together ex: 1 + 4 = 5 if ( strlen ( $t ) > 1 ) { //add the digits together $tmp = 0; //loop through the digits that resulted of //the multiplication by two above and add them //together for ( $s = 0; $s < strlen ( $t ); $s++ ) { $tmp = substr ( $t, $s, 1 ) + $tmp; } }else{ // result of (* 2) is only one digit long $tmp = $t; } //place the result in an array for later //adding to the odd digits in the credit card number $digit_array [ $cnt++ ] = $tmp; } $tmp = 0; //Add the numbers not doubled earlier ( odd placement ) for ( $i = 0; $i <= strlen ( $card_temp ); $i = $i + 2 ) { $tmp = substr ( $card_temp, $i, 1 ) + $tmp; } //Add the earlier doubled and digit-added numbers to the result $result = $tmp + array_sum ( $digit_array ); //Check to make sure that the remainder //of dividing by 10 is 0 by using the modulas //operator if ( $result % 10 == 0 ) { return CC_SUCCESS; }else{ return ERR_INVALID_MOD10; } } /** * Validate_Credit_Card::get_card_type() * * Returns the card type based on credit card number * Has to match prefix and then length much match based * on prefix match * * @param string $card_number Credit Card Number * @return integer ERR_UNKNOWN or integer that matches defines for Credit Cards * * @see match_prefix() * @see match_length() * @access public * */ function get_card_type ( $card_number ) { //try and match up the card number against //know valid prefix values $prefix_ret = $this->match_prefix ( $card_number ); if ( $prefix_ret == ERR_UNKNOWN ) { return ERR_UNKNOWN; } //using the card number and card type check to make sure //the card number is a valid length $length_ret = $this->match_length ( $card_number, $prefix_ret ); //if the card type returned by both functions is //the same then we have a valid credit card number if ( $length_ret == $prefix_ret ) { //set the internal global card type to //the current card number type and return it $this->cc_type = $prefix_ret; return $prefix_ret; } //set the internal global card type to //unknown and return that fact $this->cc_type = ERR_UNKNOWN; return ERR_UNKNOWN; } /** * Validate_Credit_Card::match_prefix() * * Finds Credit Card Type base on prefix match alone * * @param string $card_number * @return integer ERR_UNKNOWN or integer that matches defines for Credit Cards * * @access public */ function match_prefix ( $card_number ) { //initialize to first card type $tmp_type = 1; //loop through the card types foreach ( $this->cards_prefix as $card_array ) { //loop through the valid prefixes for each card //type until a match is found foreach ( $card_array as $prefix ) { //get the numbers from the card number that //match the length of the valid prefix $tmp = substr ( $card_number, 0, strlen ( $prefix ) ); //if the retrieved card numbers match the //valid prefix return the card type matched if ( $tmp == $prefix ) { return $tmp_type; } } //move on to the next card type $tmp_type++; } //sorry we didn't find any prefixes that //match your card number return ERR_UNKNOWN; } /** * Validate_Credit_Card::match_length() * * Ensures the length of the credit card number matches * the published allowed lengths. I use this just to verify * once I have the Card Type * * @param string $card_number * @return integer ERR_UNKNOWN or integer that matches defines for Credit Cards * * @access public */ function match_length ( $card_number, $card_type ) { //get card number length $tmp_length = strlen ( $card_number ); //set the working array to the card type passed in $card_array = $this->cards_length [ $card_type ]; //loop through the valid lengths for specified card //type until a match is found foreach ( $card_array as $valid_length ) { //if the retrieved length matches the length //of the card number passed in then return card type if ( $tmp_length == $valid_length ) { return $card_type; } } //sorry we didn't find any lengths in the //card type you specified that match the //credit card number length return ERR_UNKNOWN; } /** * Validate_Credit_Card::get_error_text() * * Outputs text for Error Codes Listed in defines * * @param integer $errno * @return string Error Text * * @see $error_text * @access public */ function get_error_text ( $errno ) { if ( isset ( $this->error_text [ $errno ] ) ) { return $this->error_text [ $errno ]; }else{ return "Invalid Error Code"; } } /** * Validate_Credit_Card::get_credit_card_name() * * Returns Proper Name for Last Credit Card Validated * * @return string Credit Card Company Name * * @see $credit_card_name * @access public */ function get_credit_card_name () { return $this->credit_card_name [ $this->cc_type ]; } } //END CLASS Validate_Credit_Card ?>