File indexing completed on 2024-12-22 05:37:12
0001 <?php 0002 /** 0003 * Zend Framework 0004 * 0005 * LICENSE 0006 * 0007 * This source file is subject to the new BSD license that is bundled 0008 * with this package in the file LICENSE.txt. 0009 * It is also available through the world-wide-web at this URL: 0010 * http://framework.zend.com/license/new-bsd 0011 * If you did not receive a copy of the license and are unable to 0012 * obtain it through the world-wide-web, please send an email 0013 * to license@zend.com so we can send you a copy immediately. 0014 * 0015 * @category Zend 0016 * @package Zend_Validate 0017 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0018 * @license http://framework.zend.com/license/new-bsd New BSD License 0019 * @version $Id$ 0020 */ 0021 0022 /** 0023 * @see Zend_Validate_Abstract 0024 */ 0025 // require_once 'Zend/Validate/Abstract.php'; 0026 0027 /** 0028 * @category Zend 0029 * @package Zend_Validate 0030 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0031 * @license http://framework.zend.com/license/new-bsd New BSD License 0032 */ 0033 class Zend_Validate_CreditCard extends Zend_Validate_Abstract 0034 { 0035 /** 0036 * Detected CCI list 0037 * 0038 * @var string 0039 */ 0040 const ALL = 'All'; 0041 const AMERICAN_EXPRESS = 'American_Express'; 0042 const UNIONPAY = 'Unionpay'; 0043 const DINERS_CLUB = 'Diners_Club'; 0044 const DINERS_CLUB_US = 'Diners_Club_US'; 0045 const DISCOVER = 'Discover'; 0046 const JCB = 'JCB'; 0047 const LASER = 'Laser'; 0048 const MAESTRO = 'Maestro'; 0049 const MASTERCARD = 'Mastercard'; 0050 const SOLO = 'Solo'; 0051 const VISA = 'Visa'; 0052 0053 const CHECKSUM = 'creditcardChecksum'; 0054 const CONTENT = 'creditcardContent'; 0055 const INVALID = 'creditcardInvalid'; 0056 const LENGTH = 'creditcardLength'; 0057 const PREFIX = 'creditcardPrefix'; 0058 const SERVICE = 'creditcardService'; 0059 const SERVICEFAILURE = 'creditcardServiceFailure'; 0060 0061 /** 0062 * Validation failure message template definitions 0063 * 0064 * @var array 0065 */ 0066 protected $_messageTemplates = array( 0067 self::CHECKSUM => "'%value%' seems to contain an invalid checksum", 0068 self::CONTENT => "'%value%' must contain only digits", 0069 self::INVALID => "Invalid type given. String expected", 0070 self::LENGTH => "'%value%' contains an invalid amount of digits", 0071 self::PREFIX => "'%value%' is not from an allowed institute", 0072 self::SERVICE => "'%value%' seems to be an invalid creditcard number", 0073 self::SERVICEFAILURE => "An exception has been raised while validating '%value%'", 0074 ); 0075 0076 /** 0077 * List of allowed CCV lengths 0078 * 0079 * @var array 0080 */ 0081 protected $_cardLength = array( 0082 self::AMERICAN_EXPRESS => array(15), 0083 self::DINERS_CLUB => array(14), 0084 self::DINERS_CLUB_US => array(16), 0085 self::DISCOVER => array(16), 0086 self::JCB => array(16), 0087 self::LASER => array(16, 17, 18, 19), 0088 self::MAESTRO => array(12, 13, 14, 15, 16, 17, 18, 19), 0089 self::MASTERCARD => array(16), 0090 self::SOLO => array(16, 18, 19), 0091 self::UNIONPAY => array(16, 17, 18, 19), 0092 self::VISA => array(16), 0093 ); 0094 0095 /** 0096 * List of accepted CCV provider tags 0097 * 0098 * @var array 0099 */ 0100 protected $_cardType = array( 0101 self::AMERICAN_EXPRESS => array('34', '37'), 0102 self::DINERS_CLUB => array('300', '301', '302', '303', '304', '305', '36'), 0103 self::DINERS_CLUB_US => array('54', '55'), 0104 self::DISCOVER => array('6011', '622126', '622127', '622128', '622129', '62213', 0105 '62214', '62215', '62216', '62217', '62218', '62219', 0106 '6222', '6223', '6224', '6225', '6226', '6227', '6228', 0107 '62290', '62291', '622920', '622921', '622922', '622923', 0108 '622924', '622925', '644', '645', '646', '647', '648', 0109 '649', '65'), 0110 self::JCB => array('3528', '3529', '353', '354', '355', '356', '357', '358'), 0111 self::LASER => array('6304', '6706', '6771', '6709'), 0112 self::MAESTRO => array('5018', '5020', '5038', '6304', '6759', '6761', '6763'), 0113 self::MASTERCARD => array('51', '52', '53', '54', '55'), 0114 self::SOLO => array('6334', '6767'), 0115 self::UNIONPAY => array('622126', '622127', '622128', '622129', '62213', '62214', 0116 '62215', '62216', '62217', '62218', '62219', '6222', '6223', 0117 '6224', '6225', '6226', '6227', '6228', '62290', '62291', 0118 '622920', '622921', '622922', '622923', '622924', '622925'), 0119 self::VISA => array('4'), 0120 ); 0121 0122 /** 0123 * CCIs which are accepted by validation 0124 * 0125 * @var array 0126 */ 0127 protected $_type = array(); 0128 0129 /** 0130 * Service callback for additional validation 0131 * 0132 * @var callback 0133 */ 0134 protected $_service; 0135 0136 /** 0137 * Constructor 0138 * 0139 * @param string|array|Zend_Config $options OPTIONAL Type of CCI to allow 0140 */ 0141 public function __construct($options = array()) 0142 { 0143 if ($options instanceof Zend_Config) { 0144 $options = $options->toArray(); 0145 } else if (!is_array($options)) { 0146 $options = func_get_args(); 0147 $temp['type'] = array_shift($options); 0148 if (!empty($options)) { 0149 $temp['service'] = array_shift($options); 0150 } 0151 0152 $options = $temp; 0153 } 0154 0155 if (!array_key_exists('type', $options)) { 0156 $options['type'] = self::ALL; 0157 } 0158 0159 $this->setType($options['type']); 0160 if (array_key_exists('service', $options)) { 0161 $this->setService($options['service']); 0162 } 0163 } 0164 0165 /** 0166 * Returns a list of accepted CCIs 0167 * 0168 * @return array 0169 */ 0170 public function getType() 0171 { 0172 return $this->_type; 0173 } 0174 0175 /** 0176 * Sets CCIs which are accepted by validation 0177 * 0178 * @param string|array $type Type to allow for validation 0179 * @return Zend_Validate_CreditCard Provides a fluent interface 0180 */ 0181 public function setType($type) 0182 { 0183 $this->_type = array(); 0184 return $this->addType($type); 0185 } 0186 0187 /** 0188 * Adds a CCI to be accepted by validation 0189 * 0190 * @param string|array $type Type to allow for validation 0191 * @return Zend_Validate_CreditCard Provides a fluent interface 0192 */ 0193 public function addType($type) 0194 { 0195 if (is_string($type)) { 0196 $type = array($type); 0197 } 0198 0199 foreach($type as $typ) { 0200 if (defined('self::' . strtoupper($typ)) && !in_array($typ, $this->_type)) { 0201 $this->_type[] = $typ; 0202 } 0203 0204 if (($typ == self::ALL)) { 0205 $this->_type = array_keys($this->_cardLength); 0206 } 0207 } 0208 0209 return $this; 0210 } 0211 0212 /** 0213 * Returns the actual set service 0214 * 0215 * @return callback 0216 */ 0217 public function getService() 0218 { 0219 return $this->_service; 0220 } 0221 0222 /** 0223 * Sets a new callback for service validation 0224 * 0225 * @param mixed $service 0226 * @throws Zend_Validate_Exception 0227 * @return $this 0228 */ 0229 public function setService($service) 0230 { 0231 if (!is_callable($service)) { 0232 // require_once 'Zend/Validate/Exception.php'; 0233 throw new Zend_Validate_Exception('Invalid callback given'); 0234 } 0235 0236 $this->_service = $service; 0237 return $this; 0238 } 0239 0240 /** 0241 * Defined by Zend_Validate_Interface 0242 * 0243 * Returns true if and only if $value follows the Luhn algorithm (mod-10 checksum) 0244 * 0245 * @param string $value 0246 * @return boolean 0247 */ 0248 public function isValid($value) 0249 { 0250 $this->_setValue($value); 0251 0252 if (!is_string($value)) { 0253 $this->_error(self::INVALID, $value); 0254 return false; 0255 } 0256 0257 if (!ctype_digit($value)) { 0258 $this->_error(self::CONTENT, $value); 0259 return false; 0260 } 0261 0262 $length = strlen($value); 0263 $types = $this->getType(); 0264 $foundp = false; 0265 $foundl = false; 0266 foreach ($types as $type) { 0267 foreach ($this->_cardType[$type] as $prefix) { 0268 if (substr($value, 0, strlen($prefix)) == $prefix) { 0269 $foundp = true; 0270 if (in_array($length, $this->_cardLength[$type])) { 0271 $foundl = true; 0272 break 2; 0273 } 0274 } 0275 } 0276 } 0277 0278 if ($foundp == false){ 0279 $this->_error(self::PREFIX, $value); 0280 return false; 0281 } 0282 0283 if ($foundl == false) { 0284 $this->_error(self::LENGTH, $value); 0285 return false; 0286 } 0287 0288 $sum = 0; 0289 $weight = 2; 0290 0291 for ($i = $length - 2; $i >= 0; $i--) { 0292 $digit = $weight * $value[$i]; 0293 $sum += floor($digit / 10) + $digit % 10; 0294 $weight = $weight % 2 + 1; 0295 } 0296 0297 if ((10 - $sum % 10) % 10 != $value[$length - 1]) { 0298 $this->_error(self::CHECKSUM, $value); 0299 return false; 0300 } 0301 0302 if (!empty($this->_service)) { 0303 try { 0304 // require_once 'Zend/Validate/Callback.php'; 0305 $callback = new Zend_Validate_Callback($this->_service); 0306 $callback->setOptions($this->_type); 0307 if (!$callback->isValid($value)) { 0308 $this->_error(self::SERVICE, $value); 0309 return false; 0310 } 0311 } catch (Zend_Exception $e) { 0312 $this->_error(self::SERVICEFAILURE, $value); 0313 return false; 0314 } 0315 } 0316 0317 return true; 0318 } 0319 }