From d53430155ab627ee2eee5eb987562fb015a9d84d Mon Sep 17 00:00:00 2001 From: Ivan Sadovin Date: Tue, 7 Jul 2026 19:19:14 +0300 Subject: [PATCH] fix: changed check response from xml to json --- README.md | 2 +- install.xml | 2 +- .../extension/payment/payanyway.php | 2 +- .../extension/payment/payanyway.php | 262 +++++++++++++----- 4 files changed, 203 insertions(+), 65 deletions(-) diff --git a/README.md b/README.md index bd51ab7..3776fdc 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ![PHP](https://img.shields.io/badge/PHP-5.6%2B-purple) ![License](https://img.shields.io/badge/License-MIT-green) -> **📦 Версия модуля: 1.0.2** +> **📦 Версия модуля: 1.0.3** > **ℹ️ Совместимость версий OpenCart:** > 3.0.0.0, 3.0.1.1, 3.0.1.2, 3.0.2.0, 3.0.3.0, 3.0.3.1, 3.0.3.2, 3.0.3.3, 3.0.3.4, 3.0.3.5, 3.0.3.6, 3.0.3.7, 3.0.3.8, 3.0.3.9, 3.0.4.0, 3.0.4.1, 3.0.5.0 diff --git a/install.xml b/install.xml index b4a2813..de05886 100644 --- a/install.xml +++ b/install.xml @@ -2,7 +2,7 @@ PayAnyWay payanyway - 1.0.2 + 1.0.3 PayAnyWay https://payanyway.ru/ diff --git a/upload/admin/controller/extension/payment/payanyway.php b/upload/admin/controller/extension/payment/payanyway.php index 7f630b3..50092de 100755 --- a/upload/admin/controller/extension/payment/payanyway.php +++ b/upload/admin/controller/extension/payment/payanyway.php @@ -9,7 +9,7 @@ class ControllerExtensionPaymentPayanyway extends Controller { const CMS_NAME = 'OpenCart'; const MODULE_NAME = 'payanyway'; - const MODULE_VERSION = '1.0.2'; + const MODULE_VERSION = '1.0.3'; const MODULE_DEFAULT_SORT_ORDER = 0; private $supportedCurrencies = array('RUB'); diff --git a/upload/catalog/controller/extension/payment/payanyway.php b/upload/catalog/controller/extension/payment/payanyway.php index aa598c5..69145b6 100755 --- a/upload/catalog/controller/extension/payment/payanyway.php +++ b/upload/catalog/controller/extension/payment/payanyway.php @@ -10,14 +10,20 @@ class ControllerExtensionPaymentPayanyway extends Controller { const PAYMENT_URL_PROD = 'https://www.payanyway.ru/assistant.htm'; const PAYMENT_URL_DEMO = 'https://demo.moneta.ru/assistant.htm'; + const FAIL_RESPONSE = 'FAIL'; + const XML_CONTENT_TYPE = 'application/xml'; + const JSON_CONTENT_TYPE = 'application/json'; + const TEXT_CONTENT_TYPE = 'text/plain; charset=UTF-8'; + const TRANSACTION_ID_STRING_DELIMITER = '|'; const DESCRIPTION_MAX_LENGTH = 500; const CLIENT_NAME_MAX_LENGTH = 256; - const INVENTORY_ITEM_NAME_MAX_LENGTH = 128; - const INVENTORY_ITEM_DEFAULT_PAYMENT_METHOD = 'full_payment'; - const INVENTORY_ITEM_DEFAULT_PAYMENT_OBJECT = 'commodity'; - const INVENTORY_ITEM_DEFAULT_MEASURE = 'unit'; + const ITEM_NAME_MAX_LENGTH = 128; + const DEFAULT_PAYMENT_METHOD = 'full_payment'; + const DEFAULT_PAYMENT_OBJECT = 'commodity'; + const SHIPPING_PAYMENT_OBJECT = 'service'; + const DEFAULT_MEASURE = 'unit'; /** @var int */ private $mntId; @@ -259,19 +265,14 @@ class ControllerExtensionPaymentPayanyway extends Controller { * @throws \Exception */ public function callback() { - if (null === $this->orderId) { - $this->sendResponse('FAIL'); - } - if (false === $this->orderInfo) { - $this->sendResponse('FAIL'); - } - $requestData = ($_SERVER['REQUEST_METHOD'] === 'POST') ? $_POST : $_GET; - $callbackData = $this->getCallbackData($requestData); + $callback = $this->getCallbackData($requestData); - (isset($callbackData['MNT_COMMAND'])) && ('CHECK' === $callbackData['MNT_COMMAND']) - ? $this->handleCheckCallback($callbackData) - : $this->handlePayCallback($callbackData); + $this->validateCallback($callback); + + (isset($callback['MNT_COMMAND'])) && ('CHECK' === $callback['MNT_COMMAND']) + ? $this->handleCheckCallback($callback) + : $this->handlePayCallback($callback); } /** @@ -308,23 +309,46 @@ class ControllerExtensionPaymentPayanyway extends Controller { } /** - * @param string $response - * @param int $statusCode + * @param array|string $response + * @param int $statusCode * @return void */ - private function sendResponse($response, $statusCode = 200) { + private function sendResponse($response, $statusCode = 200) + { http_response_code($statusCode); - - if (strpos($response, 'orderId) { + $this->sendResponse(self::FAIL_RESPONSE); + } + if (false === $this->orderInfo) { + $this->sendResponse(self::FAIL_RESPONSE); + } + + if (empty($this->mntId) || empty($this->integrityCode)) { + $this->sendResponse(self::FAIL_RESPONSE); + } + + if ($this->mntId !== (int)$callback['MNT_ID']) { + $this->sendResponse(self::FAIL_RESPONSE); + } + } + /** * @param array $data * @return bool @@ -358,13 +382,19 @@ class ControllerExtensionPaymentPayanyway extends Controller { */ private function handleCheckCallback(array $callbackData) { if (!$this->checkSignature($callbackData, $callbackData['MNT_SIGNATURE'])) { - $this->sendResponse('FAIL'); + $this->sendResponse(self::FAIL_RESPONSE); } list($resultCode, $description) = $this->determineCheckResult($callbackData); - $responseData = $this->buildResponseData($callbackData, $resultCode, $description); + $responseData = $this->buildBaseResponseData($callbackData, $resultCode, $description); - $this->sendResponse($this->buildXMLResponse($responseData)); + $products = $this->getAccountOrderProducts(); + $deliveryInfo = $this->getDeliveryInfo(); + + $responseData['client'] = $this->getClientInfo(); + $responseData['items'] = $this->getCheckItems($products, $deliveryInfo); + + $this->sendResponse($this->buildJsonResponse($responseData)); } /** @@ -386,7 +416,7 @@ class ControllerExtensionPaymentPayanyway extends Controller { ); if (!$this->checkSignature($shopData, $callbackData['MNT_SIGNATURE'])) { - $this->sendResponse('FAIL'); + $this->sendResponse(self::FAIL_RESPONSE); } $isPayedOrder = $this->isOrderPaid(); @@ -397,9 +427,19 @@ class ControllerExtensionPaymentPayanyway extends Controller { $resultCode = 200; $description = $isPayedOrder ? 'Order already paid' : 'Order success paid'; - $responseData = $this->buildResponseData($callbackData, $resultCode, $description); + $responseData = $this->buildBaseResponseData($callbackData, $resultCode, $description); - $this->sendResponse($this->buildXMLResponse($responseData)); + $products = $this->getAccountOrderProducts(); + $deliveryInfo = $this->getDeliveryInfo(); + + $payInventoryJson = $this->getPayInventoryJson($products, $deliveryInfo); + $responseData['inventory'] = $payInventoryJson ?: null; + $clientInfo = $this->getClientInfo(); + $responseData['client'] = $clientInfo + ? json_encode([$clientInfo], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) + : null; + + $this->sendResponse($this->buildXmlResponse($responseData)); } /** @@ -409,14 +449,14 @@ class ControllerExtensionPaymentPayanyway extends Controller { * @return array * @throws \Exception */ - private function buildResponseData(array $callbackData, $resultCode, $description) { + private function buildBaseResponseData(array $callbackData, $resultCode, $description) { $orderTotal = (float)(isset($this->orderInfo['total']) ? $this->orderInfo['total'] : 0); $data = array( 'MNT_ID' => $this->mntId, 'MNT_TRANSACTION_ID' => $callbackData['MNT_TRANSACTION_ID'], 'MNT_RESULT_CODE' => $resultCode, - 'MNT_DESCRIPTION' => $description, + 'MNT_DESCRIPTION' => $this->validateString($description, self::DESCRIPTION_MAX_LENGTH), 'MNT_AMOUNT' => $this->formatPrice($orderTotal), 'MNT_CURRENCY_CODE' => $callbackData['MNT_CURRENCY_CODE'], ); @@ -424,15 +464,6 @@ class ControllerExtensionPaymentPayanyway extends Controller { $signatureSource = $resultCode . $this->mntId . $callbackData['MNT_TRANSACTION_ID'] . $this->integrityCode; $data['MNT_SIGNATURE'] = md5($signatureSource); - $this->load->model('account/order'); - $products = $this->model_account_order->getOrderProducts($this->orderId); - $deliveryPrice = $this->getDeliveryPrice(); - - $data['inventory'] = $this->getInventoryJson($products) ?: null; - $data['client'] = $this->getClientInfoJson() ?: null; - $data['sno'] = null; - $data['delivery'] = (null !== $deliveryPrice) ? $this->formatPrice($deliveryPrice) : null; - return $data; } @@ -449,7 +480,7 @@ class ControllerExtensionPaymentPayanyway extends Controller { $orderStatus = $this->getOrderStatusById($orderStatusId); if (empty($callbackData['MNT_AMOUNT'])) { - return array(100, "Order status is '{$orderStatus}'"); + return array(100, "Order status is {$orderStatus}"); } if ($this->successOrderStatusId === $orderStatusId) { @@ -457,7 +488,7 @@ class ControllerExtensionPaymentPayanyway extends Controller { } if (in_array($orderStatusId, array(7, 9, 16), true)) { - return array(500, "Order status is '{$orderStatus}'"); + return array(500, "Order status is {$orderStatus}"); } return array(402, 'Order created, but not paid'); @@ -473,15 +504,14 @@ class ControllerExtensionPaymentPayanyway extends Controller { } /** - * @return float|null + * @return array * @throws \Exception */ - private function getDeliveryPrice() { + private function getDeliveryInfo() { $this->load->model('account/order'); foreach ($this->model_account_order->getOrderTotals($this->orderId) as $orderTotal) { if (isset($orderTotal['code']) && ($orderTotal['code'] === 'shipping')) { - $deliveryPrice = isset($orderTotal['value']) ? (float)$orderTotal['value'] : 0; - return ($deliveryPrice !== 0.0) ? $deliveryPrice : null; + return $orderTotal; } } @@ -490,10 +520,11 @@ class ControllerExtensionPaymentPayanyway extends Controller { /** * @param array $products + * @param array $deliveryInfo * @return false|string */ - private function getInventoryJson(array $products) { - if (empty($products)) { + private function getPayInventoryJson(array $products, array $deliveryInfo) { + if (empty($products) && empty($deliveryInfo)) { return false; } @@ -504,22 +535,33 @@ class ControllerExtensionPaymentPayanyway extends Controller { $quantity = isset($product['quantity']) ? (float)$product['quantity'] : 0; $inventory[] = array( - 'name' => $this->validateString($name, self::INVENTORY_ITEM_NAME_MAX_LENGTH), + 'name' => $this->validateString($name, self::ITEM_NAME_MAX_LENGTH), 'price' => $this->formatPrice($price), 'quantity' => $this->formatQuantity($quantity), 'vatTag' => $this->vatRate, - 'pm' => self::INVENTORY_ITEM_DEFAULT_PAYMENT_METHOD, - 'po' => self::INVENTORY_ITEM_DEFAULT_PAYMENT_OBJECT, - 'measure' => self::INVENTORY_ITEM_DEFAULT_MEASURE, + 'pm' => self::DEFAULT_PAYMENT_METHOD, + 'po' => self::DEFAULT_PAYMENT_OBJECT, + 'measure' => self::DEFAULT_MEASURE, ); } + $name = isset($deliveryInfo['title']) ? $deliveryInfo['title'] : ''; + $price = isset($deliveryInfo['value']) ? (float)$deliveryInfo['value'] : 0; + $inventory[] = array( + 'name' => $this->validateString($name, self::ITEM_NAME_MAX_LENGTH), + 'price' => $this->formatPrice($price), + 'quantity' => '1', + 'vatTag' => $this->vatRate, + 'pm' => self::DEFAULT_PAYMENT_METHOD, + 'po' => self::SHIPPING_PAYMENT_OBJECT, + 'measure' => self::DEFAULT_MEASURE, + ); + return json_encode($inventory, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); } - /** @return false|string */ - private function getClientInfoJson() - { + /** @return array */ + private function getClientInfo() { $clientInfo = array(); $name = $this->getClientName(); if (null !== $name) { @@ -534,11 +576,67 @@ class ControllerExtensionPaymentPayanyway extends Controller { $clientInfo['phone'] = $phoneNumber; } - if (empty($clientInfo)) { - return false; + return $clientInfo; + } + + /** + * @param array $products + * @param array $deliveryInfo + * @return non-empty-array + */ + private function getCheckItems(array $products, array $deliveryInfo) { + $checkItems = array(); + foreach ($products as $product) { + $name = isset($product['name']) ? $product['name'] : ''; + $price = isset($product['price']) ? (float)$product['price'] : 0; + $quantity = isset($product['quantity']) ? (float)$product['quantity'] : 0; + + $checkItems[] = array( + 'name' => $this->validateString($name, self::ITEM_NAME_MAX_LENGTH), + 'price' => $this->formatPrice($price), + 'quantity' => $quantity, + 'vat' => $this->getVat($this->vatRate), + 'measure' => self::DEFAULT_MEASURE, + 'paymentMethod' => self::DEFAULT_PAYMENT_METHOD, + 'paymentObject' => self::DEFAULT_PAYMENT_OBJECT, + ); } - return json_encode(array($clientInfo), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + $name = isset($deliveryInfo['title']) ? $deliveryInfo['title'] : ''; + $price = isset($deliveryInfo['value']) ? (float)$deliveryInfo['value'] : 0; + $checkItems[] = array( + 'name' => $this->validateString($name, self::ITEM_NAME_MAX_LENGTH), + 'price' => $this->formatPrice($price), + 'quantity' => 1, + 'vat' => $this->getVat($this->vatRate), + 'measure' => self::DEFAULT_MEASURE, + 'paymentMethod' => self::DEFAULT_PAYMENT_METHOD, + 'paymentObject' => self::SHIPPING_PAYMENT_OBJECT, + ); + + return $checkItems; + } + + /** + * @param string $vatRate + * @return string + */ + public function getVat($vatRate) { + $map = array( + '1104' => 'vat0', + '1108' => 'vat5', + '1109' => 'vat7', + '1103' => 'vat10', + '7000' => 'vat20', + '1113' => 'vat22', + '1110' => 'vat105', + '1111' => 'vat107', + '1107' => 'vat110', + '7001' => 'vat120', + '1114' => 'vat122', + ); + + return isset($map[$vatRate]) ? $map[$vatRate] : 'none'; } /** @@ -572,13 +670,40 @@ class ControllerExtensionPaymentPayanyway extends Controller { return rtrim(rtrim($quantityString, '0'), '.'); } + /** + * @param array $data + * @return array + * @throws \JsonException + */ + public function buildJsonResponse(array $data) { + $jsonData = array( + 'id' => $data['MNT_ID'], + 'transactionId' => $data['MNT_TRANSACTION_ID'], + 'amount' => $data['MNT_AMOUNT'], + 'signature' => $data['MNT_SIGNATURE'], + 'resultCode' => $data['MNT_RESULT_CODE'], + 'description' => $data['MNT_DESCRIPTION'], + 'externalId' => $data['MNT_TRANSACTION_ID'], + 'receipt' => array( + 'client' => $data['client'], + 'items' => $data['items'], + ), + ); + + $json = json_encode($jsonData, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + + return array( + 'data' => $json ?: '', + 'contentType' => self::JSON_CONTENT_TYPE, + ); + } + /** * @param array $data - * @return false|string - * @return bool|string + * @return array * @throws \Exception */ - private function buildXMLResponse(array $data) { + private function buildXmlResponse(array $data) { $dom = new \DOMDocument('1.0', 'UTF-8'); $dom->formatOutput = false; @@ -621,7 +746,11 @@ class ControllerExtensionPaymentPayanyway extends Controller { } $xml = $dom->saveXML(); - return (false !== $xml) ? $xml : 'FAIL'; + + return array( + 'data' => (false !== $xml) ? $xml : self::FAIL_RESPONSE, + 'contentType' => (false !== $xml) ? self::XML_CONTENT_TYPE : self::TEXT_CONTENT_TYPE, + ); } /** @@ -724,4 +853,13 @@ class ControllerExtensionPaymentPayanyway extends Controller { $this->errorOrderStatusId = (int)$this->config->get('payment_payanyway_error_order_status_id'); $this->cmsModuleVersion = (string)$this->config->get('payment_payanyway_cms_module_version'); } + + /** + * @return array + * @throws \Exception + */ + public function getAccountOrderProducts() { + $this->load->model('account/order'); + return $this->model_account_order->getOrderProducts($this->orderId); + } }