ErrorMsgs['credit_card_validation_error'] = '!lu_cc_validation_error!'; $this->ErrorMsgs['credit_card_expired'] = '!lu_cc_expired!'; } /** * Return error message for field * * @param string $field * @return string * @access public */ public function GetErrorMsg($field, $force_escape = null) { if ( $field != 'OrderNumber' ) { return parent::GetErrorMsg($field, $force_escape); } $number['error'] = parent::GetErrorMsg('Number', $force_escape); $number['pseudo'] = $this->GetErrorPseudo('Number'); $subnumber['error'] = parent::GetErrorMsg('SubNumber', $force_escape); $subnumber['pseudo'] = $this->GetErrorPseudo('SubNumber'); // if pseudo match & not empty -> return 1st // if one of pseudos not empty -> return it // if we got one pseudo "bad_type" and other pseudo "required", then return "bad_type" error message if ( $number['pseudo'] && ($number['pseudo'] == $subnumber['pseudo']) ) { return $number['error']; } if ( $number['pseudo'] && !$subnumber['pseudo'] ) { return $number['error']; } if ( !$number['pseudo'] && $subnumber['pseudo'] ) { return $subnumber['error']; } if ( $number['pseudo'] == 'bad_type' ) { return $number['error']; } if ( $subnumber['pseudo'] == 'bad_type' ) { return $subnumber['error']; } return ''; /*$msg = '[' . $number_error . '(' . $number_pseudo . ')] [' . $subnumber_error . '] (' . $subnumber_pseudo . ')'; return $msg;*/ } /** * Check field value by user-defined alghoritm * * @param string $field field name * @param Array $params field options from config * @return bool */ function CustomValidation($field, $params) { // TODO: move to OrdersEventHandler OR extend kValidator class $res = true; $res = $res && $this->ValidateCCNumber($field, $params); $res = $res && $this->ValidateCCExpiration($field, $params); return $res; } /** * Check if field value is valid credit card number against credit card type specified * * @param string $field field name * @param Array $params field options from config * @return bool * @access private */ function ValidateCCNumber($field, $params) { $value = $this->dataSource->GetDBField($field); $cardtype_field = getArrayValue($params, 'cardtype_field'); if ( !$cardtype_field || !$value || !$this->dataSource->requireCreditCard() ) { return true; } if ( $this->Application->ConfigValue('Comm_MaskProcessedCreditCards') ) { $mask_found = strpos($value, str_repeat('X', 4)) !== false; if ( $this->Application->isAdminUser && $mask_found ) { // masked card numbers always appear valid in admin return true; } } if (defined('DEBUG_MODE') && kUtil::constOn('DBG_PAYMENT_GW')) { $gw_data = $this->dataSource->getGatewayData(); /** @var kGWBase $gateway_object */ $gateway_object = $this->Application->recallObject($gw_data['ClassName']); $test_numbers = $gateway_object->GetTestCCNumbers(); if ( in_array($value, $test_numbers) ) { return true; } } $error_field = isset($params['error_field']) ? $params['error_field'] : $field; // '1' => 'Visa','2' => 'Mastercard', '3' => 'Amex', '4' => 'Discover', 5 => 'Diners Club', 6 => 'JBC' // Innocent until proven guilty $cc_valid = true; // Get rid of any non-digits $value = preg_replace('/[^\d]/', '', $value); // Perform card-specific checks, if applicable switch( $this->dataSource->GetDBField($cardtype_field) ) { case 2: // MasterCard $cc_valid = preg_match('/^5[1-5].{14}$/', $value); break; case 1: // Visa $cc_valid = preg_match('/^4.{15}$|^4.{12}$/', $value); break; case 3: // American Express $cc_valid = preg_match('/^3[47].{13}$/', $value); break; case 4: // Discover $cc_valid = preg_match('/^6011.{12}$/', $value); break; case 5: // Diners Club $cc_valid = preg_match('/^30[0-5].{11}$|^3[68].{12}$/', $value); break; case 6: // JBC $cc_valid = preg_match('/^3.{15}$|^2131|1800.{11}$/', $value); break; default: $this->SetError($error_field, 'credit_card_validation_error'); return false; break; } // The Luhn formula works right to left, so reverse the number. $value = strrev($value); $total = 0; for($x = 0; $x < strlen($value); $x++) { $digit = substr($value, $x, 1); // If it's an odd digit, double it if( $x / 2 != floor($x/2) ) { $digit *= 2; // If the result is two digits, add them if( strlen($digit) == 2 ) { $digit = substr($digit, 0, 1) + substr($digit, 1, 1); } } // Add the current digit, doubled and added if applicable, to the Total $total += $digit; } // If it passed (or bypassed) the card-specific check and the Total is // evenly divisible by 10, it's cool! if ($cc_valid && $total % 10 == 0) { return true; } $this->SetError($error_field, 'credit_card_validation_error'); return false; } /** * Check if field value is non-expired credit card expiration date * * @param string $field field name * @param Array $params field options from config * @return bool * @access private */ function ValidateCCExpiration($field, $params) { $formatter = getArrayValue($params, 'formatter'); if ( ($formatter != 'kCCDateFormatter') || !$this->dataSource->requireCreditCard() ) { return true; } if ( !$this->Application->isAdminUser ) { // validate expiration date only for front if ( preg_match('/([\d]{2})\/([\d]{2})/', $this->dataSource->GetDBField($field), $rets) ) { $month = $rets[1]; $year = $rets[2]; $now_date = adodb_mktime(0, 0, 0, adodb_date('m'), adodb_date('d'), adodb_date('Y') ); $day_count = adodb_date('t', adodb_mktime(0, 0, 0, $month, 1, $year) ); $cc_date = adodb_mktime(23, 59, 59, $month, $day_count, $year); if ($cc_date < $now_date) { $error_field = isset($params['error_field']) ? $params['error_field'] : $field; $this->SetError($error_field, 'credit_card_expired'); return false; } } } return true; } }