3namespace Sale\Handlers\PaySystem;
6use Bitrix\Main\Web\HttpClient;
7use Bitrix\Main\Localization\Loc;
9use Bitrix\Sale\PaySystem;
10use Bitrix\Main\Request;
11use Bitrix\Sale\Payment;
12use Bitrix\Sale\PaySystem\ServiceResult;
13use Bitrix\Sale\PaymentCollection;
14use Bitrix\Sale\PriceMaths;
15use Bitrix\Main\Context;
17Loc::loadMessages(__FILE__);
27 private const API_URL =
'https://api.bepaid.by';
28 private const TRACKING_ID_DELIMITER =
'#';
29 private const STATUS_SUCCESSFUL_CODE =
'successful';
34 public function initiatePay(Payment
$payment, Request
$request =
null): ServiceResult
38 $createInvoiceResult = $this->createInvoice(
$payment);
39 if (!$createInvoiceResult->isSuccess())
41 $result->addErrors($createInvoiceResult->getErrors());
45 $invoiceData = $createInvoiceResult->getData();
46 if (!empty($invoiceData[
'transaction'][
'uid']))
48 $result->setPsData([
'PS_INVOICE_ID' => $invoiceData[
'transaction'][
'uid']]);
52 'sum' => PriceMaths::roundPrecision(
$payment->getSum()),
53 'currency' =>
$payment->getField(
'CURRENCY'),
54 'instruction' => $invoiceData[
'transaction'][
'erip'][
'instruction'],
55 'qr_code' => $invoiceData[
'transaction'][
'erip'][
'qr_code'],
56 'account_number' => $invoiceData[
'transaction'][
'erip'][
'account_number'],
57 'service_no_erip' => $invoiceData[
'transaction'][
'erip'][
'service_no_erip'],
61 if ($showTemplateResult->isSuccess())
63 $result->setTemplate($showTemplateResult->getTemplate());
67 $result->addErrors($showTemplateResult->getErrors());
76 public function processRequest(Payment
$payment, Request
$request): ServiceResult
80 $inputStream = self::readFromStream();
81 $data = self::decode($inputStream);
85 PaySystem\Error::create(
86 Loc::getMessage(
'SALE_HPS_BEPAID_ERIP_RESPONSE_ERROR')
90 $transaction =
$data[
'transaction'];
92 if (!$this->isSignatureCorrect(
$payment, $inputStream))
95 PaySystem\Error::create(
96 Loc::getMessage(
'SALE_HPS_BEPAID_ERIP_ERROR_SIGNATURE')
101 $getInvoiceResult = $this->getInvoice(
$payment);
102 if (!$getInvoiceResult->isSuccess())
104 return $result->addErrors($getInvoiceResult->getErrors());
107 $invoiceData = $getInvoiceResult->getData();
108 if ($invoiceData[
'transaction'][
'status'] === self::STATUS_SUCCESSFUL_CODE)
111 'PS_STATUS_CODE' => $transaction[
'status'],
112 'PS_STATUS_DESCRIPTION' => Loc::getMessage(
'SALE_HPS_BEPAID_ERIP_TRANSACTION', [
113 '#ID#' => $transaction[
'uid'],
115 'PS_SUM' => $transaction[
'amount'] / 100,
117 'PS_CURRENCY' => $transaction[
'currency'],
118 'PS_RESPONSE_DATE' =>
new Main\Type\DateTime()
120 if ($this->isSumCorrect(
$payment, $transaction[
'amount'] / 100))
124 PaySystem\Logger::addDebugInfo(
126 '%s: PS_CHANGE_STATUS_PAY=%s',
133 $result->setOperationType(PaySystem\ServiceResult::MONEY_COMING);
138 $error = Loc::getMessage(
'SALE_HPS_BEPAID_ERIP_ERROR_SUM');
152 public function getPaymentIdFromRequest(Request
$request)
154 $inputStream = self::readFromStream();
160 $data = self::decode($inputStream);
166 if (isset(
$data[
'transaction'][
'tracking_id']))
168 [$trackingPaymentId] = explode(self::TRACKING_ID_DELIMITER,
$data[
'transaction'][
'tracking_id']);
169 return (
int)$trackingPaymentId;
186 public static function isMyResponse(Request
$request, $paySystemId): bool
188 $inputStream = self::readFromStream();
191 $data = self::decode($inputStream);
197 if (isset(
$data[
'transaction'][
'tracking_id']))
199 [, $trackingPaySystemId] = explode(
200 self::TRACKING_ID_DELIMITER,
201 $data[
'transaction'][
'tracking_id']
203 return (
int)$trackingPaySystemId === (int)$paySystemId;
228 'createInvoice' => self::API_URL .
'/beyag/payments',
229 'getInvoice' => self::API_URL .
'/beyag/payments/#invoice-id#',
236 protected function isTestMode(Payment
$payment =
null): bool
245 private function createInvoice(Payment
$payment): ServiceResult
251 'test' => $this->isTestMode(
$payment),
252 'amount' => (string)(PriceMaths::roundPrecision(
$payment->getSum()) * 100),
253 'currency' =>
$payment->getField(
'CURRENCY'),
254 'description' => $this->getInvoiceDescription(
$payment),
255 'tracking_id' =>
$payment->getId() . self::TRACKING_ID_DELIMITER . $this->service->getField(
'ID'),
257 'language' => LANGUAGE_ID,
258 'email' => $this->getUserEmail(
$payment),
259 'ip' => Context::getCurrent()->getServer()->getRemoteAddr(),
260 'payment_method' => [
262 'account_number' =>
$payment->getId(),
264 'additional_data' => self::getAdditionalData(),
269 if (isset($serviceCode) && !empty($serviceCode))
271 $params[
'request'][
'payment_method'][
'service_no'] = $serviceCode;
274 $sendResult = $this->send(
275 HttpClient::HTTP_POST,
276 $this->getUrl(
$payment,
'createInvoice'),
280 if ($sendResult->isSuccess())
282 $invoiceData = $sendResult->getData();
283 $verifyResponseResult = $this->verifyResponse($invoiceData);
284 if ($verifyResponseResult->isSuccess())
286 $result->setData($invoiceData);
290 $result->addErrors($verifyResponseResult->getErrors());
295 $result->addErrors($sendResult->getErrors());
305 private function getInvoice(Payment
$payment): ServiceResult
309 $sendResult = $this->send(
310 HttpClient::HTTP_GET,
311 $this->getUrl(
$payment,
'getInvoice'),
315 if ($sendResult->isSuccess())
317 $paymentData = $sendResult->getData();
318 $verifyResponseResult = $this->verifyResponse($paymentData);
319 if ($verifyResponseResult->isSuccess())
321 $result->setData($paymentData);
325 $result->addErrors($verifyResponseResult->getErrors());
330 $result->addErrors($sendResult->getErrors());
341 private function isSignatureCorrect(Payment
$payment, $inputStream): bool
343 PaySystem\Logger::addDebugInfo(
345 '%s: Signature: %s; Webhook: %s',
352 $signature = base64_decode(
$_SERVER[
'HTTP_CONTENT_SIGNATURE']);
358 $publicKey = sprintf(
359 "-----BEGIN PUBLIC KEY-----\n%s-----END PUBLIC KEY-----",
372 $key = openssl_pkey_get_public($publicKey);
374 return openssl_verify($inputStream, $signature,
$key, OPENSSL_ALGO_SHA256) === 1;
384 'Content-Type' =>
'application/json',
385 'Accept' =>
'application/json',
386 'Authorization' =>
'Basic ' . $this->getBasicAuthString(
$payment),
387 'RequestID' => $this->getIdempotenceKey(),
402 $httpClient =
new HttpClient();
403 foreach ($headers as
$name => $value)
405 $httpClient->setHeader(
$name, $value);
408 PaySystem\Logger::addDebugInfo(__CLASS__.
': request url: '.
$url);
410 if (
$method === HttpClient::HTTP_GET)
422 PaySystem\Logger::addDebugInfo(__CLASS__.
': request data: '.
$postData);
433 $errors = $httpClient->getError();
442 PaySystem\Logger::addDebugInfo(__CLASS__.
': response data: '.
$response);
447 return $result->addError(PaySystem\Error::create(
448 Loc::getMessage(
'SALE_HPS_BEPAID_ERIP_RESPONSE_ERROR')
477 private function getInvoiceDescription(Payment
$payment)
480 $collection =
$payment->getCollection();
481 $order = $collection->getOrder();
492 $payment->getField(
'ACCOUNT_NUMBER'),
493 $order->getField(
'ACCOUNT_NUMBER'),
498 $this->getBusinessValue(
$payment,
'BEPAID_ERIP_PAYMENT_DESCRIPTION')
508 private function getUserEmail(Payment
$payment): string
511 $collection =
$payment->getCollection();
512 $order = $collection->getOrder();
513 $userEmail =
$order->getPropertyCollection()->getUserEmail();
515 return $userEmail ? (string)$userEmail->getValue() :
'';
523 private function isSumCorrect(Payment
$payment,
$sum): bool
525 PaySystem\Logger::addDebugInfo(
526 sprintf(
'%s: bePaidSum=%s; paymentSum=%s',
528 PriceMaths::roundPrecision(
$sum),
529 PriceMaths::roundPrecision(
$payment->getSum())
533 return PriceMaths::roundPrecision(
$sum) === PriceMaths::roundPrecision(
$payment->getSum());
540 private function getBasicAuthString(Payment
$payment): string
542 return base64_encode(
554 private function getIdempotenceKey(): string
556 return sprintf(
'%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
557 mt_rand(0, 0xffff), mt_rand(0, 0xffff),
559 mt_rand(0, 0x0fff) | 0x4000,
560 mt_rand(0, 0x3fff) | 0x8000,
561 mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
568 private static function readFromStream()
570 return file_get_contents(
'php://input');
579 return Main\Web\Json::encode(
$data, JSON_UNESCAPED_UNICODE);
586 private static function decode(
$data)
590 return Main\Web\Json::decode(
$data);
592 catch (Main\ArgumentException $exception)
601 private static function getAdditionalData():
array
604 'platform_data' => self::getPlatformData(),
607 $integrationData = self::getIntegrationData();
608 if ($integrationData)
610 $result[
'integration_data'] = $integrationData;
619 private static function getPlatformData(): string
621 if (Main\ModuleManager::isModuleInstalled(
'bitrix24'))
623 $platformType =
'Bitrix24';
625 elseif (Main\ModuleManager::isModuleInstalled(
'intranet'))
627 $platformType =
'Self-hosted';
631 $platformType =
'Bitrix Site Manager';
634 return $platformType;
640 private static function getIntegrationData(): ?string
642 $version = self::getSaleModuleVersion();
648 return 'bePaid system module v' . $version;
654 private static function getSaleModuleVersion(): ?string
656 $modulePath =
getLocalPath(
'modules/sale/install/version.php');
657 if ($modulePath ===
false)
662 $arModuleVersion = [];
663 include
$_SERVER[
'DOCUMENT_ROOT'] . $modulePath;
664 return isset($arModuleVersion[
'VERSION']) ? (string)$arModuleVersion[
'VERSION'] : null;
if(!Loader::includeModule('catalog')) if(!AccessController::getCurrent() ->check(ActionDictionary::ACTION_PRICE_EDIT)) if(!check_bitrix_sessid()) $request
showTemplate(Payment $payment=null, $template='')
setExtraParams(array $values)
getBusinessValue(Payment $payment=null, $code)
</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
$_SERVER["DOCUMENT_ROOT"]
if(!is_null($config))($config as $configItem)(! $configItem->isVisible()) $code
getLocalPath($path, $baseFolder="/bitrix")
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
if(empty($signedUserToken)) $key
if($inWords) echo htmlspecialcharsbx(Number2Word_Rus(roundEx($totalVatSum $params['CURRENCY']