![]() |
|
Snippets |
|
Here is another variation on the credit card validator. In your validate/[action].yml file, you can implement this helper like so:
fields:
cc_type:
required:
msg: Please select a card type
sfStringValidator:
values: [Visa, MasterCard, Discover, American Express]
values_error: Please select a credit card type
insensitive: true
cc_number:
required:
msg: Please provide a credit card number
myCreditCardValidator:
card_name: cc_type # refers to field name in form that contains card type, like Visa, MasteCard, etc.
Place this file, myCreditCardValidator.class.php, in you application's /lib directory and clear the cache.
<?php /** * This class has been converted to a Symfony Validator from original code * created by John Gardner, 4th January 2005. * http://www.braemoor.co.uk/software/index.shtml * * Symfony conversion by Scott Meves, Stereo Interactive & Design, 2007 * http://www.stereointeractive.com * * This routine checks the credit card number. The following checks are made: * * 1. A number has been provided * 2. The number is a right length for the card * 3. The number has an appropriate prefix for the card * 4. The number has a valid modulus 10 number check digit if required * **/ class myCreditCardValidator extends sfValidator { static protected $CARDS = array ( array ('name' => 'American Express', 'length' => '15', 'prefixes' => '34,37', 'checkdigit' => true ), array ('name' => 'Carte Blanche', 'length' => '14', 'prefixes' => '300,301,302,303,304,305,36,38', 'checkdigit' => true ), array ('name' => 'Diners Club', 'length' => '14', 'prefixes' => '300,301,302,303,304,305,36,38', 'checkdigit' => true ), array ('name' => 'Discover', 'length' => '16', 'prefixes' => '6011', 'checkdigit' => true ), array ('name' => 'Enroute', 'length' => '15', 'prefixes' => '2014,2149', 'checkdigit' => true ), array ('name' => 'JCB', 'length' => '15,16', 'prefixes' => '3,1800,2131', 'checkdigit' => true ), array ('name' => 'Maestro', 'length' => '16', 'prefixes' => '5020,6', 'checkdigit' => true ), array ('name' => 'MasterCard', 'length' => '16', 'prefixes' => '51,52,53,54,55', 'checkdigit' => true ), array ('name' => 'Solo', 'length' => '16,18,19', 'prefixes' => '6334, 6767', 'checkdigit' => true ), array ('name' => 'Switch', 'length' => '16,18,19', 'prefixes' => '4903,4905,4911,4936,564182,633110,6333,6759', 'checkdigit' => true ), array ('name' => 'Visa', 'length' => '13,16', 'prefixes' => '4', 'checkdigit' => true ), array ('name' => 'Visa Electron', 'length' => '16', 'prefixes' => '417500,4917,4913', 'checkdigit' => true ) ); public function initialize($context, $parameters = null) { // initialize parent parent::initialize($context); // set defaults $parameterHolder = $this->getParameterHolder(); $parameterHolder->set('cc_error_type', 'Unknown card type'); $parameterHolder->set('cc_error_missing', 'No card number provided'); $parameterHolder->set('cc_error_format', 'Credit card number has invalid format'); $parameterHolder->set('cc_error_number', 'Credit card number is invalid'); $parameterHolder->set('cc_error_length', 'Credit card number is wrong length'); $this->getParameterHolder()->add($parameters); return true; } public function execute(&$value, &$error) { $cardName = $this->getParameterHolder()->get('card_name'); $cardName = $this->getContext()->getRequest()->getParameter($cardName); $cardNumber = $value; // Establish card type $cardType = -1; for ($i=0; $i<sizeof(self::$CARDS); $i++) { // See if it is this card (ignoring the case of the string) if (strtolower($cardName) == strtolower(self::$CARDS[$i]['name'])) { $cardType = $i; break; } } // If card type not found, report an error if ($cardType == -1) { $error = $this->getParameterHolder()->get('cc_error_type'); return false; } // Ensure that the user has provided a credit card number if (strlen($cardNumber) == 0) { $error = $this->getParameterHolder()->get('cc_error_missing'); return false; } // Remove any non-digits from the credit card number $cardNo = preg_replace('/[^0-9]/', '', $cardNumber); // Check that the number is numeric and of the right sort of length. if (!eregi('^[0-9]{13,19}$',$cardNo)) { $error = $this->getParameterHolder()->get('cc_error_format'); return false; } // Now check the modulus 10 check digit - if required if (self::$CARDS[$cardType]['checkdigit']) { $checksum = 0; // running checksum total $mychar = ""; // next char to process $j = 1; // takes value of 1 or 2 // Process each digit one by one starting at the right for ($i = strlen($cardNo) - 1; $i >= 0; $i--) { // Extract the next digit and multiply by 1 or 2 on alternative digits. $calc = $cardNo{$i} * $j; // If the result is in two digits add 1 to the checksum total if ($calc > 9) { $checksum = $checksum + 1; $calc = $calc - 10; } // Add the units element to the checksum total $checksum = $checksum + $calc; // Switch the value of j if ($j ==1) {$j = 2;} else {$j = 1;}; } // All done - if checksum is divisible by 10, it is a valid modulus 10. // If not, report an error. if ($checksum % 10 != 0) { $error = $this->getParameterHolder()->get('cc_error_number'); return false; } } // The following are the card-specific checks we undertake. // Load an array with the valid prefixes for this card $prefix = split(',',self::$CARDS[$cardType]['prefixes']); // Now see if any of them match what we have in the card number $prefixValid = false; for ($i=0; $i<sizeof($prefix); $i++) { $exp = '^' . $prefix[$i]; if (ereg($exp,$cardNo)) { $prefixValid = true; break; } } // If it isn't a valid prefix there's no point at looking at the length if (!$prefixValid) { $error = $this->getParameterHolder()->get('cc_error_number'); return false; } // See if the length is valid for this card $lengthValid = false; $lengths = split(',',self::$CARDS[$cardType]['length']); for ($j=0; $j<sizeof($lengths); $j++) { if (strlen($cardNo) == $lengths[$j]) { $lengthValid = true; break; } } // See if all is OK by seeing if the length was valid. if (!$lengthValid) { $error = $this->getParameterHolder()->get('cc_error_length'); return false; }; // The credit card is in the required format. return true; } }