initializeModule(); $this->loadOrder($this->getOrderIdFromSession()); } /** * @throws \Exception */ public function index(): string { if (([] === $this->orderInfo) || !$this->isPaidWithModule() || !$this->isAwaitingPayment((int)($this->orderInfo['order_status_id'] ?? 0)) ) { return ''; } $this->load->language('extension/payanyway/payment/payanyway'); $this->document->setTitle($this->language->get('heading_title')); $data['action'] = (self::PAYMENT_SERVER_PROD === $this->server) ? self::PAYMENT_URL_PROD : self::PAYMENT_URL_DEMO; $data['MNT_ID'] = $this->mntId; $data['MNT_AMOUNT'] = $this->getOrderAmount(); $data['MNT_TRANSACTION_ID'] = $this->createTransactionId($this->orderId, $data['MNT_AMOUNT']); $data['MNT_CURRENCY_CODE'] = $this->currency; $data['MNT_TEST_MODE'] = $this->testMode ? '1' : '0'; $data['MNT_DESCRIPTION'] = $this->getDescription(); $data['MNT_SUBSCRIBER_ID'] = $this->getSubscriberId(); $data['MNT_SIGNATURE'] = $this->buildFormSignature($data); $urlArgs = $this->getUrlArgs($this->session->data['customer_token'] ?? null); $data['MNT_SUCCESS_URL'] = $this->url->link('extension/payanyway/payment/payanyway.success', $urlArgs, true); $data['MNT_FAIL_URL'] = $this->url->link('extension/payanyway/payment/payanyway.fail', $urlArgs, true); $data['MNT_RETURN_URL'] = $this->url->link('checkout/checkout', $urlArgs, true); $data['moneta_locale'] = $this->language->get('text_locale'); $data['MNT_CMS'] = $this->getCms(); $data['btn_confirm'] = $this->language->get('text_paw_pay'); $data['confirm_url'] = $this->url->link('extension/payanyway/payment/payanyway.confirm', $urlArgs, true); return $this->load->view('extension/payanyway/payment/payanyway', $data); } /** * @param array $data */ private function buildFormSignature(array $data): string { return md5( $data['MNT_ID'] . $data['MNT_TRANSACTION_ID'] . $data['MNT_AMOUNT'] . $data['MNT_CURRENCY_CODE'] . $data['MNT_SUBSCRIBER_ID'] . $data['MNT_TEST_MODE'] . $this->integrityCode, ); } public function confirm(): void { $this->load->language('extension/payanyway/payment/payanyway'); $json['success'] = $this->changeStatusUnderOrderLock(true, $this->pendingOrderStatusId, $this->language->get('text_order_confirmed')); $this->response->addHeader('Content-Type: application/json'); $this->response->setOutput(json_encode($json)); } public function success(): void { $this->load->language('extension/payanyway/payment/payanyway'); $this->changeStatusUnderOrderLock(false, $this->pendingOrderStatusId, $this->language->get('text_payment_processing')); $this->response->redirect($this->url->link('checkout/success', $this->getUrlArgs($this->request->get['customer_token'] ?? null), true)); } public function fail(): void { $this->load->language('extension/payanyway/payment/payanyway'); $this->changeStatusUnderOrderLock(false, $this->errorOrderStatusId, $this->language->get('text_payment_error')); $this->response->redirect($this->url->link('checkout/failure', '', true)); } /** * Смена статуса со страницы покупателя — под блокировкой заказа, как в Pay URL: заказ * перечитывается, оплаченный (сейчас или по истории) не трогается. Возврат с MONETA.RU * приходит параллельно с уведомлением об оплате. * * @param bool $allowFailed подтверждение формы допускает повторную попытку после «Неуспешного» * @throws \Exception */ private function changeStatusUnderOrderLock(bool $allowFailed, int $orderStatusId, string $comment): bool { if (!$this->isStatusChangeableByCustomer($allowFailed)) { return false; } $this->load->model('extension/payanyway/payment/payanyway'); if (!$this->model_extension_payanyway_payment_payanyway->acquireOrderLock((int)$this->orderId, self::$orderLockTimeoutSeconds)) { return false; } $this->loadOrder($this->orderId); $changeable = $this->isStatusChangeableByCustomer($allowFailed) && !$this->model_extension_payanyway_payment_payanyway->hasOrderStatusInHistory((int)$this->orderId, $this->successOrderStatusId); if ($changeable) { $this->addOrderHistory($this->orderId, $orderStatusId, $comment); } $this->model_extension_payanyway_payment_payanyway->releaseOrderLock((int)$this->orderId); return $changeable; } /** Заказ модуля, ещё не ушедший дальше «Обрабатываемого» (или «Неуспешного» — для повторной попытки). */ private function isStatusChangeableByCustomer(bool $allowFailed): bool { if (([] === $this->orderInfo) || !$this->isPaidWithModule()) { return false; } $orderStatusId = (int)$this->orderInfo['order_status_id']; return $allowFailed ? $this->isAwaitingPayment($orderStatusId) : in_array($orderStatusId, [0, $this->pendingOrderStatusId], true); } private function getDescription(): string { $orderId = $this->orderInfo['order_id'] ?? null; $description = $this->language->get('text_description'); if (!empty($orderId)) { $description .= ' ' . sprintf($this->language->get('text_description_number'), $orderId); } $name = $this->getClientName(); if (null !== $name) { $description .= ' ' . sprintf($this->language->get('text_description_customer'), $name); } $comment = $this->orderInfo['comment'] ?? ''; if ('' !== $comment) { $description .= ': ' . htmlspecialchars($comment, ENT_QUOTES, 'UTF-8'); } return $this->validateString($description, self::DESCRIPTION_MAX_LENGTH); } private function getUrlArgs(mixed $customerToken): string { $args = 'language=' . $this->config->get('config_language'); if (is_string($customerToken) && ('' !== $customerToken)) { $args .= '&customer_token=' . urlencode($customerToken); } return $args; } private function getSubscriberId(): string { return $this->getClientEmail() ?? $this->getClientPhone() ?? ''; } }