fix: changed check response from xml to json

This commit is contained in:
2026-07-08 15:23:17 +03:00
committed by Git Moneta
parent 018f675acd
commit 6cc9866f24
11 changed files with 301 additions and 76 deletions
+199 -46
View File
@@ -13,11 +13,18 @@ class Callback extends \Opencart\System\Engine\Controller
{
use \Opencart\Catalog\Library\Extension\PayAnyWay\Traits\PayAnyWay;
private const INVENTORY_ITEM_NAME_MAX_LENGTH = 128;
private const INVENTORY_ITEM_DEFAULT_VAT_TAG = '1105';
private const INVENTORY_ITEM_DEFAULT_PAYMENT_METHOD = 'full_payment';
private const INVENTORY_ITEM_DEFAULT_PAYMENT_OBJECT = 'commodity';
private const INVENTORY_ITEM_DEFAULT_MEASURE = 'unit';
private const FAIL_RESPONSE = 'FAIL';
private const XML_CONTENT_TYPE = 'application/xml';
private const JSON_CONTENT_TYPE = 'application/json';
private const TEXT_CONTENT_TYPE = 'text/plain; charset=UTF-8';
public const DESCRIPTION_MAX_LENGTH = 500;
private const ITEM_NAME_MAX_LENGTH = 128;
private const ITEM_DEFAULT_VAT_TAG = '1105';
private const DEFAULT_PAYMENT_METHOD = 'full_payment';
private const DEFAULT_PAYMENT_OBJECT = 'commodity';
private const SHIPPING_PAYMENT_OBJECT = 'service';
private const DEFAULT_MEASURE = 'unit';
private const CANCEL_ORDER_STATUS_IDS = [7, 9, 16];
@@ -42,7 +49,7 @@ class Callback extends \Opencart\System\Engine\Controller
parent::__construct($registry);
if (!$this->validateOrder()) {
$this->sendResponse('FAIL');
$this->sendResponse(self::FAIL_RESPONSE);
}
$this->initializeModule();
@@ -52,6 +59,8 @@ class Callback extends \Opencart\System\Engine\Controller
/** @throws \Exception */
public function index(): void
{
$this->validateCallback();
($this->callbackData['MNT_COMMAND'] === 'CHECK')
? $this->handleCheckCallback()
: $this->handlePayCallback();
@@ -74,28 +83,53 @@ class Callback extends \Opencart\System\Engine\Controller
];
}
private function sendResponse(string $response, int $statusCode = 200): void
private function validateCallback(): void
{
if (empty($this->mntId) || empty($this->integrityCode)) {
$this->sendResponse(self::FAIL_RESPONSE);
}
if ($this->mntId !== (int)$this->callbackData['MNT_ID']) {
$this->sendResponse(self::FAIL_RESPONSE);
}
}
/**
* @param array|string $response
* @param int $statusCode
* @return void
*/
private function sendResponse(array|string $response, int $statusCode = 200): void
{
http_response_code($statusCode);
if (is_string($response)) {
header('Content-Type: ' . self::TEXT_CONTENT_TYPE);
echo $response;
} else {
header('Content-Type: ' . $response['contentType']);
echo $response['data'];
}
$isXml = str_starts_with($response, '<?xml') || str_contains($response, '<MNT_RESPONSE');
header('Content-Type: ' . ($isXml ? 'application/xml' : 'text/plain; charset=UTF-8'));
echo $response;
exit;
exit();
}
/** @throws \Exception */
private function handleCheckCallback(): void
{
if (!$this->checkSignature($this->callbackData)) {
$this->sendResponse('FAIL');
$this->sendResponse(self::FAIL_RESPONSE);
}
[$resultCode, $description] = $this->determineCheckResult();
$responseData = $this->buildResponseData($resultCode, $description);
$responseData = $this->buildBaseResponseData($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));
}
/**
@@ -111,7 +145,7 @@ class Callback extends \Opencart\System\Engine\Controller
$orderStatus = $this->getOrderStatusById($orderStatusId);
if (empty($this->callbackData['MNT_AMOUNT'])) {
return [100, "Order status is '{$orderStatus}'"];
return [100, "Order status is {$orderStatus}"];
}
if ($this->successOrderStatusId === $orderStatusId) {
@@ -119,7 +153,7 @@ class Callback extends \Opencart\System\Engine\Controller
}
if (in_array($orderStatusId, self::CANCEL_ORDER_STATUS_IDS, true)) {
return [500, "Order status is '{$orderStatus}'"];
return [500, "Order status is {$orderStatus}"];
}
return [402, 'Order created, but not paid'];
@@ -139,7 +173,7 @@ class Callback extends \Opencart\System\Engine\Controller
];
if (!$this->checkSignature($shopData)) {
$this->sendResponse('FAIL');
$this->sendResponse(self::FAIL_RESPONSE);
}
$isPayedOrder = $this->isOrderPaid();
@@ -150,9 +184,19 @@ class Callback extends \Opencart\System\Engine\Controller
$resultCode = 200;
$description = $isPayedOrder ? 'Order already paid' : 'Order success paid';
$responseData = $this->buildResponseData($resultCode, $description);
$responseData = $this->buildBaseResponseData($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));
}
/**
@@ -161,7 +205,7 @@ class Callback extends \Opencart\System\Engine\Controller
* @return array<string, mixed>
* @throws \Exception
*/
private function buildResponseData(int $resultCode, string $description): array
private function buildBaseResponseData(int $resultCode, string $description): array
{
$orderTotal = (float)($this->orderInfo['total'] ?? 0);
@@ -169,7 +213,7 @@ class Callback extends \Opencart\System\Engine\Controller
'MNT_ID' => $this->mntId,
'MNT_TRANSACTION_ID' => $this->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' => $this->callbackData['MNT_CURRENCY_CODE'],
];
@@ -177,15 +221,6 @@ class Callback extends \Opencart\System\Engine\Controller
$signatureSource = $resultCode . $this->mntId . $this->callbackData['MNT_TRANSACTION_ID'] . $this->integrityCode;
$data['MNT_SIGNATURE'] = md5($signatureSource);
$this->load->model('account/order');
$products = $this->model_account_order->getProducts($this->orderId);
$deliveryPrice = $this->getDeliveryPrice();
$data['inventory'] = $this->getInventoryJson($products) ?: null;
$data['client'] = $this->callbackData['MNT_SUBSCRIBER_ID'];
$data['sno'] = null;
$data['delivery'] = (null !== $deliveryPrice) ? $this->formatPrice($deliveryPrice) : null;
return $data;
}
@@ -218,39 +253,97 @@ class Callback extends \Opencart\System\Engine\Controller
return hash_equals(md5($signatureString), $this->callbackData['MNT_SIGNATURE']);
}
private function getDeliveryPrice(): ?float
private function getDeliveryInfo(): ?array
{
$this->load->model('account/order');
foreach ($this->model_account_order->getTotals($this->orderId) as $orderTotal) {
if (($orderTotal['code'] ?? '') === 'shipping') {
$deliveryPrice = (float)($orderTotal['value'] ?? 0);
return $deliveryPrice !== 0.0 ? $deliveryPrice : null;
return $orderTotal;
}
}
return null;
}
/**
* @param array<string, mixed> $products
* @return false|string
*/
private function getInventoryJson(array $products): false|string
private function getClientInfo(): array
{
if (empty($products)) {
$clientInfo = [];
$name = $this->getClientName();
if (null !== $name) {
$clientInfo['name'] = $name;
}
$email = $this->getClientEmail();
if (null !== $email) {
$clientInfo['email'] = $email;
}
$phoneNumber = $this->getClientPhone();
if (null !== $phoneNumber) {
$clientInfo['phone'] = $phoneNumber;
}
return $clientInfo;
}
private function getCheckItems(array $products, ?array $deliveryInfo): array
{
$checkItems = array();
foreach ($products as $product) {
$checkItems[] = array(
'name' => $this->validateString($product['name'] ?? '', self::ITEM_NAME_MAX_LENGTH),
'price' => $this->formatPrice((float)($product['price'] ?? 0)),
'quantity' => $this->formatQuantity((float)($product['quantity'] ?? 0)),
'vat' => $this->getVat($this->vatRate),
'measure' => self::DEFAULT_MEASURE,
'paymentMethod' => self::DEFAULT_PAYMENT_METHOD,
'paymentObject' => self::DEFAULT_PAYMENT_OBJECT,
);
}
if (null === $deliveryInfo) {
return $checkItems;
}
$checkItems[] = array(
'name' => $this->validateString($deliveryInfo['title'] ?? '', self::ITEM_NAME_MAX_LENGTH),
'price' => $this->formatPrice((float)($deliveryInfo['value'] ?? 0)),
'quantity' => 1,
'vat' => $this->getVat($this->vatRate),
'measure' => self::DEFAULT_MEASURE,
'paymentMethod' => self::DEFAULT_PAYMENT_METHOD,
'paymentObject' => self::SHIPPING_PAYMENT_OBJECT,
);
return $checkItems;
}
private function getPayInventoryJson(array $products, ?array $deliveryInfo): false|string
{
if (empty($products) && empty($deliveryInfo)) {
return false;
}
$inventory = [];
foreach ($products as $product) {
$inventory[] = [
'name' => $this->validateString($product['name'] ?? '', self::INVENTORY_ITEM_NAME_MAX_LENGTH),
'name' => $this->validateString($product['name'] ?? '', self::ITEM_NAME_MAX_LENGTH),
'price' => $this->formatPrice((float)($product['price'] ?? 0)),
'quantity' => $this->formatQuantity((float)($product['quantity'] ?? 0)),
'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,
];
}
if (null !== $deliveryInfo) {
$inventory[] = [
'name' => $this->validateString($deliveryInfo['title'] ?? '', self::ITEM_NAME_MAX_LENGTH),
'price' => $this->formatPrice((float)($deliveryInfo['value'] ?? 0)),
'quantity' => '1',
'vatTag' => $this->vatRate,
'pm' => self::DEFAULT_PAYMENT_METHOD,
'po' => self::SHIPPING_PAYMENT_OBJECT,
'measure' => self::DEFAULT_MEASURE,
];
}
@@ -286,11 +379,39 @@ class Callback extends \Opencart\System\Engine\Controller
return rtrim(rtrim($quantityString, '0'), '.');
}
/**
* @param array<non-empty-string, mixed> $data
* @return array
*/
public function buildJsonResponse(array $data): array
{
$jsonData = [
'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' => [
'client' => $data['client'],
'items' => $data['items'],
],
];
$json = json_encode($jsonData, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
return [
'data' => $json ?: '',
'contentType' => self::JSON_CONTENT_TYPE,
];
}
/**
* @param array<string, mixed> $data
* @throws \Exception
*/
private function buildXMLResponse(array $data): false|string
private function buildXmlResponse(array $data): array
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->formatOutput = false;
@@ -334,7 +455,39 @@ class Callback extends \Opencart\System\Engine\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,
);
}
public function getVat($vatRate): string
{
return match ($vatRate) {
'1104' => 'vat0',
'1108' => 'vat5',
'1109' => 'vat7',
'1103' => 'vat10',
'7000' => 'vat20',
'1113' => 'vat22',
'1110' => 'vat105',
'1111' => 'vat107',
'1107' => 'vat110',
'7001' => 'vat120',
'1114' => 'vat122',
default => 'none'
};
}
/**
* @return array
* @throws \Exception
*/
public function getAccountOrderProducts(): array
{
$this->load->model('account/order');
return $this->model_account_order->getProducts($this->orderId);
}
/** @param array<string, mixed> $params */