1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
handler.php
См. документацию.
1<?php
2
3namespace Sale\Handlers\PaySystem;
4
5use Bitrix\Main;
6use Bitrix\Main\Web\HttpClient;
7use Bitrix\Main\Localization\Loc;
8use Bitrix\Sale;
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;
16
17Loc::loadMessages(__FILE__);
18
25class BePaidEripHandler extends PaySystem\ServiceHandler
26{
27 private const API_URL = 'https://api.bepaid.by';
28 private const TRACKING_ID_DELIMITER = '#';
29 private const STATUS_SUCCESSFUL_CODE = 'successful';
30
34 public function initiatePay(Payment $payment, Request $request = null): ServiceResult
35 {
36 $result = new ServiceResult();
37
38 $createInvoiceResult = $this->createInvoice($payment);
39 if (!$createInvoiceResult->isSuccess())
40 {
41 $result->addErrors($createInvoiceResult->getErrors());
42 return $result;
43 }
44
45 $invoiceData = $createInvoiceResult->getData();
46 if (!empty($invoiceData['transaction']['uid']))
47 {
48 $result->setPsData(['PS_INVOICE_ID' => $invoiceData['transaction']['uid']]);
49 }
50
51 $this->setExtraParams([
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'],
58 ]);
59
60 $showTemplateResult = $this->showTemplate($payment, 'template');
61 if ($showTemplateResult->isSuccess())
62 {
63 $result->setTemplate($showTemplateResult->getTemplate());
64 }
65 else
66 {
67 $result->addErrors($showTemplateResult->getErrors());
68 }
69
70 return $result;
71 }
72
76 public function processRequest(Payment $payment, Request $request): ServiceResult
77 {
78 $result = new ServiceResult();
79
80 $inputStream = self::readFromStream();
81 $data = self::decode($inputStream);
82 if ($data === false)
83 {
84 return $result->addError(
85 PaySystem\Error::create(
86 Loc::getMessage('SALE_HPS_BEPAID_ERIP_RESPONSE_ERROR')
87 )
88 );
89 }
90 $transaction = $data['transaction'];
91
92 if (!$this->isSignatureCorrect($payment, $inputStream))
93 {
94 return $result->addError(
95 PaySystem\Error::create(
96 Loc::getMessage('SALE_HPS_BEPAID_ERIP_ERROR_SIGNATURE')
97 )
98 );
99 }
100
101 $getInvoiceResult = $this->getInvoice($payment);
102 if (!$getInvoiceResult->isSuccess())
103 {
104 return $result->addErrors($getInvoiceResult->getErrors());
105 }
106
107 $invoiceData = $getInvoiceResult->getData();
108 if ($invoiceData['transaction']['status'] === self::STATUS_SUCCESSFUL_CODE)
109 {
110 $fields = [
111 'PS_STATUS_CODE' => $transaction['status'],
112 'PS_STATUS_DESCRIPTION' => Loc::getMessage('SALE_HPS_BEPAID_ERIP_TRANSACTION', [
113 '#ID#' => $transaction['uid'],
114 ]),
115 'PS_SUM' => $transaction['amount'] / 100,
116 'PS_STATUS' => 'N',
117 'PS_CURRENCY' => $transaction['currency'],
118 'PS_RESPONSE_DATE' => new Main\Type\DateTime()
119 ];
120 if ($this->isSumCorrect($payment, $transaction['amount'] / 100))
121 {
122 $fields['PS_STATUS'] = 'Y';
123
124 PaySystem\Logger::addDebugInfo(
125 sprintf(
126 '%s: PS_CHANGE_STATUS_PAY=%s',
127 __CLASS__,
128 $this->getBusinessValue($payment, 'PS_CHANGE_STATUS_PAY')
129 )
130 );
131 if ($this->getBusinessValue($payment, 'PS_CHANGE_STATUS_PAY') === 'Y')
132 {
133 $result->setOperationType(PaySystem\ServiceResult::MONEY_COMING);
134 }
135 }
136 else
137 {
138 $error = Loc::getMessage('SALE_HPS_BEPAID_ERIP_ERROR_SUM');
139 $fields['PS_STATUS_DESCRIPTION'] .= '. ' . $error;
140 $result->addError(PaySystem\Error::create($error));
141 }
142
143 $result->setPsData($fields);
144 }
145
146 return $result;
147 }
148
152 public function getPaymentIdFromRequest(Request $request)
153 {
154 $inputStream = self::readFromStream();
155 if (!$inputStream)
156 {
157 return false;
158 }
159
160 $data = self::decode($inputStream);
161 if ($data === false)
162 {
163 return false;
164 }
165
166 if (isset($data['transaction']['tracking_id']))
167 {
168 [$trackingPaymentId] = explode(self::TRACKING_ID_DELIMITER, $data['transaction']['tracking_id']);
169 return (int)$trackingPaymentId;
170 }
171
172 return false;
173 }
174
178 public function getCurrencyList(): array
179 {
180 return ['BYN'];
181 }
182
186 public static function isMyResponse(Request $request, $paySystemId): bool
187 {
188 $inputStream = self::readFromStream();
189 if ($inputStream)
190 {
191 $data = self::decode($inputStream);
192 if ($data === false)
193 {
194 return false;
195 }
196
197 if (isset($data['transaction']['tracking_id']))
198 {
199 [, $trackingPaySystemId] = explode(
200 self::TRACKING_ID_DELIMITER,
201 $data['transaction']['tracking_id']
202 );
203 return (int)$trackingPaySystemId === (int)$paySystemId;
204 }
205 }
206
207 return false;
208 }
209
213 protected function getUrl(Payment $payment = null, $action): string
214 {
215 return str_replace(
216 '#invoice-id#',
217 $payment ? $payment->getField('PS_INVOICE_ID') : '',
218 parent::getUrl($payment, $action)
219 );
220 }
221
225 protected function getUrlList(): array
226 {
227 return [
228 'createInvoice' => self::API_URL . '/beyag/payments',
229 'getInvoice' => self::API_URL . '/beyag/payments/#invoice-id#',
230 ];
231 }
232
236 protected function isTestMode(Payment $payment = null): bool
237 {
238 return ($this->getBusinessValue($payment, 'PS_IS_TEST') === 'Y');
239 }
240
245 private function createInvoice(Payment $payment): ServiceResult
246 {
247 $result = new ServiceResult();
248
249 $params = [
250 'request' => [
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'),
256 'notification_url' => $this->getBusinessValue($payment, 'BEPAID_ERIP_NOTIFICATION_URL'),
257 'language' => LANGUAGE_ID,
258 'email' => $this->getUserEmail($payment),
259 'ip' => Context::getCurrent()->getServer()->getRemoteAddr(),
260 'payment_method' => [
261 'type' => 'erip',
262 'account_number' => $payment->getId(),
263 ],
264 'additional_data' => self::getAdditionalData(),
265 ]
266 ];
267
268 $serviceCode = $this->getBusinessValue($payment, 'BEPAID_ERIP_SERVICE_CODE');
269 if (isset($serviceCode) && !empty($serviceCode))
270 {
271 $params['request']['payment_method']['service_no'] = $serviceCode;
272 }
273
274 $sendResult = $this->send(
275 HttpClient::HTTP_POST,
276 $this->getUrl($payment, 'createInvoice'),
277 $params,
278 $this->getHeaders($payment)
279 );
280 if ($sendResult->isSuccess())
281 {
282 $invoiceData = $sendResult->getData();
283 $verifyResponseResult = $this->verifyResponse($invoiceData);
284 if ($verifyResponseResult->isSuccess())
285 {
286 $result->setData($invoiceData);
287 }
288 else
289 {
290 $result->addErrors($verifyResponseResult->getErrors());
291 }
292 }
293 else
294 {
295 $result->addErrors($sendResult->getErrors());
296 }
297
298 return $result;
299 }
300
305 private function getInvoice(Payment $payment): ServiceResult
306 {
307 $result = new ServiceResult();
308
309 $sendResult = $this->send(
310 HttpClient::HTTP_GET,
311 $this->getUrl($payment, 'getInvoice'),
312 [],
313 $this->getHeaders($payment)
314 );
315 if ($sendResult->isSuccess())
316 {
317 $paymentData = $sendResult->getData();
318 $verifyResponseResult = $this->verifyResponse($paymentData);
319 if ($verifyResponseResult->isSuccess())
320 {
321 $result->setData($paymentData);
322 }
323 else
324 {
325 $result->addErrors($verifyResponseResult->getErrors());
326 }
327 }
328 else
329 {
330 $result->addErrors($sendResult->getErrors());
331 }
332
333 return $result;
334 }
335
341 private function isSignatureCorrect(Payment $payment, $inputStream): bool
342 {
343 PaySystem\Logger::addDebugInfo(
344 sprintf(
345 '%s: Signature: %s; Webhook: %s',
346 __CLASS__,
347 $_SERVER['HTTP_CONTENT_SIGNATURE'],
348 $inputStream
349 )
350 );
351
352 $signature = base64_decode($_SERVER['HTTP_CONTENT_SIGNATURE']);
353 if (!$signature)
354 {
355 return false;
356 }
357
358 $publicKey = sprintf(
359 "-----BEGIN PUBLIC KEY-----\n%s-----END PUBLIC KEY-----",
360 chunk_split(
361 str_replace(
362 [
363 "\r\n",
364 "\n",
365 ],
366 '',
367 $this->getBusinessValue($payment, 'BEPAID_ERIP_PUBLIC_KEY')
368 ),
369 64
370 )
371 );
372 $key = openssl_pkey_get_public($publicKey);
373
374 return openssl_verify($inputStream, $signature, $key, OPENSSL_ALGO_SHA256) === 1;
375 }
376
381 private function getHeaders(Payment $payment): array
382 {
383 return [
384 'Content-Type' => 'application/json',
385 'Accept' => 'application/json',
386 'Authorization' => 'Basic ' . $this->getBasicAuthString($payment),
387 'RequestID' => $this->getIdempotenceKey(),
388 ];
389 }
390
398 private function send(string $method, string $url, array $params = [], array $headers = []): ServiceResult
399 {
400 $result = new ServiceResult();
401
402 $httpClient = new HttpClient();
403 foreach ($headers as $name => $value)
404 {
405 $httpClient->setHeader($name, $value);
406 }
407
408 PaySystem\Logger::addDebugInfo(__CLASS__.': request url: '.$url);
409
410 if ($method === HttpClient::HTTP_GET)
411 {
412 $response = $httpClient->get($url);
413 }
414 else
415 {
416 $postData = null;
417 if ($params)
418 {
419 $postData = self::encode($params);
420 }
421
422 PaySystem\Logger::addDebugInfo(__CLASS__.': request data: '.$postData);
423
424 $response = $httpClient->query($method, $url, $postData);
425 if ($response)
426 {
427 $response = $httpClient->getResult();
428 }
429 }
430
431 if ($response === false)
432 {
433 $errors = $httpClient->getError();
434 foreach ($errors as $code => $message)
435 {
436 $result->addError(PaySystem\Error::create($message, $code));
437 }
438
439 return $result;
440 }
441
442 PaySystem\Logger::addDebugInfo(__CLASS__.': response data: '.$response);
443
444 $response = self::decode($response);
445 if ($response === false)
446 {
447 return $result->addError(PaySystem\Error::create(
448 Loc::getMessage('SALE_HPS_BEPAID_ERIP_RESPONSE_ERROR')
449 ));
450 }
451
452 $result->setData($response);
453
454 return $result;
455 }
456
461 private function verifyResponse(array $response): ServiceResult
462 {
463 $result = new ServiceResult();
464
465 if (!empty($response['errors']))
466 {
467 $result->addError(PaySystem\Error::create($response['message']));
468 }
469
470 return $result;
471 }
472
477 private function getInvoiceDescription(Payment $payment)
478 {
480 $collection = $payment->getCollection();
481 $order = $collection->getOrder();
482
483 $description = str_replace(
484 [
485 '#PAYMENT_NUMBER#',
486 '#ORDER_NUMBER#',
487 '#PAYMENT_ID#',
488 '#ORDER_ID#',
489 '#USER_EMAIL#'
490 ],
491 [
492 $payment->getField('ACCOUNT_NUMBER'),
493 $order->getField('ACCOUNT_NUMBER'),
494 $payment->getId(),
495 $order->getId(),
496 $this->getUserEmail($payment)
497 ],
498 $this->getBusinessValue($payment, 'BEPAID_ERIP_PAYMENT_DESCRIPTION')
499 );
500
501 return $description;
502 }
503
508 private function getUserEmail(Payment $payment): string
509 {
511 $collection = $payment->getCollection();
512 $order = $collection->getOrder();
513 $userEmail = $order->getPropertyCollection()->getUserEmail();
514
515 return $userEmail ? (string)$userEmail->getValue() : '';
516 }
517
523 private function isSumCorrect(Payment $payment, $sum): bool
524 {
525 PaySystem\Logger::addDebugInfo(
526 sprintf( '%s: bePaidSum=%s; paymentSum=%s',
527 __CLASS__,
528 PriceMaths::roundPrecision($sum),
529 PriceMaths::roundPrecision($payment->getSum())
530 )
531 );
532
533 return PriceMaths::roundPrecision($sum) === PriceMaths::roundPrecision($payment->getSum());
534 }
535
540 private function getBasicAuthString(Payment $payment): string
541 {
542 return base64_encode(
543 sprintf(
544 '%s:%s',
545 $this->getBusinessValue($payment, 'BEPAID_ERIP_ID'),
546 $this->getBusinessValue($payment, 'BEPAID_ERIP_SECRET_KEY')
547 )
548 );
549 }
550
554 private function getIdempotenceKey(): string
555 {
556 return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
557 mt_rand(0, 0xffff), mt_rand(0, 0xffff),
558 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)
562 );
563 }
564
568 private static function readFromStream()
569 {
570 return file_get_contents('php://input');
571 }
572
577 private static function encode(array $data)
578 {
579 return Main\Web\Json::encode($data, JSON_UNESCAPED_UNICODE);
580 }
581
586 private static function decode($data)
587 {
588 try
589 {
590 return Main\Web\Json::decode($data);
591 }
592 catch (Main\ArgumentException $exception)
593 {
594 return false;
595 }
596 }
597
601 private static function getAdditionalData(): array
602 {
603 $result = [
604 'platform_data' => self::getPlatformData(),
605 ];
606
607 $integrationData = self::getIntegrationData();
608 if ($integrationData)
609 {
610 $result['integration_data'] = $integrationData;
611 }
612
613 return $result;
614 }
615
619 private static function getPlatformData(): string
620 {
621 if (Main\ModuleManager::isModuleInstalled('bitrix24'))
622 {
623 $platformType = 'Bitrix24';
624 }
625 elseif (Main\ModuleManager::isModuleInstalled('intranet'))
626 {
627 $platformType = 'Self-hosted';
628 }
629 else
630 {
631 $platformType = 'Bitrix Site Manager';
632 }
633
634 return $platformType;
635 }
636
640 private static function getIntegrationData(): ?string
641 {
642 $version = self::getSaleModuleVersion();
643 if (!$version)
644 {
645 return null;
646 }
647
648 return 'bePaid system module v' . $version;
649 }
650
654 private static function getSaleModuleVersion(): ?string
655 {
656 $modulePath = getLocalPath('modules/sale/install/version.php');
657 if ($modulePath === false)
658 {
659 return null;
660 }
661
662 $arModuleVersion = [];
663 include $_SERVER['DOCUMENT_ROOT'] . $modulePath;
664 return isset($arModuleVersion['VERSION']) ? (string)$arModuleVersion['VERSION'] : null;
665 }
666}
$sum
Определения checkout.php:6
if(!Loader::includeModule('catalog')) if(!AccessController::getCurrent() ->check(ActionDictionary::ACTION_PRICE_EDIT)) if(!check_bitrix_sessid()) $request
Определения catalog_reindex.php:36
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
$errors
Определения iblock_catalog_edit.php:74
$_SERVER["DOCUMENT_ROOT"]
Определения cron_frame.php:9
if(!is_null($config))($config as $configItem)(! $configItem->isVisible()) $code
Определения options.php:195
getLocalPath($path, $baseFolder="/bitrix")
Определения tools.php:5092
$name
Определения menu_edit.php:35
$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(empty($signedUserToken)) $key
Определения quickway.php:257
if($inWords) echo htmlspecialcharsbx(Number2Word_Rus(roundEx($totalVatSum $params['CURRENCY']
Определения template.php:799
$response
Определения result.php:21
$method
Определения index.php:27
$postData
Определения index.php:29
$error
Определения subscription_card_product.php:20
$action
Определения file_dialog.php:21
$url
Определения iframe.php:7
$fields
Определения yandex_run.php:501