22 private $handlerFields = array();
24 private const FORM_MODE =
'form';
25 private const CHECKOUT_MODE =
'checkout';
26 private const IFRAME_MODE =
'iframe';
36 if ($request ===
null)
41 $mode = $this->getMode();
43 if ($mode === self::CHECKOUT_MODE)
45 return $this->initiateCheckoutPay($payment, $request);
48 if ($mode === self::IFRAME_MODE)
50 return $this->initiateIframePay($payment);
53 return $this->initiateFormPay($payment, $request);
60 $settings = $this->getHandlerSettings();
62 $actionUri = $settings[
'CHECKOUT_DATA'][
'ACTION_URI'] ??
null;
63 if (!isset($actionUri))
71 $template = $this->getCheckoutFormTemplate($payment);
75 $params = $this->getCheckoutPayParams($payment, $request);
76 $requestResult = Rest\Http::sendRequest($actionUri, $params);
77 if (!$requestResult->isSuccess())
79 $result->addErrors($requestResult->getErrors());
83 $requestData = $requestResult->getData();
84 if (empty($requestData[
'PAYMENT_URL']) || empty($requestData[
'PAYMENT_ID']))
86 if (!empty($requestData[
'PAYMENT_ERRORS']) && is_array($requestData[
'PAYMENT_ERRORS']))
88 foreach ($requestData[
'PAYMENT_ERRORS'] as $error)
90 $result->addError(
new Error($error));
100 $result->setPsData([
'PS_INVOICE_ID' => $requestData[
'PAYMENT_ID']]);
101 $url = $requestData[
'PAYMENT_URL'];
103 $result->setPaymentUrl($url);
105 $qrCode = ((
new PaySystem\BarcodeGenerator())->generate($url));
108 $result->setQr(base64_encode($qrCode));
111 $template = $this->getCheckoutPayTemplate($url);
114 if ($this->initiateMode === static::STREAM)
120 $result->setTemplate($template);
126 private function needMoreCheckoutParams(array $settings, array $params): bool
128 $checkoutSettings = $settings[
'CHECKOUT_DATA'];
129 if (isset($checkoutSettings[
'FIELDS']))
131 return !empty(array_diff_key($checkoutSettings[
'FIELDS'], $params));
137 private function getCheckoutPayParams(Payment $payment, Request $request): array
141 $checkoutSettings = $this->getHandlerSettings()[
'CHECKOUT_DATA'];
142 if (isset($checkoutSettings[
'FIELDS']))
144 $params = $this->getQueryDataFromFields($payment, $checkoutSettings[
'FIELDS']);
147 $requestData = $request->toArray();
148 foreach ($requestData as $field => $value)
150 if (isset($checkoutSettings[
'FIELDS'][$field]))
152 $params[$field] = $value;
156 return array_merge($params, $this->getSystemParams($payment));
159 private function getCheckoutFormTemplate(Payment $payment): string
161 $settings = $this->getHandlerSettings();
162 $formSettings = $settings[
'CHECKOUT_DATA'];
164 $template =
'<div class="mb-4" id="rest-checkout">';
165 $template .=
'<form name="rest-checkout-form" id="rest-checkout-form">';
167 if (isset($formSettings[
'FIELDS']))
169 $template .= $this->getTemplateFromFields($payment, $formSettings[
'FIELDS']);
172 $template .=
'<input type="hidden" name="BX_PAYSYSTEM_ID" value="'.$this->service->getField(
'ID').
'">';
173 $template .=
'<input name="button" value="'.Loc::getMessage(
'SALE_HANDLERS_REST_HANDLER_BUTTON_PAID').
'" type="submit" class="btn btn-lg btn-success pl-4 pr-4" style="border-radius: 32px;">';
174 $template .=
'</form>';
175 $template .=
'</div>';
180 BX.message(' . \CUtil::PhpToJSObject($messages) .
');
190 if (BX.Sale.RestHandler)
195 BX.Sale.RestHandler = {
196 init: function(params)
198 this.formNode = BX(params.formId);
199 this.paysystemBlockNode = BX(params.paysystemBlockId);
200 this.ajaxUrl = params.ajaxUrl;
201 this.paymentId = params.paymentId;
202 this.paySystemId = params.paySystemId;
203 this.isAllowedSubmitting = true;
204 this.returnUrl = params.returnUrl;
209 bindEvents: function()
211 BX.bind(this.formNode, "submit", BX.proxy(this.sendRequest, this));
214 sendRequest: function(e)
218 if (!this.isAllowedSubmitting)
224 formData = this.getAllFormData(),
225 submitButton = this.formNode.querySelector("input[type=\"submit\"]"),
230 submitButton.disabled = true;
232 this.isAllowedSubmitting = false;
235 sessid: BX.bitrix_sessid(),
236 PAYMENT_ID: this.paymentId,
237 PAYSYSTEM_ID: this.paySystemId,
238 RETURN_URL: this.returnUrl,
243 if (formData.hasOwnProperty(i))
245 data[i] = formData[i];
254 onsuccess: BX.proxy(function (result) {
255 if (result.status === "success")
257 this.isAllowedSubmitting = true;
258 this.updateTemplateHtml(result.template);
260 else if (result.status === "error")
262 this.isAllowedSubmitting = true;
263 this.showErrorTemplate(result.buyerErrors);
264 BX.onCustomEvent("onPaySystemAjaxError", [result.buyerErrors]);
270 getAllFormData: function()
272 var prepared = BX.ajax.prepareForm(this.formNode),
275 for (i in prepared.data)
277 if (prepared.data.hasOwnProperty(i) && i === "")
279 delete prepared.data[i];
283 return !!prepared && prepared.data ? prepared.data : {};
286 updateTemplateHtml: function (html)
288 BX.html(this.paysystemBlockNode, html)
291 showErrorTemplate: function(errors)
294 BX.message("SALE_HANDLERS_REST_HANDLER_TEMPLATE_ERROR_MESSAGE_HEADER"),
298 for (var error in errors)
300 if (errors.hasOwnProperty(error))
302 errorsList.push(errors[error]);
307 errorsList.push(BX.message("SALE_HANDLERS_REST_HANDLER_TEMPLATE_ERROR_MESSAGE_FOOTER"));
309 var resultDiv = BX.create("div", {
310 props: {className: "alert alert-danger"},
311 html: errorsList.join("<br />"),
314 this.paysystemBlockNode.innerHTML = "";
315 this.paysystemBlockNode.appendChild(resultDiv);
320 BX.ready(function() {
321 BX.Sale.RestHandler.init({
322 formId: "rest-checkout-form",
323 paysystemBlockId: "rest-checkout",
324 ajaxUrl: "/bitrix/tools/sale_ps_ajax.php",
325 paymentId: "' . \CUtil::JSEscape($payment->getId()) .
'",
326 paySystemId: "' . \CUtil::JSEscape($payment->getPaymentSystemId()) .
'",
327 returnUrl: "' . $this->service->getContext()->getUrl() .
'",
336 private function getCheckoutPayTemplate($paymentUrl): string
338 $template =
'<a class="btn btn-lg btn-success" style="border-radius: 32px;" href="' . $paymentUrl .
'">';
339 $template .=
Loc::getMessage(
'SALE_HANDLERS_REST_HANDLER_BUTTON_PAID');
345 private function initiateIframePay(Payment $payment): ServiceResult
347 $result =
new ServiceResult();
349 $template = $this->getIframeTemplate($payment);
350 if ($this->initiateMode === static::STREAM)
356 $result->setTemplate($template);
362 private function initiateFormPay(Payment $payment, ?Request $request): ServiceResult
365 if (!$result->isSuccess())
367 $result =
new ServiceResult();
368 $template = $this->getDefaultTemplate($payment);
369 if ($this->initiateMode === static::STREAM)
375 $result->setTemplate($template);
379 $result->setPaymentUrl($this->getPaymentUrl($payment));
388 private function getPaymentUrl(Payment $payment): string
390 if ($this->isAllowAutoRedirect())
392 $settings = $this->getHandlerSettings();
394 if (isset($settings[
'FORM_DATA'][
'FIELDS']))
396 $queryParams = $this->getQueryDataFromFields($payment, $settings[
'FORM_DATA'][
'FIELDS']);
398 elseif (isset($settings[
'FORM_DATA'][
'PARAMS']))
400 $queryParams = $this->getQueryDataFromParams($payment, $settings[
'FORM_DATA'][
'PARAMS']);
403 $queryParams[
'BX_PAYSYSTEM_ID'] = $this->service->getField(
'ID');
404 return (
new Uri($settings[
'FORM_DATA'][
'ACTION_URI']))->addParams($queryParams)->getLocator();
413 private function isAllowAutoRedirect(): bool
417 $settings = $this->getHandlerSettings();
418 $formSettings = $settings[
'FORM_DATA'];
419 if (!empty($formSettings[
'ACTION_URI']) && mb_strtoupper($formSettings[
'METHOD']) ===
'GET')
422 if (isset($formSettings[
'FIELDS']))
424 foreach ($formSettings[
'FIELDS'] as $value)
426 if ((isset($value[
'VISIBLE']) && $value[
'VISIBLE'] ===
'Y') || is_array($value[
'CODE']))
443 private function getQueryDataFromFields(Payment $payment, array $fields): array
448 foreach ($fields as $key => $value)
450 if (!is_array($value[
'CODE']) && !empty($value[
'CODE']))
452 $result[$key] = $businessValueParams[$value[
'CODE']];
459 private function getIframeTemplate(Payment $payment): string
461 \CJSCore::Init(
"loader");
463 $settings = $this->getHandlerSettings();
464 $formSettings = $settings[
'IFRAME_DATA'];
466 $iframeData = $this->getIframePayParams($payment);
468 $actionUriHost = (
new Uri($formSettings[
'ACTION_URI']))->getHost();
471 <div class='rest-paysystem-wrapper' id='rest-paysystem-wrapper'>
473 src='{$formSettings['ACTION_URI']}'
474 class='rest-payment-frame'
475 name='restPaymentFrame'
476 id='rest-payment-frame'
477 style='border: none; height: 350px; width: 100%'
478 sandbox='allow-forms allow-scripts allow-modals allow-top-navigation allow-same-origin'
480 <div class='alert alert-danger'>" .
Loc::getMessage(
'SALE_HANDLERS_REST_HANDLER_ERROR_IFRAME') .
"</div>
482 <div class='alert alert-info'>" .
Loc::getMessage(
'SALE_HANDLERS_REST_HANDLER_TEMPLATE_WARNING_RETURN') .
"</div>
487 BX.ready(function() {
488 var iframe = document.getElementById("rest-payment-frame");
493 loader = new BX.Loader({
494 target: iframe.parentElement,
495 size: iframe.offsetHeight / 2,
500 var parent = iframe.parentElement;
501 iframe.style.width = parent.clientWidth;
503 iframe.onload = function () {
509 var paymentFrame = iframe.contentWindow;
512 var iframeData = ' . \CUtil::PhpToJSObject($iframeData) .
';
513 iframeData.BX_COMPUTED_STYLE = JSON.parse(JSON.stringify(window.getComputedStyle(parent)));
515 paymentFrame.postMessage(iframeData, "' . $formSettings[
'ACTION_URI'] .
'");
518 iframe.onerror = function () {
524 var restPaysystemWrapper = document.getElementById("rest-paysystem-wrapper");
525 restPaysystemWrapper.innerHTML = "";
526 restPaysystemWrapper.appendChild(
528 props: {className: "alert alert-danger"},
529 text: "' .
Loc::getMessage(
'SALE_HANDLERS_REST_HANDLER_ERROR_IFRAME_LOAD') .
'",
534 window.addEventListener("message", function (event) {
537 var originHost = new URL(event.origin).hostname;
544 if (originHost !== "' . $actionUriHost .
'")
549 if (event.data.width && parseInt(event.data.width) > 0)
551 iframe.style.width = event.data.width + "px";
553 if (event.data.height && parseInt(event.data.height) > 0)
555 iframe.style.height = event.data.height + "px";
565 private function getIframePayParams(Payment $payment): array
569 $formSettings = $this->getHandlerSettings()[
'IFRAME_DATA'];
570 if (isset($formSettings[
'FIELDS']))
572 $params = $this->getQueryDataFromFields($payment, $formSettings[
'FIELDS']);
575 return array_merge($params, $this->getSystemParams($payment));
583 private function getDefaultTemplate(Payment $payment): string
585 $settings = $this->getHandlerSettings();
586 $formSettings = $settings[
'FORM_DATA'];
588 $template =
'<form action="'.htmlspecialcharsbx($formSettings[
'ACTION_URI']).
'" method="'.htmlspecialcharsbx($formSettings[
'METHOD']).
'" name="rest-handler-form">';
590 if (isset($formSettings[
'FIELDS']))
592 $template .= $this->getTemplateFromFields($payment, $formSettings[
'FIELDS']);
594 elseif (isset($formSettings[
'PARAMS']))
596 $template .= $this->getTemplateFromParams($payment, $formSettings[
'PARAMS']);
599 $template .=
'<input type="hidden" name="BX_PAYSYSTEM_ID" value="' . $this->service->getField(
'ID') .
'">';
600 $template .=
'<input type="hidden" name="BX_RETURN_URL" value="' . $this->service->getContext()->getUrl() .
'">';
601 $template .=
'<input name="button" value="' .
Loc::getMessage(
'SALE_HANDLERS_REST_HANDLER_BUTTON_PAID') .
'" type="submit" class="btn btn-lg btn-success pl-4 pr-4" style="border-radius: 32px;">';
602 $template .=
'</form>';
613 private function getTemplateFromFields(Payment $payment, array $fields): string
617 foreach ($fields as $key => $value)
619 $input = $this->getInputParams($payment, $value);
620 $template .= $this->createInput($key, $input);
631 private function getInputParams(Payment $payment, $value): array
640 $settings = $this->getHandlerSettings();
643 if (is_array($value[
'CODE']))
646 $input = $value[
'CODE'];
647 $result[
'NAME'] = $input[
'NAME'] ??
'';
652 $code = $value[
'CODE'];
653 $input = $settings[
'CODES'][$code];
654 $result[
'VALUE'] = $params[$code];
655 $result[
'NAME'] = $input[
'NAME'] ??
'';
658 if (isset($input[
'INPUT'][
'TYPE']))
660 $result[
'TYPE'] = mb_strtoupper($input[
'INPUT'][
'TYPE']);
663 if (isset($value[
'VISIBLE']) && $value[
'VISIBLE'] ===
'Y')
665 $result[
'HIDDEN'] =
'N';
677 private function createInput(
string $name, array $input): string
679 if (!in_array($input[
'TYPE'], [
'Y/N',
'STRING',
'ENUM'],
true))
684 if ($input[
'HIDDEN'] ===
'Y')
686 return Input\Manager::getEditHtml($name, $input);
690 $input[
'STYLE'] =
"max-width: 300px;";
692 if ($input[
'TYPE'] ===
'Y/N')
694 $input[
'CLASS'] =
"form-check-input";
696 $inputHtml .=
'<div class="form-check">';
697 $inputHtml .= Input\Manager::getEditHtml($name, $input);
698 $inputHtml .=
'<label class="form-check-label">'.$input[
'NAME'].
'</label>';
699 $inputHtml .=
'</div>';
701 elseif ($input[
'TYPE'] ===
'STRING' || $input[
'TYPE'] ===
'ENUM')
703 $input[
'CLASS'] =
"form-control";
705 $inputHtml .=
'<div class="form-group">';
706 $inputHtml .=
'<label>'.$input[
'NAME'].
'</label>';
707 $inputHtml .= Input\Manager::getEditHtml($name, $input);
708 $inputHtml .=
'</div>';
719 $settings = $this->getHandlerSettings();
720 return $settings[
'CURRENCY'];
728 $settings = $this->getHandlerSettings();
730 $clientType = (string)($settings[
'CLIENT_TYPE'] ??
'');
736 return parent::getClientType($psMode);
741 $fields = $this->getHandlerFields();
742 $settings = $this->getHandlerSettings();
745 'NAME' => $fields[
'NAME'] ??
'',
746 'SORT' => $fields[
'SORT'] ?? 100,
747 'CODES' => $settings[
'CODES'] ?: []
751 private function getHandlerFields(): array
753 if (!$this->handlerFields)
755 $handler = $this->service->getField(
'ACTION_FILE');
756 $dbRes = PaySystemRestHandlersTable::getList([
757 'filter' => [
'CODE' => $handler]
759 $data = $dbRes->fetch();
763 $this->handlerFields = $data;
767 return $this->handlerFields;
773 private function getHandlerSettings(): array
775 $handlerFields = $this->getHandlerFields();
776 return $handlerFields[
'SETTINGS'] ?? [];
779 private function getMode(): string
781 $settings = $this->getHandlerSettings();
783 if (!empty($settings[
'IFRAME_DATA']))
785 return self::IFRAME_MODE;
788 if (!empty($settings[
'CHECKOUT_DATA']))
790 return self::CHECKOUT_MODE;
793 return self::FORM_MODE;
796 private function getSystemParams(Payment $payment): array
798 $params[
'BX_SYSTEM_PARAMS'] = [
799 'RETURN_URL' => $this->service->getContext()->getUrl(),
800 'PAYSYSTEM_ID' => $this->service->getField(
'ID'),
801 'PAYMENT_ID' => $payment->getId(),
802 'SUM' => $payment->getSum(),
803 'CURRENCY' => $payment->getField(
'CURRENCY'),
806 $invoiceId = $payment->getField(
'PS_INVOICE_ID');
807 if (isset($invoiceId))
809 $params[
'BX_SYSTEM_PARAMS'][
'EXTERNAL_PAYMENT_ID'] = $invoiceId;
824 $result->setPsData($this->getPsData($request));
834 private function getPsData(
Request $request): array
838 'PS_STATUS_CODE' =>
'Y',
843 if ($psInvoiceId = $request->get(
'PS_INVOICE_ID'))
845 $psData[
'PS_INVOICE_ID'] = $psInvoiceId;
848 if ($psStatusCode = $request->get(
'PS_STATUS_CODE'))
850 $psData[
'PS_STATUS_CODE'] = $psStatusCode;
853 if ($psStatusDescription = $request->get(
'PS_STATUS_DESCRIPTION'))
855 $psData[
'PS_STATUS_DESCRIPTION'] = $psStatusDescription;
858 if ($psStatusMessage = $request->get(
'PS_STATUS_MESSAGE'))
860 $psData[
'PS_STATUS_MESSAGE'] = $psStatusMessage;
863 if ($psSum = $request->get(
'PS_SUM'))
865 $psData[
'PS_SUM'] = $psSum;
868 if ($psCurrency = $request->get(
'PS_CURRENCY'))
870 $psData[
'PS_CURRENCY'] = $psCurrency;
873 if ($psRecurringToken = $request->get(
'PS_RECURRING_TOKEN'))
875 $psData[
'PS_RECURRING_TOKEN'] = $psRecurringToken;
878 if ($psCardNumber = $request->get(
'PS_CARD_NUMBER'))
880 $psData[
'PS_CARD_NUMBER'] = $psCardNumber;
892 return $request->get(
'PAYMENT_ID');
902 private function getQueryDataFromParams(
Payment $payment, array $params): array
907 foreach ($params as $key => $value)
909 $result[$key] = $businessValueParams[$value];
922 private function getTemplateFromParams(Payment $payment, array $params): string
927 foreach ($params as $key => $value)
929 $template .=
'<input type="hidden" name="'.htmlspecialcharsbx($key).
'" value="'.htmlspecialcharsbx($businessValueParams[$value]).
'">';
937 if ($request ===
null)
942 $mode = $this->getMode();
943 if ($mode !== self::CHECKOUT_MODE)
948 $settings = $this->getHandlerSettings();
949 $actionUri = $settings[
'CHECKOUT_DATA'][
'ACTION_URI'] ??
null;
950 if (!isset($actionUri))
955 $params = $this->getCheckoutPayParams($payment, $request);
957 return !$this->needMoreCheckoutParams($settings, $params);