1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
cashboxrobokassa.php
См. документацию.
1<?php
2namespace Bitrix\Sale\Cashbox;
3
4use Bitrix\Main;
5use Bitrix\Sale;
6use Bitrix\Catalog;
7
9
15{
16 public const CACHE_ID = 'BITRIX_CASHBOX_ROBOKASSA_ID';
17
18 private const URL = 'https://ws.roboxchange.com/RoboFiscal/Receipt/';
19
20 private const CODE_VAT_0 = 'vat0';
21 private const CODE_VAT_10 = 'vat10';
22 private const CODE_VAT_20 = 'vat20';
23 private const CODE_VAT_5 = 'vat5';
24 private const CODE_VAT_7 = 'vat7';
25
26 private const CHECK_PAYMENT_TYPE = 2;
27
28 private const MAX_NAME_LENGTH = 128;
29
30 public static function getName(): string
31 {
32 return Main\Localization\Loc::getMessage('SALE_CASHBOX_ROBOKASSA_TITLE');
33 }
34
35 protected function getPrintUrl(): string
36 {
37 return self::URL . 'Attach';
38 }
39
40 protected function getCheckUrl(): string
41 {
42 return self::URL . 'Status';
43 }
44
49 public function buildCheckQuery(Check $check): array
50 {
51 $checkParamsResult = $this->checkParams($check);
52 if (!$checkParamsResult->isSuccess())
53 {
54 return [];
55 }
56
58 if (!$payment)
59 {
60 return [];
61 }
62
63 $checkData = $check->getDataForCheck();
64
65 $request = Main\Application::getInstance()->getContext()->getRequest();
66 $protocol = $request->isHttps() ? 'https://' : 'http://';
67
68 $fields = [
69 'merchantId' => $this->getPaySystemSetting($payment, 'ROBOXCHANGE_SHOPLOGIN'),
70 'id' => $checkData['unique_id'],
71 'originId' => $payment->getId(),
72 'operation' => SellCheck::getType(),
73 'sno' => $this->getValueFromSettings('TAX', 'SNO'),
74 'url' => \urlencode($protocol . $request->getHttpHost()),
75 'total' => (string)Sale\PriceMaths::roundPrecision($checkData['total_sum']),
76 'client' => [
77 'email' => $checkData['client_email'],
78 'phone' => $checkData['client_phone'],
79 ],
80 'payments' => [],
81 'items' => [],
82 'vats' => [],
83 ];
84
85 foreach ($checkData['payments'] as $paymentItem)
86 {
87 $fields['payments'][] = [
88 'type' => self::CHECK_PAYMENT_TYPE,
89 'sum' => (string)Sale\PriceMaths::roundPrecision($paymentItem['sum']),
90 ];
91 }
92
93 $checkTypeMap = $this->getCheckTypeMap();
94 $paymentMethod = $checkTypeMap[$check::getType()];
95 $paymentObjectMap = $this->getPaymentObjectMap();
96 foreach ($checkData['items'] as $item)
97 {
98 $vat = $this->getValueFromSettings('VAT', $item['vat']);
99 $tax = $vat ?? $this->getValueFromSettings('VAT', 'NOT_VAT');
100
101 $receiptItem = [
102 'name' => mb_substr($item['name'], 0, self::MAX_NAME_LENGTH),
103 'quantity' => (string)$item['quantity'],
104 'sum' => (string)Sale\PriceMaths::roundPrecision($item['sum']),
105 'tax' => $tax,
106 'payment_method' => $paymentMethod,
107 'payment_object' => $paymentObjectMap[$item['payment_object']],
108 ];
109
110 if (!empty($item['marking_code']))
111 {
112 $receiptItem['nomenclature_code'] = $item['marking_code'];
113 }
114
115 $fields['items'][] = $receiptItem;
116
117 $fields['vats'][] = [
118 'type' => $tax,
119 'sum' => (string)Sale\PriceMaths::roundPrecision($item['vat_sum']),
120 ];
121 }
122
123 return $fields;
124 }
125
126 protected static function extractCheckData(array $data): array
127 {
128 $result = [];
129
130 $id = $data['CheckId'] ?? null;
131 if (!$id)
132 {
133 return $result;
134 }
135
136 $result['ID'] = $id;
137
138 $check = CheckManager::getObjectById($id);
139 if ($check)
140 {
141 $result['LINK_PARAMS'] = [
142 Check::PARAM_FISCAL_DOC_ATTR => $data['FiscalDocumentAttribute'],
143 Check::PARAM_FISCAL_DOC_NUMBER => $data['FiscalDocumentNumber'],
144 Check::PARAM_FN_NUMBER => $data['FnNumber'],
145 Check::PARAM_DOC_SUM => (float)$check->getField('SUM'),
146 Check::PARAM_CALCULATION_ATTR => $check::getCalculatedSign()
147 ];
148
149 if (!empty($data['FiscalDate']))
150 {
151 try
152 {
153 $dateTime = new Main\Type\DateTime($data['FiscalDate']);
154 $result['LINK_PARAMS'][Check::PARAM_DOC_TIME] = $dateTime->getTimestamp();
155 }
156 catch (Main\ObjectException $ex)
157 {}
158 }
159 }
160
161 return $result;
162 }
163
165 {
166 $processPrintResult = new Sale\Result();
167
168 $data = $result->getData();
169 $resultCode = (int)$data['ResultCode'];
170 switch ($resultCode)
171 {
172 case 0:
173 $processPrintResult->setData(['UUID' => ($data['OpKey'] ?? '')]);
174 break;
175
176 case 1:
177 $processPrintResult->addError(new Main\Error(Main\Localization\Loc::getMessage('SALE_CASHBOX_ROBOKASSA_PRINT_ERROR_FORMAT')));
178 break;
179
180 case 2:
181 $processPrintResult->addError(new Main\Error(Main\Localization\Loc::getMessage('SALE_CASHBOX_ROBOKASSA_PRINT_ERROR_NOT_ENOUGH_MONEY')));
182 break;
183
184 default:
185 $processPrintResult->addError(new Main\Error(Main\Localization\Loc::getMessage('SALE_CASHBOX_ROBOKASSA_PRINT_ERROR_INTERNAL')));
186 break;
187 }
188
189 return $processPrintResult;
190 }
191
193 {
194 return [
195 'merchantId' => $this->getPaySystemSetting($payment, 'ROBOXCHANGE_SHOPLOGIN'),
196 'id' => $payment->getId(),
197 ];
198 }
199
201 {
202 $processCheckResult = new Sale\Result();
203
204 $data = $result->getData();
205 $resultCode = (int)$data['Code'];
206 switch ($resultCode)
207 {
208 case 1:
209 $processCheckResult->addError(new Main\Error(Main\Localization\Loc::getMessage('SALE_CASHBOX_ROBOKASSA_PRINT_ERROR_WAIT')));
210 break;
211
212 case 0:
213 case 2:
214 if (!empty($data['Statuses']))
215 {
216 $statuses = [];
217 foreach ($data['Statuses'] as $status)
218 {
219 $statuses[] = $status;
220 }
221
222 $processCheckResult->setData($statuses);
223 }
224 break;
225
226 case 3:
227 $processCheckResult->addError(new Main\Error(Main\Localization\Loc::getMessage('SALE_CASHBOX_ROBOKASSA_STATUS_ERROR_REGISTER')));
228 break;
229
230 default:
231 $processCheckResult->addError(new Main\Error(Main\Localization\Loc::getMessage('SALE_CASHBOX_ROBOKASSA_STATUS_ERROR_PROCESSING')));
232 break;
233 }
234
235 return $processCheckResult;
236 }
237
239 {
240 $onAfterProcessCheckResult = new Sale\Result();
241
242 $checkList = CheckManager::getList([
243 'select' => ['ID'],
244 'filter' => [
245 'ORDER_ID' => $payment->getOrder()->getId(),
246 ],
247 ])->fetchAll();
248
249 if ($checkList)
250 {
251 $statuses = $result->getData();
252 foreach ($statuses as $key => $status)
253 {
254 $resultCode = (int)$status['Code'];
255 if ($resultCode === 0)
256 {
257 continue;
258 }
259
260 switch ($resultCode)
261 {
262 case 1:
263 $onAfterProcessCheckResult->addError(new Main\Error(Main\Localization\Loc::getMessage('SALE_CASHBOX_ROBOKASSA_PRINT_ERROR_WAIT')));
264 break;
265
266 case 2:
267 $status['CheckId'] = $checkList[$key]['ID'];
268 $applyCheckResult = static::applyCheckResult($status);
269 if (!$applyCheckResult->isSuccess())
270 {
271 $onAfterProcessCheckResult->addErrors($applyCheckResult->getErrors());
272 }
273 break;
274
275 case 3:
276 $onAfterProcessCheckResult->addError(new Main\Error(Main\Localization\Loc::getMessage('SALE_CASHBOX_ROBOKASSA_STATUS_ERROR_REGISTER')));
277 break;
278
279 default:
280 $onAfterProcessCheckResult->addError(new Main\Error(Main\Localization\Loc::getMessage('SALE_CASHBOX_ROBOKASSA_STATUS_ERROR_PROCESSING')));
281 break;
282 }
283 }
284 }
285 else
286 {
287 $onAfterProcessCheckResult->addError(new Main\Error(Main\Localization\Loc::getMessage('SALE_CASHBOX_ROBOKASSA_CHECK_NOT_FOUND')));
288 }
289
290 return $onAfterProcessCheckResult;
291 }
292
305 protected function send(string $url, Sale\Payment $payment, array $fields, string $method = self::SEND_METHOD_HTTP_POST): Sale\Result
306 {
307 $result = new Sale\Result();
308
309 $startupHash = self::cutSign(
310 \base64_encode(
311 self::formatSign(
312 self::encode($fields)
313 )
314 )
315 );
316
317 $sign = self::cutSign(
318 \base64_encode(
319 \md5($startupHash . $this->getPaySystemSetting($payment, 'ROBOXCHANGE_SHOPPASSWORD'))
320 )
321 );
322
323 Logger::addDebugInfo(__CLASS__ . ': request data fields: ' . self::encode($fields));
324 Logger::addDebugInfo(__CLASS__ . ': request data: ' . $startupHash . '.' . $sign);
325
326 $httpClient = new Main\Web\HttpClient();
327 $response = $httpClient->post($url, $startupHash . '.' . $sign);
328 if ($response === false)
329 {
330 $result->addError(new Main\Error(Main\Localization\Loc::getMessage('SALE_CASHBOX_ROBOKASSA_ERROR_EMPTY_RESPONSE')));
331
332 $errors = $httpClient->getError();
333 foreach ($errors as $code => $message)
334 {
335 $result->addError(new Main\Error($message, $code));
336 }
337
338 return $result;
339 }
340
341 Logger::addDebugInfo(__CLASS__ . ': response data: ' . $response);
342
343 $response = static::decode($response);
344 if (!$response)
345 {
346 $result->addError(new Main\Error(Main\Localization\Loc::getMessage('SALE_CASHBOX_ROBOKASSA_ERROR_DECODE_RESPONSE')));
347 return $result;
348 }
349
350 $result->setData($response);
351
352 return $result;
353 }
354
359 protected static function formatSign(string $sign): string
360 {
361 return \strtr(
362 $sign,
363 [
364 '+' => '-',
365 '/' => '_',
366 ]
367 );
368 }
369
374 private static function cutSign(string $sign): string
375 {
376 return \preg_replace('/^(.*?)(=*)$/', '$1', $sign);
377 }
378
384 private static function encode(array $data)
385 {
386 return Main\Web\Json::encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
387 }
388
393 private static function decode($data)
394 {
395 try
396 {
398 }
399 catch (Main\ArgumentException $exception)
400 {
401 return false;
402 }
403 }
404
408 protected function getCheckTypeMap(): array
409 {
410 return [
411 SellCheck::getType() => 'full_payment',
412 AdvancePaymentCheck::getType() => 'advance',
413 PrepaymentCheck::getType() => 'prepayment',
414 FullPrepaymentCheck::getType() => 'full_prepayment',
415 ];
416 }
417
426 public static function getSettings($modelId = 0): array
427 {
428 $settings['VAT'] = [
429 'LABEL' => Main\Localization\Loc::getMessage('SALE_CASHBOX_ROBOKASSA_SETTINGS_VAT'),
430 'REQUIRED' => 'Y',
431 'ITEMS' => [
432 'NOT_VAT' => [
433 'TYPE' => 'STRING',
434 'LABEL' => Main\Localization\Loc::getMessage('SALE_CASHBOX_ROBOKASSA_SETTINGS_VAT_LABEL_NOT_VAT'),
435 'VALUE' => 'none'
436 ]
437 ]
438 ];
439
440 if (Main\Loader::includeModule('catalog'))
441 {
442 $dbRes = Catalog\VatTable::getList(['filter' => ['ACTIVE' => 'Y']]);
443 $vatList = $dbRes->fetchAll();
444 if ($vatList)
445 {
446 $defaultVatList = [
447 0 => self::CODE_VAT_0,
448 10 => self::CODE_VAT_10,
449 20 => self::CODE_VAT_20,
450 5 => self::CODE_VAT_5,
451 7 => self::CODE_VAT_7,
452 ];
453
454 foreach ($vatList as $vat)
455 {
456 $value = null;
457 if (isset($defaultVatList[(int)$vat['RATE']]))
458 {
459 $value = $defaultVatList[(int)$vat['RATE']];
460 }
461
462 $settings['VAT']['ITEMS'][(int)$vat['ID']] = [
463 'TYPE' => 'STRING',
464 'LABEL' => $vat['NAME'].' ['.(int)$vat['RATE'].'%]',
465 'VALUE' => $value
466 ];
467 }
468 }
469 }
470
471 $settings['TAX'] = [
472 'LABEL' => Main\Localization\Loc::getMessage('SALE_CASHBOX_ROBOKASSA_SETTINGS_SNO'),
473 'REQUIRED' => 'Y',
474 'ITEMS' => [
475 'SNO' => [
476 'TYPE' => 'ENUM',
477 'LABEL' => Main\Localization\Loc::getMessage('SALE_CASHBOX_ROBOKASSA_SETTINGS_SNO_LABEL'),
478 'VALUE' => 'osn',
479 'OPTIONS' => [
480 'osn' => Main\Localization\Loc::getMessage('SALE_CASHBOX_ROBOKASSA_SNO_OSN'),
481 'usn_income' => Main\Localization\Loc::getMessage('SALE_CASHBOX_ROBOKASSA_SNO_UI'),
482 'usn_income_outcome' => Main\Localization\Loc::getMessage('SALE_CASHBOX_ROBOKASSA_SNO_UIO'),
483 'envd' => Main\Localization\Loc::getMessage('SALE_CASHBOX_ROBOKASSA_SNO_ENVD'),
484 'esn' => Main\Localization\Loc::getMessage('SALE_CASHBOX_ROBOKASSA_SNO_ESN'),
485 'patent' => Main\Localization\Loc::getMessage('SALE_CASHBOX_ROBOKASSA_SNO_PATENT')
486 ]
487 ]
488 ]
489 ];
490
491 return $settings;
492 }
493
494 public static function getPaySystemCodeForKkm(): string
495 {
496 return 'ROBOXCHANGE_SHOPLOGIN';
497 }
498
502 private function getPaymentObjectMap(): array
503 {
504 return [
505 Check::PAYMENT_OBJECT_COMMODITY => 'commodity',
510 Check::PAYMENT_OBJECT_GAMBLING_BET => 'gambling_bet',
511 Check::PAYMENT_OBJECT_GAMBLING_PRIZE => 'gambling_prize',
513 Check::PAYMENT_OBJECT_LOTTERY_PRIZE => 'lottery_prize',
514 Check::PAYMENT_OBJECT_INTELLECTUAL_ACTIVITY => 'intellectual_activity',
515 Check::PAYMENT_OBJECT_AGENT_COMMISSION => 'agent_commission',
516 Check::PAYMENT_OBJECT_COMPOSITE => 'composite',
518 Check::PAYMENT_OBJECT_PROPERTY_RIGHT => 'property_right',
519 Check::PAYMENT_OBJECT_NON_OPERATING_GAIN => 'non-operating_gain',
520 Check::PAYMENT_OBJECT_SALES_TAX => 'sales_tax',
521 Check::PAYMENT_OBJECT_RESORT_FEE => 'resort_fee',
524 Check::PAYMENT_OBJECT_PENSION_INSURANCE_IP => 'pension_insurance_ip',
525 Check::PAYMENT_OBJECT_PENSION_INSURANCE => 'pension_insurance',
526 Check::PAYMENT_OBJECT_MEDICAL_INSURANCE_IP => 'medical_insurance_ip',
527 Check::PAYMENT_OBJECT_MEDICAL_INSURANCE => 'medical_insurance',
528 Check::PAYMENT_OBJECT_SOCIAL_INSURANCE => 'social_insurance',
529 Check::PAYMENT_OBJECT_CASINO_PAYMENT => 'casino_payment',
534 ];
535 }
536}
if(!Loader::includeModule('catalog')) if(!AccessController::getCurrent() ->check(ActionDictionary::ACTION_PRICE_EDIT)) if(!check_bitrix_sessid()) $request
Определения catalog_reindex.php:36
static getInstance()
Определения application.php:98
Определения error.php:15
static loadMessages($file)
Определения loc.php:65
static getMessage($code, $replace=null, $language=null)
Определения loc.php:30
static getList(array $parameters=array())
Определения datamanager.php:431
static decode($data)
Определения json.php:50
static encode($data, $options=null)
Определения json.php:22
const PARAM_CALCULATION_ATTR
Определения abstractcheck.php:31
const PARAM_FISCAL_DOC_ATTR
Определения abstractcheck.php:24
const PARAM_FISCAL_DOC_NUMBER
Определения abstractcheck.php:23
getValueFromSettings($name, $code)
Определения cashbox.php:203
getPaySystemSetting(Sale\Payment $payment, string $code)
Определения cashboxpaysystem.php:80
checkParams(Check $check)
Определения cashboxpaysystem.php:91
send(string $url, Sale\Payment $payment, array $fields, string $method=self::SEND_METHOD_HTTP_POST)
Определения cashboxrobokassa.php:305
onAfterProcessCheck(Sale\Result $result, Sale\Payment $payment)
Определения cashboxrobokassa.php:238
static getSettings($modelId=0)
Определения cashboxrobokassa.php:426
getDataForCheck(Sale\Payment $payment)
Определения cashboxrobokassa.php:192
buildCheckQuery(Check $check)
Определения cashboxrobokassa.php:49
static extractCheckData(array $data)
Определения cashboxrobokassa.php:126
processCheckResult(Sale\Result $result)
Определения cashboxrobokassa.php:200
static formatSign(string $sign)
Определения cashboxrobokassa.php:359
processPrintResult(Sale\Result $result)
Определения cashboxrobokassa.php:164
const PAYMENT_OBJECT_EXCISE
Определения check.php:25
const PAYMENT_OBJECT_LOTTERY
Определения check.php:31
const PAYMENT_OBJECT_SOCIAL_INSURANCE
Определения check.php:47
const PAYMENT_OBJECT_COMPOSITE
Определения check.php:35
const PAYMENT_OBJECT_LOTTERY_PRIZE
Определения check.php:32
const PAYMENT_OBJECT_MEDICAL_INSURANCE_IP
Определения check.php:45
const PAYMENT_OBJECT_COMMODITY_MARKING_EXCISE
Определения check.php:50
const PAYMENT_OBJECT_NON_OPERATING_GAIN
Определения check.php:38
getDataForCheck()
Определения check.php:238
const PAYMENT_OBJECT_COMMODITY_MARKING_NO_MARKING_EXCISE
Определения check.php:49
const PAYMENT_OBJECT_RESORT_FEE
Определения check.php:40
const PAYMENT_OBJECT_PENSION_INSURANCE_IP
Определения check.php:43
const PAYMENT_OBJECT_PROPERTY_RIGHT
Определения check.php:37
const PAYMENT_OBJECT_GAMBLING_PRIZE
Определения check.php:30
const PAYMENT_OBJECT_COMMODITY_MARKING
Определения check.php:52
const PAYMENT_OBJECT_COMMODITY
Определения check.php:24
const PAYMENT_OBJECT_AGENT_COMMISSION
Определения check.php:34
const PAYMENT_OBJECT_ANOTHER
Определения check.php:36
const PAYMENT_OBJECT_DEPOSIT
Определения check.php:41
const PAYMENT_OBJECT_MEDICAL_INSURANCE
Определения check.php:46
const PAYMENT_OBJECT_SERVICE
Определения check.php:27
const PAYMENT_OBJECT_INTELLECTUAL_ACTIVITY
Определения check.php:33
const PAYMENT_OBJECT_PAYMENT
Определения check.php:28
const PAYMENT_OBJECT_EXPENSE
Определения check.php:42
const PAYMENT_OBJECT_GAMBLING_BET
Определения check.php:29
const PAYMENT_OBJECT_CASINO_PAYMENT
Определения check.php:48
const PAYMENT_OBJECT_PENSION_INSURANCE
Определения check.php:44
const PAYMENT_OBJECT_JOB
Определения check.php:26
const PAYMENT_OBJECT_COMMODITY_MARKING_NO_MARKING
Определения check.php:51
const PAYMENT_OBJECT_SALES_TAX
Определения check.php:39
static getPaymentByCheck(Check $check)
Определения checkmanager.php:1910
static getList(array $parameters=array())
Определения checkmanager.php:1722
static getObjectById($id)
Определения checkmanager.php:1698
static addDebugInfo(?string $message, $cashboxId=null)
Определения logger.php:43
static getType()
Определения sellcheck.php:19
Определения payment.php:19
static roundPrecision($value)
Определения pricemaths.php:16
$data['IS_AVAILABLE']
Определения .description.php:13
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
$result
Определения get_property_values.php:14
$protocol
Определения .description.php:9
$errors
Определения iblock_catalog_edit.php:74
if(!is_null($config))($config as $configItem)(! $configItem->isVisible()) $code
Определения options.php:195
$status
Определения session.php:10
$payment
Определения payment.php:14
$sign
Определения payment.php:69
$message
Определения payment.php:8
$settings
Определения product_settings.php:43
if(empty($signedUserToken)) $key
Определения quickway.php:257
$vat
Определения template.php:273
$response
Определения result.php:21
$method
Определения index.php:27
$url
Определения iframe.php:7
$dbRes
Определения yandex_detail.php:168
$vatList
Определения yandex_run.php:916