'Оплата через Монету временно недоступна. Свяжитесь с магазином.', PaymentEligibility::ALREADY_PAID => 'Заказ оплачен.', PaymentEligibility::CANCELLED => 'Заказ отменён.', PaymentEligibility::AWAITING_STATUS => 'Заказ обрабатывается менеджером.', PaymentEligibility::UNSUPPORTED_CURRENCY => 'Оплата через Монету возможна только в рублях.', PaymentEligibility::ZERO_AMOUNT => 'Оплата не требуется.', ]; /** Текст кнопки оплаты; на карточке после оформления к нему добавляется сумма. */ private const BUTTON_TITLE = 'Оплатить'; private const UNAVAILABLE = 'Оплата временно недоступна. Попробуйте позже или свяжитесь с магазином.'; /** @var SettingsRepository */ private $settingsRepository; /** @var OrderRepository */ private $orderRepository; /** @var ShopContext */ private $shopContext; public function __construct( SettingsRepository $settingsRepository, OrderRepository $orderRepository, ShopContext $shopContext ) { $this->settingsRepository = $settingsRepository; $this->orderRepository = $orderRepository; $this->shopContext = $shopContext; } public static function create(): self { return new self(new SettingsRepository(), new OrderRepository(), new ShopContext()); } /** * Блок на странице «Заказ оформлен»: сумма и кнопка, если заказ уже можно оплатить, иначе * текст о состоянии заказа. Разметку собирает шаблон `payment_forma.tpl` в хуке; части — в cp1251. */ public function afterCheckout(int $orderId): CheckoutBlock { return $this->block($orderId, true); } /** * Ячейка «Способ оплаты» в личном кабинете: описание оплаты и кнопка или состояние заказа, без суммы. */ public function inAccount(int $orderId): string { $block = $this->block($orderId, false); if ($block->isEmpty()) { return ''; } $info = ($block->getInfo() === '') ? '' : '

' . $block->getInfo() . '

'; return HostCharset::toPage('
' . $info . $block->getForm() . '
'); } /** * Что показать по состоянию заказа: форму оплаты с суммой, если заказ можно оплатить, * иначе — текст о состоянии. * * @param string $formHtml форма оплаты; нужна только для {@see PaymentEligibility::PAYABLE} * @param string $total сумма к оплате текстом; показывается только вместе с формой * @param string $description «Описание оплаты» обычным текстом; показывается только вместе с формой: * «Оплатите заказ» без кнопки (заказ ждёт менеджера) сбивал бы с толку */ public static function compose( string $eligibility, string $formHtml, string $total = '', string $description = '' ): CheckoutBlock { if ($eligibility === PaymentEligibility::PAYABLE) { return new CheckoutBlock($formHtml, $total, self::descriptionHtml($description)); } return new CheckoutBlock('

' . (self::MESSAGES[$eligibility] ?? self::UNAVAILABLE) . '

'); } private function block(int $orderId, bool $afterCheckout): CheckoutBlock { try { $settings = $this->settingsRepository->load(); $order = $this->orderRepository->findById($orderId); if ($order === null) { return new CheckoutBlock(''); } // Личный кабинет открывает заказ по одному uid, не сверяя владельца // (`users.core/action_order_info.php` ядра): форму и запись в сессию — только владельцу. if (!$afterCheckout && !ReturnAccess::isOwner($order, $this->shopContext->sessionUserId())) { return new CheckoutBlock(''); } $currency = $this->shopContext->orderCurrency(); $eligibility = PaymentEligibility::check($settings, $order, $currency); if ($eligibility !== PaymentEligibility::PAYABLE) { return self::compose($eligibility, ''); } $this->shopContext->rememberPayableOrder($order->getId()); // На карточке после оформления — сумма отдельной строкой и в кнопке, // со знаком валюты витрины; в ячейке личного кабинета — короткая кнопка. $amount = ''; $currencyIcon = ''; if ($afterCheckout) { $amount = AmountText::format($order->getAmount(), $this->shopContext->priceDecimals()); $currencyIcon = $this->shopContext->currencyIcon(); } $suffix = ($currencyIcon === '') ? '' : ' ' . $currencyIcon; $formHtml = $this->form( $settings, $order, (string) AssistantProtocol::normalizeCurrency($currency), ($amount === '') ? self::BUTTON_TITLE : self::BUTTON_TITLE . ' ' . $amount, ($amount === '') ? '' : $suffix, ); $total = ($amount === '') ? '' : $amount . $suffix; $description = HostCharset::fromDatabase($settings->getPaymentDescription()); return self::compose($eligibility, $formHtml, $total, $description); } catch (\Throwable $exception) { (new ErrorLogLogger())->error( 'Кнопка оплаты заказа ' . $orderId . ' не построена: ' . $exception->getMessage(), ); return new CheckoutBlock('

' . self::UNAVAILABLE . '

'); } } /** * Текст мерчанта из настроек — в HTML: экранирование, переносы строк — `
`. */ private static function descriptionHtml(string $text): string { $text = trim($text); return ($text === '') ? '' : nl2br(htmlspecialchars($text, ENT_QUOTES, 'Windows-1251'), false); } private function form( Settings $settings, Order $order, string $currency, string $buttonTitle, string $buttonSuffixHtml ): string { return PaymentForm::create( $settings, $order, $currency, $this->shopContext->now(), new ReturnUrls($this->shopContext->returnPageUrl()), 'Оплата заказа №' . $order->getUid(), $this->shopContext->cms(), AssistantProtocol::normalizeLocale($this->shopContext->shopLocale()), )->toHtml($buttonTitle, $buttonSuffixHtml); } }