Server IP : 80.87.202.40 / Your IP : 216.73.216.169 Web Server : Apache System : Linux rospirotorg.ru 5.14.0-539.el9.x86_64 #1 SMP PREEMPT_DYNAMIC Thu Dec 5 22:26:13 UTC 2024 x86_64 User : bitrix ( 600) PHP Version : 8.2.27 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /home/bitrix/ext_www/rospirotorg.ru/bitrix/modules/sberbank.ecom2/lib/rbs/ |
Upload File : |
<?php namespace Sberbank\Payments; // version: 1.0.13 // date: 2025-03-14 use Bitrix\Main\Web; use DateTime; define('LOG_FILE', realpath(dirname(dirname(dirname(__FILE__)))) . "/logs/sberbank.log"); define('SBERBANK_CACERT_FILE', realpath(dirname(dirname(dirname(__FILE__)))) . "/cacert.cer"); // 0 - заказ зарегистрирован, но не оплачен; // 1 - предавторизованная сумма удержана (для двухстадийных платежей); // 2 - проведена полная авторизация суммы заказа; // 3 - авторизация отменена; // 4 - по транзакции была проведена операция возврата; // 5 - инициирована авторизация через сервер контроля доступа банка-эмитента; // 6 - авторизация отклонена. // 1 = approved - операция удержания (холдирования) суммы; // 6 = declinedByTimeout - операция отклонения заказа по истечении его времени жизни; // 2 = deposited - операция завершения; // 3 = reversed - операция отмены; // 4 = refunded - операция возврата. class Gateway { const log_file = LOG_FILE; const cacert_file = SBERBANK_CACERT_FILE; const PHONE_VALIDATION_PATTERN = '/^\+?[0-9]{0,15}$/'; /** * Массив с НДС * * @var integer * 0 = Без НДС * 1 = НДС по ставке 0% * 2 = НДС чека по ставке 10% * 3 = НДС чека по ставке 18% * 4 = НДС чека по ставке 10/110 * 6 = НДС чека по ставке 20% * 7 = НДС чека по ставке 20/120 * 10 = НДС по ставке 5% * 11 = НДС по расчетной ставке 5/105 * 12 = НДС по ставке 7% * 13 = НДС по расчетной ставке 7/107 */ private static $arr_tax = array( 1 => 0, 2 => 10, 3 => 18, 6 => 20, 10 => 5, 12 => 7, ); private $gate_url; private $basket = array(); private $data = array(); private $options = array( 'gate_url_prod' => '', 'gate_url_test' => '', 'payment_link' => '', 'ofd_enabled' => false, 'module_version' => 'def', 'language' => 'ru', 'ofd_tax' => 0, 'handler_two_stage' => 0, 'default_cartItem_tax' => 6, 'delivery' => false, 'handler_logging' => true, 'customer_phone' => '', 'customer_email' => '', 'customer_name' => '', 'callback_redirect' => 0, 'domain_found' => false, 'callback_url' => '', 'additionalOfdParams' => array(), 'ffd_version' => '1.05', 'measurement_code' => 0, 'ignore_product_tax' => false, 'callback_mode' => true, 'enable_cacert' => false, 'request_method' => 'curl', // curl || httpClient 'callback_actions' => [], 'callback_action_order_status' => [], 'callback_operation' => false, 'callback_operations' => ['deposited', 'approved', 'reversed', 'refunded', 'declinedByTimeout'], 'fes_cashboxId' => '', 'gate_url_alternative_prod' => '', 'gate_url_alternative_test' => '', 'callback_type' => 'DYNAMIC', // DYNAMIC | STATIC // 'enable_phone_validation' => false, 'enable_order_payer_data' => true, ); // FFD 1.2 static $measureList = array( 0 => 'шт', 1 => 'ед', // alternate 0 value 10 => 'г', 11 => 'кг', 12 => 'т', 20 => 'см', 21 => 'дм', 22 => 'м', 30 => 'кв.см', 31 => 'кв.дм', 32 => 'кв.м', 40 => 'мл', 41 => 'л', 255 => '-', ); public function buildData($data) { foreach ($data as $key => $value) { $this->data[$key] = $value; } } public function setOptions($data) { foreach ($data as $key => $value) { $this->options[$key] = $value; } } public function registerOrder() { $this->transformPrices(); $orderPayerData = array(); $json_params = array( 'CMS' => $this->options['cms_version'], 'Module-Version' => $this->options['module_version'], 'USER_FIO' => $this->options['customer_name'], ); if (strlen($this->options['customer_email']) > 3) { $json_params['email'] = $this->options['customer_email']; } if ($this->isIssetPhone($this->options['customer_phone'])) { $json_params['phone'] = $this->formatPhone($this->options['customer_phone']); $orderPayerData['mobilePhone'] = $this->formatPhone($this->options['customer_phone']); } if($this->options['fes_cashboxId'] && strlen($this->options['fes_cashboxId']) > 3) { $json_params['fes_cashboxId'] = $this->options['fes_cashboxId']; } $this->buildData(array( 'CMS' => $this->options['cms_version'], 'language' => $this->options['language'], 'jsonParams' => Web\Json::encode($json_params), )); if($this->options['enable_order_payer_data'] && count($orderPayerData)) { $this->buildData(array( 'orderPayerData' => Web\Json::encode($orderPayerData), )); } $gateData = $this->data; $orderId = $this->data['orderNumber']; $callback_actions = implode(",", array_keys($this->options['callback_actions'])); for ($i = 0; $i < 30; $i++) { $gateData['orderNumber'] = $orderId . "_" . $i; $method = 'getOrderStatusExtended.do'; $gateResponse = $this->setRequest($method, array( 'userName' => $gateData['userName'], 'password' => $gateData['password'], 'orderNumber' => $gateData['orderNumber'] )); if ($gateResponse['amount'] != $gateData['amount'] && $gateResponse['errorCode'] != 6 && $gateResponse['errorCode'] == 0) { continue; } if ($gateResponse['errorCode'] == 6) { // register order from gate if ($this->ofdEnable()) { $this->addFFDParams(); $gateData = $this->addOrderBundle($gateData); if (isset($this->options['additionalOfdParams']) && count($this->options['additionalOfdParams']) > 0) { $gateData['additionalOfdParams'] = json_encode($this->options['additionalOfdParams']); } } $method = $this->options['handler_two_stage'] ? 'registerPreAuth.do' : 'register.do'; $gateResponse = $this->setRequest($method, $gateData); if ($this->options['domain_found'] && $this->options['callback_mode']) { $this->updateCallback([ 'login' => $this->data['userName'], 'password' => $this->data['password'], 'test_mode' => $this->options['test_mode'], 'callback_type' => $this->options['callback_type'], 'callback_http_method' => 'GET', 'callbacks_enabled' => true, 'callback_addresses' => $this->options['callback_url'], 'callback_operations' => $callback_actions ]); } if ($gateResponse['errorCode'] == 0) { $this->setRequest('addParams.do', array( 'userName' => $this->data['userName'], 'password' => $this->data['password'], 'orderId' => $gateResponse['orderId'], 'language' => $this->options['language'], 'params' => Web\Json::encode(array('formUrl' => $gateResponse['formUrl'])), )); $this->createPaymentLink($gateResponse['formUrl'], 'register.do'); } break; } else if ($gateResponse['errorCode'] == 0 && $gateResponse['orderStatus'] == 0) { // return and build payment link already registered order from gate foreach ($gateResponse['merchantOrderParams'] as $key => $item) { if ($item['name'] == 'formUrl') { $this->createPaymentLink($item['value'], 'getOrderStatusExtended.do'); break; } } break; } else if ($gateResponse['errorCode'] == 0 && $gateResponse['orderStatus'] == 2 && $gateResponse['amount'] == $gateData['amount']) { // order allready payed $gateResponse = array('payment' => 1); break; } else if ($gateResponse['errorCode'] != 0) { break; } } if ($gateResponse['errorCode'] != 0) { $this->baseLogger($this->gate_url, $method, $gateData, $gateResponse, 'REGISTER_ERROR'); } else if (($method == 'registerPreAuth.do' || $method == 'register.do') && $this->options['handler_logging']) { $this->baseLogger($this->gate_url, $method, $gateData, $gateResponse, 'REGISTER_NEW_ORDER'); } return $gateResponse; } public function checkOrder() { $gateData = $this->data; $gateResponse = $this->setRequest('getOrderStatusExtended.do', $gateData); if ($this->options['handler_logging']) { $title = $this->options['callback_redirect'] ? 'CALLBACK_RETURN' : 'USER_RETURN'; $this->baseLogger($this->gate_url, 'getOrderStatusExtended.do', $gateData, Web\Json::encode($title == 'USER_RETURN' ? array('orderNumber' => $gateResponse['orderNumber']) : $gateResponse, JSON_UNESCAPED_UNICODE), $title); } return $gateResponse; } public function refund() { $gateData = $this->data; $gateResponse = $this->setRequest('refund.do', $gateData); if ($this->options['handler_logging']) { $this->baseLogger($this->gate_url, 'refund.do', $gateData, Web\Json::encode($gateResponse), 'REFUND'); } return $gateResponse; } public function deposit() { $gateData = $this->data; $gateResponse = $this->setRequest('deposit.do', $gateData); if ($this->options['handler_logging']) { $this->baseLogger($this->gate_url, 'deposit.do', $gateData, Web\Json::encode($gateResponse), 'DEPOSIT'); } return $gateResponse; } public function ofdEnable() { if ($this->options['ofd_enabled'] == true) { return true; } return false; } public function setPosition($position) { array_push($this->basket, $position); } public function getBasket() { return $this->basket; } public function getTaxCode($tax_rate) { $result = $this->options['default_cartItem_tax']; if(!$this->options['ignore_product_tax']) { if($tax_rate === null) { return 0; } $tax_rate = $tax_rate * 100; foreach (self::$arr_tax as $key => $value) { if($value == $tax_rate) { $result = $key; } } } return $result; } public function getTaxCodeDelivery($tax_rate) { $result = 0; if($tax_rate === null) { return 0; } $tax_rate = $tax_rate; foreach (self::$arr_tax as $key => $value) { if ($value == $tax_rate) { $result = $key; } } return $result; } public function getCurrencyCode($currency) { $result = 0; foreach ($this->options['iso'] as $key => $value) { if ($key == $currency) { $result = $value; } } return $result; } private function addFFDParams() { foreach ($this->basket as $key => $item) { if($this->options['delivery'] && count($this->basket) == $key+1) { $paymentMethod = $this->options['ffd_payment_method_delivery'] ? $this->options['ffd_payment_method_delivery'] : 1; $paymentObject = $this->options['ffd_payment_object_delivery'] ? $this->options['ffd_payment_object_delivery'] : 4; } else { $paymentMethod = $this->options['ffd_payment_method']; $paymentObject = $this->options['ffd_payment_object']; } $this->basket[$key]['itemAttributes'] = array( 'attributes' => array( array( 'name' => 'paymentMethod', 'value' => $paymentMethod, ), array( 'name' => 'paymentObject', 'value' => $paymentObject, ), ) ); if(isset($this->basket[$key]['supplier_info'])) { $this->basket[$key]['itemAttributes']['attributes'][] = array( 'name' => 'supplier_info.name', 'value' => $this->basket[$key]['supplier_info']['name'], ); $this->basket[$key]['itemAttributes']['attributes'][] = array( 'name' => 'supplier_info.inn', 'value' => $this->basket[$key]['supplier_info']['inn'], ); } unset($this->basket[$key]['supplier_info']); if(isset($this->basket[$key]['nomenclature'])) { if($this->basket[$key]['nomenclature']) { $this->basket[$key]['itemAttributes']['attributes'][] = array( 'name' => 'nomenclature', 'value' => $this->basket[$key]['nomenclature'], ); } } unset($this->basket[$key]['nomenclature']); if($this->options['ffd_version'] == '1.2') { $this->basket[$key]['quantity']['measure'] = strval($this->options['measurement_code']); } if($item['fes_truCode']) { $this->basket[$key]['itemDetails']['itemDetailsParams'][] = array( 'name' => 'fes_truCode', 'value' => $item['fes_truCode'], ); } unset($this->basket[$key]['fes_truCode']); } } private function transformMeasure($value) { $result = array_search($value, $this->measureList); if ($result == 1) { return '0'; } return $result ? strval($result) : strval($this->options['measurement_code']); } private function setRequest($method, $data) { global $APPLICATION; $this->gate_url = $this->options['test_mode'] ? $this->options['gate_url_test'] : $this->options['gate_url_prod']; $request_url = $this->gate_url . $method; if (mb_strtoupper(SITE_CHARSET) != 'UTF-8') { $data = $APPLICATION->ConvertCharsetArray($data, 'windows-1251', 'UTF-8'); } if ($this->options['request_method'] === 'curl' && function_exists('curl_init')) { $headers = array( 'CMS: ' . $this->options['cms_version'], 'Module-Version: ' . $this->options['module_version'], ); $response = $this->requestByCurl($request_url, $data, $headers); } else { $response = $this->requestByHttpClient($request_url, $data); } return $response; } private function requestByHttpClient($url, $data) { $http = new Web\HttpClient(); $http->setCharset("utf-8"); // $http->setHeader('CMS: ', $this->options['cms_version']); // $http->setHeader('Module-Version: ', $this->options['module_version']); $http->disableSslVerification(); $http->post($url, $data); $response = $http->getResult(); if ($this->is_json($response)) { $response = Web\Json::decode($response); } else { $response = array( 'errorCode' => 999, 'errorMessage' => 'Server not available', ); //var_dump( $http->getError() ); //var_dump( $http->getStatus() ); //var_dump( $http->getHeaders() ); } if (mb_strtoupper(SITE_CHARSET) != 'UTF-8') { $APPLICATION->ConvertCharsetArray($response, 'UTF-8', 'windows-1251'); } return $response; } private function requestByCurl($url, $data, $headers = array(), $ca_info = null) { $ca_info = self::cacert_file; $curl_opt = array( CURLOPT_HTTPHEADER => $headers, CURLOPT_VERBOSE => true, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_URL => $url, CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => $data, CURLOPT_HEADER => false, ); $ssl_verify_peer = false; if ($this->options['enable_cacert'] === true && file_exists($ca_info)) { $ssl_verify_peer = true; $curl_opt[CURLOPT_CAINFO] = $ca_info; } $curl_opt[CURLOPT_SSL_VERIFYPEER] = $ssl_verify_peer; $ch = curl_init(); curl_setopt_array($ch, $curl_opt); $response = curl_exec($ch); if ($this->is_json($response)) { $response = json_decode($response, true); } else { $response = array( 'errorCode' => 999, 'errorMessage' => curl_error($ch), ); } // echo htmlentities(substr($response, 0, $header_size)) . "<br/>"; // echo htmlentities(substr($response, $header_size)) . "<hr/>"; curl_close($ch); // return substr($response, $header_size); return $response; } private function is_json($string, $return_data = false) { $data = json_decode($string); return (json_last_error() == JSON_ERROR_NONE) ? ($return_data ? $data : TRUE) : FALSE; } private function addOrderBundle($data) { $data['orderBundle']['customerDetails'] = array( 'email' => $this->options['customer_email'], ); if ($this->isIssetPhone($this->options['customer_phone'])) { $data['orderBundle']['customerDetails']['phone'] = $this->formatPhone($this->options['customer_phone']); } $data['orderBundle']['cartItems']['items'] = $this->basket; $data['taxSystem'] = $this->options['ofd_tax']; $data['orderBundle'] = Web\Json::encode($data['orderBundle']); return $data; } private function transformPrices() { $this->data['amount'] = $this->data['amount'] * 100; if (is_float($this->data['amount'])) { $this->data['amount'] = round($this->data['amount']); } if ($this->ofdEnable()) { foreach ($this->basket as $key => $item) { $this->basket[$key]['itemPrice'] = round($item['itemPrice'] * 100); $this->basket[$key]['itemAmount'] = round($item['itemAmount'] * 100); } } } private function createPaymentLink($linkPart, $method) { if ($method == 'register.do' || $method == 'registerPreAuth.do') { $this->options['payment_link'] = $linkPart; } else if ($method == 'getOrderStatusExtended.do') { $this->options['payment_link'] = $linkPart; } } public function getPaymentLink() { return $this->options['payment_link']; } public function debug($data) { echo "<pre>"; print_r($data); echo "</pre>"; } public function baseLogger($url, $method, $data, $response, $title) { $objDateTime = new DateTime(); $file = self::log_file; $logContent = ''; if (file_exists($file)) { $logSize = filesize($file) / 1000; if ($logSize < 10000) { $logContent = file_get_contents($file); } } $operation = $this->options['callback_operation']; if ($operation) { $operation_value = in_array($operation, $this->options['callback_operations']) ? $operation : ''; } $logContent .= $title . "\n"; $logContent .= '----------------------------' . "\n"; $logContent .= "DATE: " . $objDateTime->format("Y-m-d H:i:s") . "\n"; $logContent .= 'REQUEST_METHOD: ' . $this->options['request_method'] . "\n"; $logContent .= 'URL ' . $url . "\n"; $logContent .= 'METHOD ' . $method . "\n"; $logContent .= $operation_value ? 'CALBACK_OPERATION ' . $operation_value . "\n" : ''; if ($title != 'USER_RETURN') { $logContent .= "DATA: \n" . print_r($data, true) . "\n"; } $logContent .= "RESPONSE: \n" . print_r($response, true) . "\n"; $logContent .= "\n\n"; file_put_contents($file, $logContent); } public function updateCallback($data) { if (!isset($data['login']) && !isset($data['password'])) { return false; } $data['name'] = str_replace('-api', "", $data['login']); if ($data['test_mode'] == 1) { $gate_url = $this->options['gate_url_alternative_test'] . "mportal-uat/mvc/public/merchant/"; } else { $gate_url = $this->options['gate_url_alternative_prod'] . "mportal/mvc/public/merchant/"; } $gate_url = $gate_url . 'update/' . $data['name']; $http = new Web\HttpClient(); $http->setHeader('Content-Type', 'application/json'); $http->setAuthorization($data['login'], $data['password']); $http->disableSslVerification(); $http->post($gate_url, json_encode($data)); $response = $http->getResult(); $this->baseLogger($gate_url, 'update', $data, $response, 'CALLBACK_UPDATE'); $response = json_decode($response, true); if ($response['status'] == 'SUCCESS') { return 1; } else { return 0; } } public function getCallback($data) { if (!isset($data['login']) && !isset($data['password'])) { return false; } $data['name'] = str_replace('-api', "", $data['login']); if ($data['test_mode'] == 1) { $gate_url = $this->options['gate_url_alternative_test'] . "mportal-uat/mvc/public/merchant/"; } else { $gate_url = $this->options['gate_url_alternative_prod'] . "mportal/mvc/public/merchant/"; } $gate_url = $gate_url . 'get/' . $data['name']; $http = new Web\HttpClient(); $http->setHeader('Content-Type', 'application/json'); $http->setAuthorization($data['login'], $data['password']); $http->disableSslVerification(); $http->post($gate_url, json_encode($data)); $response = $http->getResult(); // $this->baseLogger($gate_url, 'get', $data, $response,'CALLBACK_GET'); $response = json_decode($response, true); if ($response && $response['status'] == 'SUCCESS') { return $response['callback_addresses']; } return false; } public function broadcast_callback($url, $params) { $data = http_build_query($params); $result_url = strpos($url, '?') ? $url . '&' . $data : $url . '?' . $data; $http = new Web\HttpClient(); $http->get($result_url); $response = $http->getResult(); $this->baseLogger($result_url, '', '', '', 'CALLBACK_BROADCAST'); } public function closeOfdReceipt($data) { $this->buildData(array( 'mdOrder' => $data['mdOrder'], 'amount' => $data['amount'], 'userName' => $data['userName'], 'password' => $data['password'], )); $this->transformPrices(); $gateData = $this->data; if($this->ofdEnable()) { $this->addFFDParams(); $gateData = $this->addOrderBundle($this->data); } if($gateData) { $gateResponse = $this->setRequest('closeOfdReceipt.do', $gateData); } else if($this->options['handler_logging']) { $this->baseLogger($this->gate_url, 'closeOfdReceipt.do', json_encode($gateData), '', 'CLOSE_OFD_ERROR'); } if($this->options['handler_logging']) { $log_method_name = $this->ofdEnable() ? 'CLOSE_OFD_RECEIPT+ORDER_BUNDLE' : 'CLOSE_OFD_RECEIPT'; $this->baseLogger($this->gate_url, 'closeOfdReceipt.do', $gateData, Web\Json::encode($gateResponse), $log_method_name); } return $gateResponse; } /** * Форматирование номера телефона * * @param string $phone Номер телефона * @return string Отформатированный номер телефона */ private function formatPhone($phone) { if (empty($phone)) { return ''; } $phone = preg_replace('/[^0-9+]/', '', $phone); $phone = preg_replace('/^\+7/', '', $phone); return $phone; } private function isIssetPhone($phone) { return strlen($this->options['customer_phone']) > 1; } }