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/ilovecveti.ru/bitrix/modules/sale/handlers/paysystem/skb/ |
Upload File : |
<?php namespace Sale\Handlers\PaySystem; use Bitrix\Main, Bitrix\Main\Request, Bitrix\Main\Localization\Loc, Bitrix\Main\Web\HttpClient, Bitrix\Sale\Payment, Bitrix\Sale\PaySystem, Bitrix\Sale\PriceMaths, Bitrix\Sale\PaymentCollection, Bitrix\Sale\BusinessValue; Loc::loadMessages(__FILE__); /** * Class SkbHandler * @package Sale\Handlers\PaySystem */ class SkbHandler extends PaySystem\ServiceHandler implements PaySystem\IRefund { private const MODE_SKB = 'skb'; private const MODE_DELOBANK = 'delobank'; private const MODE_GAZENERGOBANK = 'gazenergobank'; private const TAG_BITRIX_24 = 'Bitrix24'; private const RESPONSE_CODE_SUCCESS = [ '0', 'RQ00000' ]; private const HTTP_CODE_OK = 200; private const HTTP_CODE_LOCKED = 423; private const PAYMENT_STATUS_NOT_STARTED = 'NTST'; private const PAYMENT_STATUS_ACCEPTED = 'ACWP'; private const PAYMENT_STATUS_REJECTED = 'RJCT'; /** * @param Payment $payment * @param Request|null $request * @return PaySystem\ServiceResult */ public function initiatePay(Payment $payment, Request $request = null): PaySystem\ServiceResult { $result = new PaySystem\ServiceResult(); $createPaymentResult = $this->createPayment($payment); if (!$createPaymentResult->isSuccess()) { $result->addErrors($createPaymentResult->getErrors()); return $result; } $result->setPsData($createPaymentResult->getPsData()); $paymentData = $createPaymentResult->getData(); $params['CURRENCY'] = $payment->getField('CURRENCY'); $params['SUM'] = PriceMaths::roundPrecision($payment->getSum()); $params['URL'] = $paymentData['payload']; $params['QR_CODE_IMAGE'] = $paymentData['qrImage']; $this->setExtraParams($params); $showTemplateResult = $this->showTemplate($payment, 'template'); if ($showTemplateResult->isSuccess()) { $result->setTemplate($showTemplateResult->getTemplate()); } else { $result->addErrors($showTemplateResult->getErrors()); } return $result; } /** * @param Payment $payment * @return PaySystem\ServiceResult */ private function changeUserPassword(Payment $payment): PaySystem\ServiceResult { $result = new PaySystem\ServiceResult(); $params = [ 'login' => $this->getBusinessValue($payment, 'SKB_LOGIN'), 'password' => $this->getBusinessValue($payment, 'SKB_PASSWORD'), 'newPassword' => Main\Security\Random::getString(10, true), ]; $sendResult = $this->send($payment, 'changeUserPassword', $params); if ($sendResult->isSuccess()) { $updatePasswordResult = $this->updatePassword($payment, $params['newPassword']); if (!$updatePasswordResult->isSuccess()) { $result->addErrors($updatePasswordResult->getErrors()); } } else { $result->addErrors($sendResult->getErrors()); } return $result; } /** * @param Payment $payment * @param string $password * @return PaySystem\ServiceResult */ private function updatePassword(Payment $payment, string $password): PaySystem\ServiceResult { $result = new PaySystem\ServiceResult(); $oldMapping = BusinessValue::getMapping('SKB_PASSWORD', $this->service->getConsumerName(), $payment->getPersonTypeId()); $updateMappingResult = BusinessValue::updateMapping( 'SKB_PASSWORD', $oldMapping, [ 'PROVIDER_KEY' => 'VALUE', 'PROVIDER_VALUE' => $password, ] ); if (!$updateMappingResult->isSuccess()) { $result->addErrors($updateMappingResult->getErrors()); } return $result; } /** * @param Payment $payment * @return PaySystem\ServiceResult */ private function createPayment(Payment $payment): PaySystem\ServiceResult { $result = new PaySystem\ServiceResult(); $params = [ 'messageId' => self::getMessageId(), 'agentId' => $this->getAgentId(), 'merchantId' => $this->getBusinessValue($payment, 'SKB_MERCHANT_ID'), 'paymentId' => (string)$payment->getId(), 'amount' => (string)($payment->getSum() * 100), 'currency' => $payment->getField('CURRENCY'), 'paymentPurpose' => $this->getAdditionalInfo($payment), 'templateVersion' => '01', 'qrcType' => '02', 'mediaType' => 'image/png', 'width' => 450, 'height' => 450, ]; $sendResult = $this->send($payment, 'register', $params); if (!$sendResult->isSuccess()) { $result->addErrors($sendResult->getErrors()); return $result; } $sendData = $sendResult->getData(); $result->setPsData(['PS_INVOICE_ID' => $sendData['qrcId']]); $result->setData($sendData); return $result; } /** * @return array */ public function getCurrencyList(): array { return ['RUB']; } /** * @param Payment $payment * @param Request $request * @return PaySystem\ServiceResult */ public function processRequest(Payment $payment, Request $request): PaySystem\ServiceResult { $result = new PaySystem\ServiceResult(); $secretKey = $this->getBusinessValue($payment, 'SKB_SECRET_KEY'); if ($secretKey && !$this->isSignCorrect($request, $secretKey)) { $result->addError(new Main\Error(Loc::getMessage('SALE_HPS_SKB_ERROR_SIGN'))); return $result; } $paymentStatusResult = $this->getSkbPaymentStatus($payment); if (!$paymentStatusResult->isSuccess()) { $result->addErrors($paymentStatusResult->getErrors()); return $result; } return $this->processSkbPaymentStatus($payment, $paymentStatusResult); } /** * @param Payment $payment * @return PaySystem\ServiceResult */ private function getSkbPaymentStatus(Payment $payment): PaySystem\ServiceResult { $result = new PaySystem\ServiceResult(); $params = [ 'messageId' => self::getMessageId(), 'agentId' => $this->getAgentId(), 'qrcIds' => [ $payment->getField('PS_INVOICE_ID'), ], ]; $sendResult = $this->send($payment, 'getPaymentsStatus', $params); if (!$sendResult->isSuccess()) { $result->addErrors($sendResult->getErrors()); return $result; } $sendData = $sendResult->getData(); if (empty($sendData['payments'])) { $result->addError(new Main\Error(Loc::getMessage('SALE_HPS_SKB_ERROR_EMPTY_PAYMENTS'))); } $result->setData($sendData); return $result; } private function processSkbPaymentStatus(Payment $payment, PaySystem\ServiceResult $paymentStatusResult): PaySystem\ServiceResult { $result = new PaySystem\ServiceResult(); $paymentStatusData = $paymentStatusResult->getData(); $skbPayment = current($paymentStatusData['payments']); if (!empty($skbPayment['status'])) { $status = $skbPayment['status']; $fields = [ 'PS_STATUS_CODE' => $status, 'PS_SUM' => $payment->getSum(), 'PS_CURRENCY' => $payment->getField('CURRENCY'), 'PS_RESPONSE_DATE' => new Main\Type\DateTime(), 'PS_STATUS' => 'N', ]; $psStatusDescription = Loc::getMessage('SALE_HPS_SKB_STATUS_DESCRIPTION_' . $status); if ($psStatusDescription) { $fields['PS_STATUS_DESCRIPTION'] = $psStatusDescription; } $additionalPsStatusDescription = ''; if (!empty($skbPayment['trxId'])) { $additionalPsStatusDescription = Loc::getMessage('SALE_HPS_SKB_OPERATION_ID_DESCRIPTION', [ '#TX_ID#' => $skbPayment['trxId'], ]); } elseif (!empty($skbPayment['qrcId'])) { $additionalPsStatusDescription = Loc::getMessage('SALE_HPS_SKB_QR_CODE_ID_DESCRIPTION', [ '#QR_CODE_ID#' => $skbPayment['qrcId'], ]); } if ($additionalPsStatusDescription) { $fields['PS_STATUS_DESCRIPTION'] = !empty($fields['PS_STATUS_DESCRIPTION']) ? $fields['PS_STATUS_DESCRIPTION'] . ' ' . $additionalPsStatusDescription : $additionalPsStatusDescription ; } if ($status === self::PAYMENT_STATUS_ACCEPTED) { $fields['PS_STATUS'] = 'Y'; PaySystem\Logger::addDebugInfo( __CLASS__ . ': PS_CHANGE_STATUS_PAY=' . $this->getBusinessValue($payment, 'PS_CHANGE_STATUS_PAY') ); if ($this->getBusinessValue($payment, 'PS_CHANGE_STATUS_PAY') === 'Y') { $result->setOperationType(PaySystem\ServiceResult::MONEY_COMING); } } elseif ($status === self::PAYMENT_STATUS_NOT_STARTED) { $result->addError( new Main\Error(Loc::getMessage('SALE_HPS_SKB_ERROR_STATUS_NOT_STARTED')) ); } elseif ($status === self::PAYMENT_STATUS_REJECTED) { $result->addError( new Main\Error(Loc::getMessage('SALE_HPS_SKB_ERROR_STATUS_REJECTED')) ); } $result->setPsData($fields); if (!$result->isSuccess()) { $result->setOperationType(PaySystem\ServiceResult::MONEY_LEAVING); } } else { $result->addError( new Main\Error(Loc::getMessage('SALE_HPS_SKB_ERROR_STATUS_NOT_FOUND')) ); } return $result; } /** * @param Request $request * @return mixed */ public function getPaymentIdFromRequest(Request $request) { return $request->get('paymentId'); } /** * @return array */ public static function getIndicativeFields(): array { return ['qrcId', 'paymentId', 'txStatus', 'txId', 'debitorId', 'amount', 'timestamp', 'sign']; } /** * @param Request $request * @param $secretKey * @return bool */ protected function isSignCorrect(Request $request, $secretKey): bool { $hash = md5( $request->get('qrcId') . $request->get('timestamp') . $request->get('txId') . $request->get('amount') . $secretKey ); return $hash === $request->get('sign'); } /** * @param Payment $payment * @param $refundableSum * @return PaySystem\ServiceResult */ public function refund(Payment $payment, $refundableSum): PaySystem\ServiceResult { $result = new PaySystem\ServiceResult(); $checkRefundTransferResult = $this->checkRefundTransfer($payment, $refundableSum); if (!$checkRefundTransferResult->isSuccess()) { $result->addErrors($checkRefundTransferResult->getErrors()); return $result; } $checkRefundTransferData = $checkRefundTransferResult->getData(); if (!$checkRefundTransferData['corelationId']) { $result->addError(new Main\Error(Loc::getMessage('SALE_HPS_SKB_ERROR_CORELATION_ID'))); return $result; } $approveRefundTransferResult = $this->approveRefundTransfer($payment, $checkRefundTransferData['corelationId']); if (!$approveRefundTransferResult->isSuccess()) { $result->addErrors($approveRefundTransferResult->getErrors()); return $result; } $approveRefundTransferData = $approveRefundTransferResult->getData(); if ($approveRefundTransferData['status'] === static::PAYMENT_STATUS_ACCEPTED) { $result->setOperationType(PaySystem\ServiceResult::MONEY_LEAVING); } return $result; } /** * @param Payment $payment * @param $refundableSum * @return PaySystem\ServiceResult */ private function checkRefundTransfer(Payment $payment, $refundableSum): PaySystem\ServiceResult { $result = new PaySystem\ServiceResult(); $paymentStatusResult = $this->getSkbPaymentStatus($payment); if (!$paymentStatusResult->isSuccess()) { $result->addErrors($paymentStatusResult->getErrors()); return $result; } $paymentStatusData = $paymentStatusResult->getData(); $skbPayment = current($paymentStatusData['payments']); $params = [ 'messageId' => self::getMessageId(), 'trxId' => $skbPayment['trxId'], 'amount' => (string)($refundableSum * 100), ]; $sendResult = $this->send($payment, 'checkRefundTransfer', $params); if ($sendResult->isSuccess()) { $result->setData($sendResult->getData()); } else { $result->addErrors($sendResult->getErrors()); } return $result; } /** * @param Payment $payment * @param string $corelationId * @return PaySystem\ServiceResult */ private function approveRefundTransfer(Payment $payment, string $corelationId): PaySystem\ServiceResult { $result = new PaySystem\ServiceResult(); $params = [ 'messageId' => self::getMessageId(), 'corelationId' => $corelationId, ]; $sendResult = $this->send($payment, 'approveRefundTransfer', $params); if ($sendResult->isSuccess()) { $result->setData($sendResult->getData()); } else { $result->addErrors($sendResult->getErrors()); } return $result; } /** * @param Payment $payment * @param $action * @param array $params * @return PaySystem\ServiceResult */ private function send(Payment $payment, $action, array $params = []): PaySystem\ServiceResult { $url = $this->getUrl($payment, $action); $result = $this->makeQuery($url, $params, $this->getHeaders($payment)); if (!$result->isSuccess() && $result->getErrorCollection()->getErrorByCode(self::HTTP_CODE_LOCKED)) { $changeUserPasswordResult = $this->changeUserPassword($payment); if (!$changeUserPasswordResult->isSuccess()) { $result->addErrors($changeUserPasswordResult->getErrors()); return $result; } $result = $this->makeQuery($url, $params, $this->getHeaders($payment)); } if (!$result->isSuccess()) { return $result; } $sendData = $result->getData(); $verifyResult = $this->verifyResponse($sendData['response']); if ($verifyResult->isSuccess()) { $result->setData(static::decode($sendData['response'])); } else { $result->addErrors($verifyResult->getErrors()); } return $result; } /** * @param $url * @param array $params * @param array $headers * @return PaySystem\ServiceResult */ private function makeQuery($url, array $params = [], array $headers = []): PaySystem\ServiceResult { $result = new PaySystem\ServiceResult(); $httpClient = new HttpClient(); $httpClient->setHeaders($headers); $postData = static::encode($params); PaySystem\Logger::addDebugInfo(__CLASS__ . ': request data: ' . $postData); $response = $httpClient->post($url, $postData); if ($response === false) { $result->addError(new Main\Error(Loc::getMessage('SALE_HPS_SKB_ERROR_EMPTY_RESPONSE'))); foreach ($httpClient->getError() as $code => $message) { $result->addError(new Main\Error($message, $code)); } return $result; } PaySystem\Logger::addDebugInfo(__CLASS__ . ': response data: ' . $response); $httpStatus = $httpClient->getStatus(); if ($httpStatus === self::HTTP_CODE_OK) { $result->setData(['response' => $response]); } else { $errorMessage = Loc::getMessage('SALE_HPS_SKB_ERROR_STATUS_'.$httpStatus); if (!$errorMessage) { $errorMessage = Loc::getMessage( 'SALE_HPS_SKB_ERROR_STATUS_UNKNOWN', [ '#STATUS#' => $httpStatus, ] ); } $result->addError(new Main\Error($errorMessage, $httpStatus)); } return $result; } /** * @param $response * @return PaySystem\ServiceResult */ private function verifyResponse($response): PaySystem\ServiceResult { $result = new PaySystem\ServiceResult(); $responseData = static::decode($response); if ($response && !$responseData) { $result->addError(new Main\Error(Loc::getMessage('SALE_HPS_SKB_ERROR_DECODE_RESPONSE'))); return $result; } if (isset($responseData['errCode']) && !\in_array($responseData['errCode'], self::RESPONSE_CODE_SUCCESS, true)) { $result->addError(new Main\Error($responseData['errMess'], $responseData['errCode'])); } elseif (isset($responseData['moreInformation'], $responseData['httpCode'])) { $result->addError(new Main\Error($responseData['moreInformation'], $responseData['httpCode'])); } return $result; } /** * @param Payment|null $payment * @return bool */ protected function isTestMode(Payment $payment = null): bool { return $this->getBusinessValue($payment, 'SKB_TEST_MODE') === 'Y'; } /** * @return array */ protected function getUrlList(): array { $testUrl = 'https://test.api.sinara.ru:443/'; $activeUrl = 'https://public.api.sinara.ru:443/'; return [ 'register' => [ self::TEST_URL => $testUrl . 'qr/register', self::ACTIVE_URL => $activeUrl . 'qr/register', ], 'getPaymentsStatus' => [ self::TEST_URL => $testUrl . 'qr/getpaymentsstatus', self::ACTIVE_URL => $activeUrl . 'qr/getpaymentsstatus', ], 'checkRefundTransfer' => [ self::TEST_URL => $testUrl . 'refund/CheckRefundTransfer', self::ACTIVE_URL => $activeUrl . 'refund/CheckRefundTransfer', ], 'approveRefundTransfer' => [ self::TEST_URL => $testUrl . 'refund/ApproveRefundTransfer', self::ACTIVE_URL => $activeUrl . 'refund/ApproveRefundTransfer', ], 'changeUserPassword' => [ self::TEST_URL => $testUrl . 'user/changeUserPassword', self::ACTIVE_URL => $activeUrl . 'user/changeUserPassword', ], ]; } /** * @param Payment $payment * @return mixed */ protected function getAdditionalInfo(Payment $payment) { /** @var PaymentCollection $collection */ $collection = $payment->getCollection(); $order = $collection->getOrder(); $userEmail = $order->getPropertyCollection()->getUserEmail(); $description = str_replace( [ '#PAYMENT_NUMBER#', '#ORDER_NUMBER#', '#PAYMENT_ID#', '#ORDER_ID#', '#USER_EMAIL#' ], [ $payment->getField('ACCOUNT_NUMBER'), $order->getField('ACCOUNT_NUMBER'), $payment->getId(), $order->getId(), ($userEmail) ? $userEmail->getValue() : '' ], $this->getBusinessValue($payment, 'SKB_ADDITIONAL_INFO') ); return mb_substr($description, 0, 140); } /** * @param Payment $payment * @return string */ private function getBasicAuthString(Payment $payment): string { return base64_encode( $this->getBusinessValue($payment, 'SKB_LOGIN') . ':' . $this->getBusinessValue($payment, 'SKB_PASSWORD') ); } /** * @param Payment $payment * @return array */ private function getHeaders(Payment $payment): array { return [ 'Authorization' => 'Basic ' . $this->getBasicAuthString($payment), 'Content-Type' => 'application/json', 'User-Agent' => self::TAG_BITRIX_24, ]; } /** * @return string */ private static function getMessageId(): string { return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x', mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0x0fff) | 0x4000, mt_rand(0, 0x3fff) | 0x8000, mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff) ); } /** * @return string */ private function getAgentId(): string { $agentList = [ self::MODE_SKB => 'A00000000001', self::MODE_DELOBANK => 'A00000000001', self::MODE_GAZENERGOBANK => 'A00000000023', ]; return $agentList[$this->service->getField('PS_MODE')]; } /** * @return array */ public static function getHandlerModeList(): array { return PaySystem\Manager::getHandlerDescription('Skb')['HANDLER_MODE_LIST']; } /** * @param array $data * @return mixed */ private static function encode(array $data) { return Main\Web\Json::encode($data, JSON_UNESCAPED_UNICODE); } /** * @param string $data * @return mixed */ private static function decode($data) { try { return Main\Web\Json::decode($data); } catch (Main\ArgumentException $exception) { return false; } } }