diff --git a/README.md b/README.md
index bd51ab7..3776fdc 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@


-> **📦 Версия модуля: 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);
+ }
}