1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
handler.php
См. документацию.
1<?php
2
3namespace Sale\Handlers\PaySystem;
4
5use Bitrix\Main,
6 Bitrix\Main\Request,
7 Bitrix\Main\Localization\Loc,
8 Bitrix\Main\Web\HttpClient,
9 Bitrix\Sale\Payment,
10 Bitrix\Sale\PaySystem,
11 Bitrix\Sale\PriceMaths,
12 Bitrix\Sale\PaymentCollection,
13 Bitrix\Sale\BusinessValue;
14
15Loc::loadMessages(__FILE__);
16
21class SkbHandler
23 implements PaySystem\IRefund
24{
25 private const MODE_SKB = 'skb';
26 private const MODE_DELOBANK = 'delobank';
27 private const MODE_GAZENERGOBANK = 'gazenergobank';
28
29 private const TAG_BITRIX_24 = 'Bitrix24';
30
31 private const RESPONSE_CODE_SUCCESS = [
32 '0',
33 'RQ00000'
34 ];
35
36 private const HTTP_CODE_OK = 200;
37 private const HTTP_CODE_LOCKED = 423;
38
39 private const PAYMENT_STATUS_NOT_STARTED = 'NTST';
40 private const PAYMENT_STATUS_ACCEPTED = 'ACWP';
41 private const PAYMENT_STATUS_REJECTED = 'RJCT';
42
48 public function initiatePay(Payment $payment, Request $request = null): PaySystem\ServiceResult
49 {
50 $result = new PaySystem\ServiceResult();
51
52 $createPaymentResult = $this->createPayment($payment);
53 if (!$createPaymentResult->isSuccess())
54 {
55 $result->addErrors($createPaymentResult->getErrors());
56 return $result;
57 }
58
59 $result->setPsData($createPaymentResult->getPsData());
60 $paymentData = $createPaymentResult->getData();
61
62 $params['CURRENCY'] = $payment->getField('CURRENCY');
63 $params['SUM'] = PriceMaths::roundPrecision($payment->getSum());
64 $params['URL'] = $paymentData['payload'];
65 $params['QR_CODE_IMAGE'] = $paymentData['qrImage'];
66 $this->setExtraParams($params);
67
68 $showTemplateResult = $this->showTemplate($payment, 'template');
69 if ($showTemplateResult->isSuccess())
70 {
71 $result->setTemplate($showTemplateResult->getTemplate());
72 }
73 else
74 {
75 $result->addErrors($showTemplateResult->getErrors());
76 }
77
78 return $result;
79 }
80
85 private function changeUserPassword(Payment $payment): PaySystem\ServiceResult
86 {
87 $result = new PaySystem\ServiceResult();
88
89 $params = [
90 'login' => $this->getBusinessValue($payment, 'SKB_LOGIN'),
91 'password' => $this->getBusinessValue($payment, 'SKB_PASSWORD'),
92 'newPassword' => Main\Security\Random::getString(10, true),
93 ];
94
95 $sendResult = $this->send($payment, 'changeUserPassword', $params);
96 if ($sendResult->isSuccess())
97 {
98 $updatePasswordResult = $this->updatePassword($payment, $params['newPassword']);
99 if (!$updatePasswordResult->isSuccess())
100 {
101 $result->addErrors($updatePasswordResult->getErrors());
102 }
103 }
104 else
105 {
106 $result->addErrors($sendResult->getErrors());
107 }
108
109 return $result;
110 }
111
117 private function updatePassword(Payment $payment, string $password): PaySystem\ServiceResult
118 {
119 $result = new PaySystem\ServiceResult();
120
121 $oldMapping = BusinessValue::getMapping('SKB_PASSWORD', $this->service->getConsumerName(), $payment->getPersonTypeId());
122 $updateMappingResult = BusinessValue::updateMapping(
123 'SKB_PASSWORD',
124 $oldMapping,
125 [
126 'PROVIDER_KEY' => 'VALUE',
127 'PROVIDER_VALUE' => $password,
128 ]
129 );
130 if (!$updateMappingResult->isSuccess())
131 {
132 $result->addErrors($updateMappingResult->getErrors());
133 }
134
135 return $result;
136 }
137
142 private function createPayment(Payment $payment): PaySystem\ServiceResult
143 {
144 $result = new PaySystem\ServiceResult();
145
146 $params = [
147 'messageId' => self::getMessageId(),
148 'agentId' => $this->getAgentId(),
149 'merchantId' => $this->getBusinessValue($payment, 'SKB_MERCHANT_ID'),
150 'paymentId' => (string)$payment->getId(),
151 'amount' => (string)($payment->getSum() * 100),
152 'currency' => $payment->getField('CURRENCY'),
153 'paymentPurpose' => $this->getAdditionalInfo($payment),
154 'templateVersion' => '01',
155 'qrcType' => '02',
156 'mediaType' => 'image/png',
157 'width' => 450,
158 'height' => 450,
159 ];
160
161 $sendResult = $this->send($payment, 'register', $params);
162 if (!$sendResult->isSuccess())
163 {
164 $result->addErrors($sendResult->getErrors());
165 return $result;
166 }
167
168 $sendData = $sendResult->getData();
169 $result->setPsData(['PS_INVOICE_ID' => $sendData['qrcId']]);
170 $result->setData($sendData);
171
172 return $result;
173 }
174
178 public function getCurrencyList(): array
179 {
180 return ['RUB'];
181 }
182
188 public function processRequest(Payment $payment, Request $request): PaySystem\ServiceResult
189 {
190 $result = new PaySystem\ServiceResult();
191
192 $secretKey = $this->getBusinessValue($payment, 'SKB_SECRET_KEY');
193 if ($secretKey && !$this->isSignCorrect($request, $secretKey))
194 {
195 $result->addError(new Main\Error(Loc::getMessage('SALE_HPS_SKB_ERROR_SIGN')));
196 return $result;
197 }
198
199 $paymentStatusResult = $this->getSkbPaymentStatus($payment);
200 if (!$paymentStatusResult->isSuccess())
201 {
202 $result->addErrors($paymentStatusResult->getErrors());
203 return $result;
204 }
205
206 return $this->processSkbPaymentStatus($payment, $paymentStatusResult);
207 }
208
213 private function getSkbPaymentStatus(Payment $payment): PaySystem\ServiceResult
214 {
215 $result = new PaySystem\ServiceResult();
216
217 $params = [
218 'messageId' => self::getMessageId(),
219 'agentId' => $this->getAgentId(),
220 'qrcIds' => [
221 $payment->getField('PS_INVOICE_ID'),
222 ],
223 ];
224
225 $sendResult = $this->send($payment, 'getPaymentsStatus', $params);
226 if (!$sendResult->isSuccess())
227 {
228 $result->addErrors($sendResult->getErrors());
229 return $result;
230 }
231
232 $sendData = $sendResult->getData();
233 if (empty($sendData['payments']))
234 {
235 $result->addError(new Main\Error(Loc::getMessage('SALE_HPS_SKB_ERROR_EMPTY_PAYMENTS')));
236 }
237
238 $result->setData($sendData);
239
240 return $result;
241 }
242
243 private function processSkbPaymentStatus(Payment $payment, PaySystem\ServiceResult $paymentStatusResult): PaySystem\ServiceResult
244 {
245 $result = new PaySystem\ServiceResult();
246
247 $paymentStatusData = $paymentStatusResult->getData();
248 $skbPayment = current($paymentStatusData['payments']);
249
250 if (!empty($skbPayment['status']))
251 {
252 $status = $skbPayment['status'];
253 $fields = [
254 'PS_STATUS_CODE' => $status,
255 'PS_SUM' => $payment->getSum(),
256 'PS_CURRENCY' => $payment->getField('CURRENCY'),
257 'PS_RESPONSE_DATE' => new Main\Type\DateTime(),
258 'PS_STATUS' => 'N',
259 ];
260
261 $psStatusDescription = Loc::getMessage('SALE_HPS_SKB_STATUS_DESCRIPTION_' . $status);
262 if ($psStatusDescription)
263 {
264 $fields['PS_STATUS_DESCRIPTION'] = $psStatusDescription;
265 }
266
267 $additionalPsStatusDescription = '';
268 if (!empty($skbPayment['trxId']))
269 {
270 $additionalPsStatusDescription = Loc::getMessage('SALE_HPS_SKB_OPERATION_ID_DESCRIPTION', [
271 '#TX_ID#' => $skbPayment['trxId'],
272 ]);
273 }
274 elseif (!empty($skbPayment['qrcId']))
275 {
276 $additionalPsStatusDescription = Loc::getMessage('SALE_HPS_SKB_QR_CODE_ID_DESCRIPTION', [
277 '#QR_CODE_ID#' => $skbPayment['qrcId'],
278 ]);
279 }
280
281 if ($additionalPsStatusDescription)
282 {
283 $fields['PS_STATUS_DESCRIPTION'] =
284 !empty($fields['PS_STATUS_DESCRIPTION'])
285 ? $fields['PS_STATUS_DESCRIPTION'] . ' ' . $additionalPsStatusDescription
286 : $additionalPsStatusDescription
287 ;
288 }
289
290 if ($status === self::PAYMENT_STATUS_ACCEPTED)
291 {
292 $fields['PS_STATUS'] = 'Y';
293
294 PaySystem\Logger::addDebugInfo(
295 __CLASS__ . ': PS_CHANGE_STATUS_PAY=' . $this->getBusinessValue($payment, 'PS_CHANGE_STATUS_PAY')
296 );
297
298 if ($this->getBusinessValue($payment, 'PS_CHANGE_STATUS_PAY') === 'Y')
299 {
300 $result->setOperationType(PaySystem\ServiceResult::MONEY_COMING);
301 }
302 }
303 elseif ($status === self::PAYMENT_STATUS_NOT_STARTED)
304 {
305 $result->addError(
306 new Main\Error(Loc::getMessage('SALE_HPS_SKB_ERROR_STATUS_NOT_STARTED'))
307 );
308 }
309 elseif ($status === self::PAYMENT_STATUS_REJECTED)
310 {
311 $result->addError(
312 new Main\Error(Loc::getMessage('SALE_HPS_SKB_ERROR_STATUS_REJECTED'))
313 );
314 }
315
316 $result->setPsData($fields);
317
318 if (!$result->isSuccess())
319 {
320 $result->setOperationType(PaySystem\ServiceResult::MONEY_LEAVING);
321 }
322 }
323 else
324 {
325 $result->addError(
326 new Main\Error(Loc::getMessage('SALE_HPS_SKB_ERROR_STATUS_NOT_FOUND'))
327 );
328 }
329
330 return $result;
331 }
332
337 public function getPaymentIdFromRequest(Request $request)
338 {
339 return $request->get('paymentId');
340 }
341
345 public static function getIndicativeFields(): array
346 {
347 return ['qrcId', 'paymentId', 'txStatus', 'txId', 'debitorId', 'amount', 'timestamp', 'sign'];
348 }
349
355 protected function isSignCorrect(Request $request, $secretKey): bool
356 {
357 $hash = md5(
358 $request->get('qrcId')
359 . $request->get('timestamp')
360 . $request->get('txId')
361 . $request->get('amount')
362 . $secretKey
363 );
364
365 return $hash === $request->get('sign');
366 }
367
373 public function refund(Payment $payment, $refundableSum): PaySystem\ServiceResult
374 {
375 $result = new PaySystem\ServiceResult();
376
377 $checkRefundTransferResult = $this->checkRefundTransfer($payment, $refundableSum);
378 if (!$checkRefundTransferResult->isSuccess())
379 {
380 $result->addErrors($checkRefundTransferResult->getErrors());
381 return $result;
382 }
383
384 $checkRefundTransferData = $checkRefundTransferResult->getData();
385 if (!$checkRefundTransferData['corelationId'])
386 {
387 $result->addError(new Main\Error(Loc::getMessage('SALE_HPS_SKB_ERROR_CORELATION_ID')));
388 return $result;
389 }
390
391 $approveRefundTransferResult = $this->approveRefundTransfer($payment, $checkRefundTransferData['corelationId']);
392 if (!$approveRefundTransferResult->isSuccess())
393 {
394 $result->addErrors($approveRefundTransferResult->getErrors());
395 return $result;
396 }
397
398 $approveRefundTransferData = $approveRefundTransferResult->getData();
399 if ($approveRefundTransferData['status'] === static::PAYMENT_STATUS_ACCEPTED)
400 {
401 $result->setOperationType(PaySystem\ServiceResult::MONEY_LEAVING);
402 }
403
404 return $result;
405 }
406
412 private function checkRefundTransfer(Payment $payment, $refundableSum): PaySystem\ServiceResult
413 {
414 $result = new PaySystem\ServiceResult();
415
416 $paymentStatusResult = $this->getSkbPaymentStatus($payment);
417 if (!$paymentStatusResult->isSuccess())
418 {
419 $result->addErrors($paymentStatusResult->getErrors());
420 return $result;
421 }
422
423 $paymentStatusData = $paymentStatusResult->getData();
424 $skbPayment = current($paymentStatusData['payments']);
425
426 $params = [
427 'messageId' => self::getMessageId(),
428 'trxId' => $skbPayment['trxId'],
429 'amount' => (string)($refundableSum * 100),
430 ];
431
432 $sendResult = $this->send($payment, 'checkRefundTransfer', $params);
433 if ($sendResult->isSuccess())
434 {
435 $result->setData($sendResult->getData());
436 }
437 else
438 {
439 $result->addErrors($sendResult->getErrors());
440 }
441
442 return $result;
443 }
444
450 private function approveRefundTransfer(Payment $payment, string $corelationId): PaySystem\ServiceResult
451 {
452 $result = new PaySystem\ServiceResult();
453
454 $params = [
455 'messageId' => self::getMessageId(),
456 'corelationId' => $corelationId,
457 ];
458
459 $sendResult = $this->send($payment, 'approveRefundTransfer', $params);
460 if ($sendResult->isSuccess())
461 {
462 $result->setData($sendResult->getData());
463 }
464 else
465 {
466 $result->addErrors($sendResult->getErrors());
467 }
468
469 return $result;
470 }
471
478 private function send(Payment $payment, $action, array $params = []): PaySystem\ServiceResult
479 {
480 $url = $this->getUrl($payment, $action);
481
482 $result = $this->makeQuery($url, $params, $this->getHeaders($payment));
483 if (!$result->isSuccess() && $result->getErrorCollection()->getErrorByCode(self::HTTP_CODE_LOCKED))
484 {
485 $changeUserPasswordResult = $this->changeUserPassword($payment);
486 if (!$changeUserPasswordResult->isSuccess())
487 {
488 $result->addErrors($changeUserPasswordResult->getErrors());
489 return $result;
490 }
491
492 $result = $this->makeQuery($url, $params, $this->getHeaders($payment));
493 }
494
495 if (!$result->isSuccess())
496 {
497 return $result;
498 }
499
500 $sendData = $result->getData();
501 $verifyResult = $this->verifyResponse($sendData['response']);
502 if ($verifyResult->isSuccess())
503 {
504 $result->setData(static::decode($sendData['response']));
505 }
506 else
507 {
508 $result->addErrors($verifyResult->getErrors());
509 }
510
511 return $result;
512 }
513
520 private function makeQuery($url, array $params = [], array $headers = []): PaySystem\ServiceResult
521 {
522 $result = new PaySystem\ServiceResult();
523
524 $httpClient = new HttpClient();
525 $httpClient->setHeaders($headers);
526
527 $postData = static::encode($params);
528 PaySystem\Logger::addDebugInfo(__CLASS__ . ': request data: ' . $postData);
529
530 $response = $httpClient->post($url, $postData);
531 if ($response === false)
532 {
533 $result->addError(new Main\Error(Loc::getMessage('SALE_HPS_SKB_ERROR_EMPTY_RESPONSE')));
534 foreach ($httpClient->getError() as $code => $message)
535 {
536 $result->addError(new Main\Error($message, $code));
537 }
538
539 return $result;
540 }
541
542 PaySystem\Logger::addDebugInfo(__CLASS__ . ': response data: ' . $response);
543
544 $httpStatus = $httpClient->getStatus();
545 if ($httpStatus === self::HTTP_CODE_OK)
546 {
547 $result->setData(['response' => $response]);
548 }
549 else
550 {
551 $errorMessage = Loc::getMessage('SALE_HPS_SKB_ERROR_STATUS_'.$httpStatus);
552 if (!$errorMessage)
553 {
554 $errorMessage = Loc::getMessage(
555 'SALE_HPS_SKB_ERROR_STATUS_UNKNOWN',
556 [
557 '#STATUS#' => $httpStatus,
558 ]
559 );
560 }
561
562 $result->addError(new Main\Error($errorMessage, $httpStatus));
563 }
564
565 return $result;
566 }
567
572 private function verifyResponse($response): PaySystem\ServiceResult
573 {
574 $result = new PaySystem\ServiceResult();
575
576 $responseData = static::decode($response);
577 if ($response && !$responseData)
578 {
579 $result->addError(new Main\Error(Loc::getMessage('SALE_HPS_SKB_ERROR_DECODE_RESPONSE')));
580 return $result;
581 }
582
583 if (isset($responseData['errCode']) && !\in_array($responseData['errCode'], self::RESPONSE_CODE_SUCCESS, true))
584 {
585 $result->addError(new Main\Error($responseData['errMess'], $responseData['errCode']));
586 }
587 elseif (isset($responseData['moreInformation'], $responseData['httpCode']))
588 {
589 $result->addError(new Main\Error($responseData['moreInformation'], $responseData['httpCode']));
590 }
591
592 return $result;
593 }
594
599 protected function isTestMode(Payment $payment = null): bool
600 {
601 return $this->getBusinessValue($payment, 'SKB_TEST_MODE') === 'Y';
602 }
603
607 protected function getUrlList(): array
608 {
609 $testUrl = 'https://test.api.sinara.ru:443/';
610 $activeUrl = 'https://public.api.sinara.ru:443/';
611
612 return [
613 'register' => [
614 self::TEST_URL => $testUrl . 'qr/register',
615 self::ACTIVE_URL => $activeUrl . 'qr/register',
616 ],
617 'getPaymentsStatus' => [
618 self::TEST_URL => $testUrl . 'qr/getpaymentsstatus',
619 self::ACTIVE_URL => $activeUrl . 'qr/getpaymentsstatus',
620 ],
621 'checkRefundTransfer' => [
622 self::TEST_URL => $testUrl . 'refund/CheckRefundTransfer',
623 self::ACTIVE_URL => $activeUrl . 'refund/CheckRefundTransfer',
624 ],
625 'approveRefundTransfer' => [
626 self::TEST_URL => $testUrl . 'refund/ApproveRefundTransfer',
627 self::ACTIVE_URL => $activeUrl . 'refund/ApproveRefundTransfer',
628 ],
629 'changeUserPassword' => [
630 self::TEST_URL => $testUrl . 'user/changeUserPassword',
631 self::ACTIVE_URL => $activeUrl . 'user/changeUserPassword',
632 ],
633 ];
634 }
635
640 protected function getAdditionalInfo(Payment $payment)
641 {
643 $collection = $payment->getCollection();
644 $order = $collection->getOrder();
645 $userEmail = $order->getPropertyCollection()->getUserEmail();
646
647 $description = str_replace(
648 [
649 '#PAYMENT_NUMBER#',
650 '#ORDER_NUMBER#',
651 '#PAYMENT_ID#',
652 '#ORDER_ID#',
653 '#USER_EMAIL#'
654 ],
655 [
656 $payment->getField('ACCOUNT_NUMBER'),
657 $order->getField('ACCOUNT_NUMBER'),
658 $payment->getId(),
659 $order->getId(),
660 ($userEmail) ? $userEmail->getValue() : ''
661 ],
662 $this->getBusinessValue($payment, 'SKB_ADDITIONAL_INFO')
663 );
664
665 return mb_substr($description, 0, 140);
666 }
667
672 private function getBasicAuthString(Payment $payment): string
673 {
674 return base64_encode(
675 $this->getBusinessValue($payment, 'SKB_LOGIN')
676 . ':'
677 . $this->getBusinessValue($payment, 'SKB_PASSWORD')
678 );
679 }
680
685 private function getHeaders(Payment $payment): array
686 {
687 return [
688 'Authorization' => 'Basic ' . $this->getBasicAuthString($payment),
689 'Content-Type' => 'application/json',
690 'User-Agent' => self::TAG_BITRIX_24,
691 ];
692 }
693
697 private static function getMessageId(): string
698 {
699 return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
700 mt_rand(0, 0xffff), mt_rand(0, 0xffff),
701 mt_rand(0, 0xffff),
702 mt_rand(0, 0x0fff) | 0x4000,
703 mt_rand(0, 0x3fff) | 0x8000,
704 mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
705 );
706 }
707
711 private function getAgentId(): string
712 {
713 $agentList = [
714 self::MODE_SKB => 'A00000000001',
715 self::MODE_DELOBANK => 'A00000000001',
716 self::MODE_GAZENERGOBANK => 'A00000000023',
717 ];
718
719 return $agentList[$this->service->getField('PS_MODE')];
720 }
721
725 public static function getHandlerModeList(): array
726 {
727 return PaySystem\Manager::getHandlerDescription('Skb')['HANDLER_MODE_LIST'];
728 }
729
734 private static function encode(array $data)
735 {
736 return Main\Web\Json::encode($data, JSON_UNESCAPED_UNICODE);
737 }
738
743 private static function decode($data)
744 {
745 try
746 {
747 return Main\Web\Json::decode($data);
748 }
749 catch (Main\ArgumentException $exception)
750 {
751 return false;
752 }
753 }
754}
$hash
Определения ajax_redirector.php:8
if(!Loader::includeModule('catalog')) if(!AccessController::getCurrent() ->check(ActionDictionary::ACTION_PRICE_EDIT)) if(!check_bitrix_sessid()) $request
Определения catalog_reindex.php:36
getUrl(Payment $payment=null, $action)
Определения baseservicehandler.php:343
showTemplate(Payment $payment=null, $template='')
Определения baseservicehandler.php:59
getBusinessValue(Payment $payment=null, $code)
Определения baseservicehandler.php:184
$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
if(Loader::includeModule( 'bitrix24')) elseif(Loader::includeModule('intranet') &&CIntranetUtils::getPortalZone() !=='ru') $description
Определения .description.php:24
while($arParentIBlockProperty=$dbParentIBlockProperty->Fetch()) $errorMessage
if(!is_null($config))($config as $configItem)(! $configItem->isVisible()) $code
Определения options.php:195
$status
Определения session.php:10
$password
Определения mysql_to_pgsql.php:34
trait Error
Определения error.php:11
$payment
Определения payment.php:14
$order
Определения payment.php:8
$message
Определения payment.php:8
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
Определения prolog_main_admin.php:393
if($inWords) echo htmlspecialcharsbx(Number2Word_Rus(roundEx($totalVatSum $params['CURRENCY']
Определения template.php:799
$response
Определения result.php:21
$postData
Определения index.php:29
$action
Определения file_dialog.php:21
$url
Определения iframe.php:7
$fields
Определения yandex_run.php:501