3namespace Sale\Handlers\PaySystem;
7 Bitrix\Main\Localization\Loc,
8 Bitrix\Main\Web\HttpClient,
10 Bitrix\Sale\PaySystem,
11 Bitrix\Sale\PriceMaths,
12 Bitrix\Sale\PaymentCollection,
13 Bitrix\Sale\BusinessValue;
15Loc::loadMessages(__FILE__);
25 private const MODE_SKB =
'skb';
26 private const MODE_DELOBANK =
'delobank';
27 private const MODE_GAZENERGOBANK =
'gazenergobank';
29 private const TAG_BITRIX_24 =
'Bitrix24';
31 private const RESPONSE_CODE_SUCCESS = [
36 private const HTTP_CODE_OK = 200;
37 private const HTTP_CODE_LOCKED = 423;
39 private const PAYMENT_STATUS_NOT_STARTED =
'NTST';
40 private const PAYMENT_STATUS_ACCEPTED =
'ACWP';
41 private const PAYMENT_STATUS_REJECTED =
'RJCT';
48 public function initiatePay(Payment
$payment, Request
$request =
null): PaySystem\ServiceResult
50 $result =
new PaySystem\ServiceResult();
52 $createPaymentResult = $this->createPayment(
$payment);
53 if (!$createPaymentResult->isSuccess())
55 $result->addErrors($createPaymentResult->getErrors());
59 $result->setPsData($createPaymentResult->getPsData());
60 $paymentData = $createPaymentResult->getData();
64 $params[
'URL'] = $paymentData[
'payload'];
65 $params[
'QR_CODE_IMAGE'] = $paymentData[
'qrImage'];
69 if ($showTemplateResult->isSuccess())
71 $result->setTemplate($showTemplateResult->getTemplate());
75 $result->addErrors($showTemplateResult->getErrors());
85 private function changeUserPassword(Payment
$payment): PaySystem\ServiceResult
87 $result =
new PaySystem\ServiceResult();
92 'newPassword' => Main\Security\Random::getString(10,
true),
96 if ($sendResult->isSuccess())
98 $updatePasswordResult = $this->updatePassword(
$payment,
$params[
'newPassword']);
99 if (!$updatePasswordResult->isSuccess())
101 $result->addErrors($updatePasswordResult->getErrors());
106 $result->addErrors($sendResult->getErrors());
117 private function updatePassword(Payment
$payment,
string $password): PaySystem\ServiceResult
119 $result =
new PaySystem\ServiceResult();
121 $oldMapping = BusinessValue::getMapping(
'SKB_PASSWORD', $this->service->getConsumerName(),
$payment->getPersonTypeId());
122 $updateMappingResult = BusinessValue::updateMapping(
126 'PROVIDER_KEY' =>
'VALUE',
130 if (!$updateMappingResult->isSuccess())
132 $result->addErrors($updateMappingResult->getErrors());
142 private function createPayment(Payment
$payment): PaySystem\ServiceResult
144 $result =
new PaySystem\ServiceResult();
147 'messageId' => self::getMessageId(),
148 'agentId' => $this->getAgentId(),
150 'paymentId' => (string)
$payment->getId(),
151 'amount' => (string)(
$payment->getSum() * 100),
152 'currency' =>
$payment->getField(
'CURRENCY'),
153 'paymentPurpose' => $this->getAdditionalInfo(
$payment),
154 'templateVersion' =>
'01',
156 'mediaType' =>
'image/png',
162 if (!$sendResult->isSuccess())
164 $result->addErrors($sendResult->getErrors());
168 $sendData = $sendResult->getData();
169 $result->setPsData([
'PS_INVOICE_ID' => $sendData[
'qrcId']]);
188 public function processRequest(Payment
$payment, Request
$request): PaySystem\ServiceResult
190 $result =
new PaySystem\ServiceResult();
193 if ($secretKey && !$this->isSignCorrect(
$request, $secretKey))
195 $result->addError(
new Main\
Error(Loc::getMessage(
'SALE_HPS_SKB_ERROR_SIGN')));
199 $paymentStatusResult = $this->getSkbPaymentStatus(
$payment);
200 if (!$paymentStatusResult->isSuccess())
202 $result->addErrors($paymentStatusResult->getErrors());
206 return $this->processSkbPaymentStatus(
$payment, $paymentStatusResult);
213 private function getSkbPaymentStatus(Payment
$payment): PaySystem\ServiceResult
215 $result =
new PaySystem\ServiceResult();
218 'messageId' => self::getMessageId(),
219 'agentId' => $this->getAgentId(),
221 $payment->getField(
'PS_INVOICE_ID'),
226 if (!$sendResult->isSuccess())
228 $result->addErrors($sendResult->getErrors());
232 $sendData = $sendResult->getData();
233 if (empty($sendData[
'payments']))
235 $result->addError(
new Main\
Error(Loc::getMessage(
'SALE_HPS_SKB_ERROR_EMPTY_PAYMENTS')));
243 private function processSkbPaymentStatus(Payment
$payment, PaySystem\ServiceResult $paymentStatusResult): PaySystem\ServiceResult
245 $result =
new PaySystem\ServiceResult();
247 $paymentStatusData = $paymentStatusResult->getData();
248 $skbPayment = current($paymentStatusData[
'payments']);
250 if (!empty($skbPayment[
'status']))
252 $status = $skbPayment[
'status'];
256 'PS_CURRENCY' =>
$payment->getField(
'CURRENCY'),
257 'PS_RESPONSE_DATE' =>
new Main\Type\DateTime(),
261 $psStatusDescription = Loc::getMessage(
'SALE_HPS_SKB_STATUS_DESCRIPTION_' .
$status);
262 if ($psStatusDescription)
264 $fields[
'PS_STATUS_DESCRIPTION'] = $psStatusDescription;
267 $additionalPsStatusDescription =
'';
268 if (!empty($skbPayment[
'trxId']))
270 $additionalPsStatusDescription = Loc::getMessage(
'SALE_HPS_SKB_OPERATION_ID_DESCRIPTION', [
271 '#TX_ID#' => $skbPayment[
'trxId'],
274 elseif (!empty($skbPayment[
'qrcId']))
276 $additionalPsStatusDescription = Loc::getMessage(
'SALE_HPS_SKB_QR_CODE_ID_DESCRIPTION', [
277 '#QR_CODE_ID#' => $skbPayment[
'qrcId'],
281 if ($additionalPsStatusDescription)
283 $fields[
'PS_STATUS_DESCRIPTION'] =
284 !empty(
$fields[
'PS_STATUS_DESCRIPTION'])
285 ?
$fields[
'PS_STATUS_DESCRIPTION'] .
' ' . $additionalPsStatusDescription
286 : $additionalPsStatusDescription
290 if (
$status === self::PAYMENT_STATUS_ACCEPTED)
294 PaySystem\Logger::addDebugInfo(
300 $result->setOperationType(PaySystem\ServiceResult::MONEY_COMING);
306 new Main\
Error(Loc::getMessage(
'SALE_HPS_SKB_ERROR_STATUS_NOT_STARTED'))
312 new Main\
Error(Loc::getMessage(
'SALE_HPS_SKB_ERROR_STATUS_REJECTED'))
320 $result->setOperationType(PaySystem\ServiceResult::MONEY_LEAVING);
326 new Main\
Error(Loc::getMessage(
'SALE_HPS_SKB_ERROR_STATUS_NOT_FOUND'))
337 public function getPaymentIdFromRequest(Request
$request)
347 return [
'qrcId',
'paymentId',
'txStatus',
'txId',
'debitorId',
'amount',
'timestamp',
'sign'];
355 protected function isSignCorrect(Request
$request, $secretKey): bool
373 public function refund(Payment
$payment, $refundableSum): PaySystem\ServiceResult
375 $result =
new PaySystem\ServiceResult();
377 $checkRefundTransferResult = $this->checkRefundTransfer(
$payment, $refundableSum);
378 if (!$checkRefundTransferResult->isSuccess())
380 $result->addErrors($checkRefundTransferResult->getErrors());
384 $checkRefundTransferData = $checkRefundTransferResult->getData();
385 if (!$checkRefundTransferData[
'corelationId'])
387 $result->addError(
new Main\
Error(Loc::getMessage(
'SALE_HPS_SKB_ERROR_CORELATION_ID')));
391 $approveRefundTransferResult = $this->approveRefundTransfer(
$payment, $checkRefundTransferData[
'corelationId']);
392 if (!$approveRefundTransferResult->isSuccess())
394 $result->addErrors($approveRefundTransferResult->getErrors());
398 $approveRefundTransferData = $approveRefundTransferResult->getData();
399 if ($approveRefundTransferData[
'status'] === static::PAYMENT_STATUS_ACCEPTED)
401 $result->setOperationType(PaySystem\ServiceResult::MONEY_LEAVING);
412 private function checkRefundTransfer(Payment
$payment, $refundableSum): PaySystem\ServiceResult
414 $result =
new PaySystem\ServiceResult();
416 $paymentStatusResult = $this->getSkbPaymentStatus(
$payment);
417 if (!$paymentStatusResult->isSuccess())
419 $result->addErrors($paymentStatusResult->getErrors());
423 $paymentStatusData = $paymentStatusResult->getData();
424 $skbPayment = current($paymentStatusData[
'payments']);
427 'messageId' => self::getMessageId(),
428 'trxId' => $skbPayment[
'trxId'],
429 'amount' => (string)($refundableSum * 100),
433 if ($sendResult->isSuccess())
435 $result->setData($sendResult->getData());
439 $result->addErrors($sendResult->getErrors());
450 private function approveRefundTransfer(Payment
$payment,
string $corelationId): PaySystem\ServiceResult
452 $result =
new PaySystem\ServiceResult();
455 'messageId' => self::getMessageId(),
456 'corelationId' => $corelationId,
460 if ($sendResult->isSuccess())
462 $result->setData($sendResult->getData());
466 $result->addErrors($sendResult->getErrors());
483 if (!
$result->isSuccess() &&
$result->getErrorCollection()->getErrorByCode(self::HTTP_CODE_LOCKED))
485 $changeUserPasswordResult = $this->changeUserPassword(
$payment);
486 if (!$changeUserPasswordResult->isSuccess())
488 $result->addErrors($changeUserPasswordResult->getErrors());
500 $sendData =
$result->getData();
501 $verifyResult = $this->verifyResponse($sendData[
'response']);
502 if ($verifyResult->isSuccess())
504 $result->setData(static::decode($sendData[
'response']));
508 $result->addErrors($verifyResult->getErrors());
522 $result =
new PaySystem\ServiceResult();
524 $httpClient =
new HttpClient();
525 $httpClient->setHeaders($headers);
528 PaySystem\Logger::addDebugInfo(__CLASS__ .
': request data: ' .
$postData);
533 $result->addError(
new Main\
Error(Loc::getMessage(
'SALE_HPS_SKB_ERROR_EMPTY_RESPONSE')));
542 PaySystem\Logger::addDebugInfo(__CLASS__ .
': response data: ' .
$response);
544 $httpStatus = $httpClient->getStatus();
545 if ($httpStatus === self::HTTP_CODE_OK)
551 $errorMessage = Loc::getMessage(
'SALE_HPS_SKB_ERROR_STATUS_'.$httpStatus);
555 'SALE_HPS_SKB_ERROR_STATUS_UNKNOWN',
557 '#STATUS#' => $httpStatus,
572 private function verifyResponse(
$response): PaySystem\ServiceResult
574 $result =
new PaySystem\ServiceResult();
576 $responseData = static::decode(
$response);
579 $result->addError(
new Main\
Error(Loc::getMessage(
'SALE_HPS_SKB_ERROR_DECODE_RESPONSE')));
583 if (isset($responseData[
'errCode']) && !\in_array($responseData[
'errCode'], self::RESPONSE_CODE_SUCCESS,
true))
585 $result->addError(
new Main\
Error($responseData[
'errMess'], $responseData[
'errCode']));
587 elseif (isset($responseData[
'moreInformation'], $responseData[
'httpCode']))
589 $result->addError(
new Main\
Error($responseData[
'moreInformation'], $responseData[
'httpCode']));
599 protected function isTestMode(Payment
$payment =
null): bool
609 $testUrl =
'https://test.api.sinara.ru:443/';
610 $activeUrl =
'https://public.api.sinara.ru:443/';
614 self::TEST_URL => $testUrl .
'qr/register',
615 self::ACTIVE_URL => $activeUrl .
'qr/register',
617 'getPaymentsStatus' => [
618 self::TEST_URL => $testUrl .
'qr/getpaymentsstatus',
619 self::ACTIVE_URL => $activeUrl .
'qr/getpaymentsstatus',
621 'checkRefundTransfer' => [
622 self::TEST_URL => $testUrl .
'refund/CheckRefundTransfer',
623 self::ACTIVE_URL => $activeUrl .
'refund/CheckRefundTransfer',
625 'approveRefundTransfer' => [
626 self::TEST_URL => $testUrl .
'refund/ApproveRefundTransfer',
627 self::ACTIVE_URL => $activeUrl .
'refund/ApproveRefundTransfer',
629 'changeUserPassword' => [
630 self::TEST_URL => $testUrl .
'user/changeUserPassword',
631 self::ACTIVE_URL => $activeUrl .
'user/changeUserPassword',
640 protected function getAdditionalInfo(Payment
$payment)
643 $collection =
$payment->getCollection();
644 $order = $collection->getOrder();
645 $userEmail =
$order->getPropertyCollection()->getUserEmail();
656 $payment->getField(
'ACCOUNT_NUMBER'),
657 $order->getField(
'ACCOUNT_NUMBER'),
660 ($userEmail) ? $userEmail->getValue() :
''
662 $this->getBusinessValue(
$payment,
'SKB_ADDITIONAL_INFO')
672 private function getBasicAuthString(Payment
$payment): string
674 return base64_encode(
688 'Authorization' =>
'Basic ' . $this->getBasicAuthString(
$payment),
689 'Content-Type' =>
'application/json',
690 'User-Agent' => self::TAG_BITRIX_24,
697 private static function getMessageId(): string
699 return sprintf(
'%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
700 mt_rand(0, 0xffff), 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)
711 private function getAgentId(): string
714 self::MODE_SKB =>
'A00000000001',
715 self::MODE_DELOBANK =>
'A00000000001',
716 self::MODE_GAZENERGOBANK =>
'A00000000023',
719 return $agentList[$this->service->getField(
'PS_MODE')];
727 return PaySystem\Manager::getHandlerDescription(
'Skb')[
'HANDLER_MODE_LIST'];
736 return Main\Web\Json::encode(
$data, JSON_UNESCAPED_UNICODE);
743 private static function decode(
$data)
747 return Main\Web\Json::decode(
$data);
749 catch (Main\ArgumentException $exception)
if(!Loader::includeModule('catalog')) if(!AccessController::getCurrent() ->check(ActionDictionary::ACTION_PRICE_EDIT)) if(!check_bitrix_sessid()) $request
static getHandlerModeList()
getUrl(Payment $payment=null, $action)
showTemplate(Payment $payment=null, $template='')
setExtraParams(array $values)
getBusinessValue(Payment $payment=null, $code)
static getIndicativeFields()
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
if(Loader::includeModule( 'bitrix24')) elseif(Loader::includeModule('intranet') &&CIntranetUtils::getPortalZone() !=='ru') $description
while($arParentIBlockProperty=$dbParentIBlockProperty->Fetch()) $errorMessage
if(!is_null($config))($config as $configItem)(! $configItem->isVisible()) $code
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
if($inWords) echo htmlspecialcharsbx(Number2Word_Rus(roundEx($totalVatSum $params['CURRENCY']