v1.0.0
This commit is contained in:
Executable
+380
@@ -0,0 +1,380 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Opencart\Catalog\Controller\Extension\PayAnyWay\Payment;
|
||||
|
||||
require_once DIR_EXTENSION . '/payanyway/catalog/library/payanyway/traits/payanyway.php';
|
||||
|
||||
/**
|
||||
* @property \Opencart\System\Engine\Config $config
|
||||
*/
|
||||
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 CANCEL_ORDER_STATUS_IDS = [7, 9, 16];
|
||||
|
||||
private int $mntId;
|
||||
private string $integrityCode;
|
||||
private string $server;
|
||||
private string $vatRate;
|
||||
private string $currency;
|
||||
private int $pendingOrderStatusId;
|
||||
private int $successOrderStatusId;
|
||||
private int $errorOrderStatusId;
|
||||
private int $geoZoneId;
|
||||
private int $sortOrder;
|
||||
private string $cmsModuleVersion;
|
||||
|
||||
private array $callbackData = [];
|
||||
private ?int $orderId;
|
||||
private array $orderInfo = [];
|
||||
|
||||
public function __construct(\Opencart\System\Engine\Registry $registry)
|
||||
{
|
||||
parent::__construct($registry);
|
||||
|
||||
if (!$this->validateOrder()) {
|
||||
$this->sendResponse('FAIL');
|
||||
}
|
||||
|
||||
$this->initializeModule();
|
||||
$this->initializeCallbackData();
|
||||
}
|
||||
|
||||
/** @throws \Exception */
|
||||
public function index(): void
|
||||
{
|
||||
($this->callbackData['MNT_COMMAND'] === 'CHECK')
|
||||
? $this->handleCheckCallback()
|
||||
: $this->handlePayCallback();
|
||||
}
|
||||
|
||||
private function initializeCallbackData(): void
|
||||
{
|
||||
$requestData = ($_SERVER['REQUEST_METHOD'] === 'POST') ? $_POST : $_GET;
|
||||
|
||||
$this->callbackData = [
|
||||
'MNT_COMMAND' => $this->getOptionalStringWithDefault($requestData, 'MNT_COMMAND'),
|
||||
'MNT_ID' => $this->getRequiredString($requestData, 'MNT_ID'),
|
||||
'MNT_TRANSACTION_ID' => $this->getRequiredString($requestData, 'MNT_TRANSACTION_ID'),
|
||||
'MNT_OPERATION_ID' => $this->getOptionalStringWithDefault($requestData, 'MNT_OPERATION_ID'),
|
||||
'MNT_AMOUNT' => $this->getOptionalStringWithDefault($requestData, 'MNT_AMOUNT'),
|
||||
'MNT_CURRENCY_CODE' => $this->getRequiredString($requestData, 'MNT_CURRENCY_CODE'),
|
||||
'MNT_SUBSCRIBER_ID' => $this->getOptionalStringWithDefault($requestData, 'MNT_SUBSCRIBER_ID'),
|
||||
'MNT_TEST_MODE' => $this->getRequiredString($requestData, 'MNT_TEST_MODE'),
|
||||
'MNT_SIGNATURE' => $this->getRequiredString($requestData, 'MNT_SIGNATURE'),
|
||||
];
|
||||
}
|
||||
|
||||
private function sendResponse(string $response, int $statusCode = 200): void
|
||||
{
|
||||
http_response_code($statusCode);
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
/** @throws \Exception */
|
||||
private function handleCheckCallback(): void
|
||||
{
|
||||
if (!$this->checkSignature($this->callbackData)) {
|
||||
$this->sendResponse('FAIL');
|
||||
}
|
||||
|
||||
$this->load->model('account/order');
|
||||
$products = $this->model_account_order->getProducts($this->orderId);
|
||||
$deliveryPrice = $this->getDeliveryPrice();
|
||||
|
||||
$xmlData = [
|
||||
'MNT_ID' => $this->mntId,
|
||||
'MNT_TRANSACTION_ID' => $this->callbackData['MNT_TRANSACTION_ID'],
|
||||
'MNT_AMOUNT' => $this->formatPrice((float)($this->orderInfo['total'] ?? 0)),
|
||||
'MNT_CURRENCY_CODE' => $this->callbackData['MNT_CURRENCY_CODE'],
|
||||
'inventory' => $this->getInventoryJson($products) ?: null,
|
||||
'client' => $this->callbackData['MNT_SUBSCRIBER_ID'],
|
||||
'sno' => null,
|
||||
'delivery' => (null !== $deliveryPrice) ? $this->formatPrice($deliveryPrice) : null,
|
||||
];
|
||||
|
||||
[$xmlData['MNT_RESULT_CODE'], $xmlData['MNT_DESCRIPTION']] = $this->determineCheckResult();
|
||||
|
||||
$xmlData['MNT_SIGNATURE'] = md5(
|
||||
$xmlData['MNT_RESULT_CODE'] .
|
||||
$xmlData['MNT_ID'] .
|
||||
$xmlData['MNT_TRANSACTION_ID'] .
|
||||
$this->integrityCode,
|
||||
);
|
||||
|
||||
$this->sendResponse($this->buildXMLResponse($xmlData));
|
||||
}
|
||||
|
||||
/** @throws \Exception */
|
||||
private function determineCheckResult(): array
|
||||
{
|
||||
$orderStatusId = isset($this->orderInfo['order_status_id']) ? (int)$this->orderInfo['order_status_id'] : null;
|
||||
if (null === $orderStatusId) {
|
||||
return [500, "Order status not set"];
|
||||
}
|
||||
$orderStatus = $this->getOrderStatusById($orderStatusId);
|
||||
|
||||
if (empty($this->callbackData['MNT_AMOUNT'])) {
|
||||
return [100, "Order status is '{$orderStatus}'"];
|
||||
}
|
||||
|
||||
if ($this->successOrderStatusId === $orderStatusId) {
|
||||
return [200, 'Order Paid'];
|
||||
}
|
||||
|
||||
if (in_array($orderStatusId, self::CANCEL_ORDER_STATUS_IDS, true)) {
|
||||
return [500, "Order status is '{$orderStatus}'"];
|
||||
}
|
||||
|
||||
return [402, 'Order created, but not paid'];
|
||||
}
|
||||
|
||||
/** @throws \Exception */
|
||||
private function handlePayCallback(): void
|
||||
{
|
||||
$shopData = [
|
||||
'MNT_ID' => $this->mntId,
|
||||
'MNT_TRANSACTION_ID' => $this->callbackData['MNT_TRANSACTION_ID'],
|
||||
'MNT_OPERATION_ID' => $this->callbackData['MNT_OPERATION_ID'],
|
||||
'MNT_AMOUNT' => $this->formatPrice((float)$this->orderInfo['total']),
|
||||
'MNT_CURRENCY_CODE' => $this->orderInfo['currency_code'],
|
||||
'MNT_SUBSCRIBER_ID' => $this->callbackData['MNT_SUBSCRIBER_ID'],
|
||||
'MNT_TEST_MODE' => $this->callbackData['MNT_TEST_MODE'],
|
||||
];
|
||||
|
||||
if (!$this->checkSignature($shopData)) {
|
||||
$this->sendResponse('FAIL');
|
||||
}
|
||||
|
||||
$isPayedOrder = $this->isOrderPaid();
|
||||
$resultCode = 200;
|
||||
|
||||
$this->load->model('account/order');
|
||||
$products = $this->model_account_order->getProducts($this->orderId);
|
||||
$deliveryPrice = $this->getDeliveryPrice();
|
||||
|
||||
$xmlData = [
|
||||
'MNT_ID' => $this->mntId,
|
||||
'MNT_TRANSACTION_ID' => $this->callbackData['MNT_TRANSACTION_ID'],
|
||||
'MNT_RESULT_CODE' => $resultCode,
|
||||
'MNT_SIGNATURE' => md5($resultCode . $this->mntId . $this->callbackData['MNT_TRANSACTION_ID'] . $this->integrityCode),
|
||||
'MNT_AMOUNT' => $this->callbackData['MNT_AMOUNT'],
|
||||
'MNT_CURRENCY_CODE' => $this->callbackData['MNT_CURRENCY_CODE'],
|
||||
'MNT_DESCRIPTION' => $isPayedOrder ? 'Order already paid' : 'Order success paid',
|
||||
'inventory' => $this->getInventoryJson($products),
|
||||
'client' => $this->callbackData['MNT_SUBSCRIBER_ID'],
|
||||
'sno' => null,
|
||||
'delivery' => $deliveryPrice !== null ? $this->formatPrice($deliveryPrice) : null,
|
||||
];
|
||||
|
||||
if (!$isPayedOrder) {
|
||||
$this->load->language('extension/payanyway/payment/payanyway');
|
||||
$this->addOrderHistory($this->orderId, $this->successOrderStatusId, $this->language->get('text_payment_completed'),);
|
||||
}
|
||||
|
||||
$this->sendResponse($this->buildXMLResponse($xmlData));
|
||||
}
|
||||
|
||||
private function isOrderPaid(): bool
|
||||
{
|
||||
return $this->successOrderStatusId === (int)$this->orderInfo['order_status_id'];
|
||||
}
|
||||
|
||||
/** @param array<string, mixed> $data */
|
||||
private function checkSignature(array $data): bool
|
||||
{
|
||||
$baseFields = [
|
||||
'MNT_ID',
|
||||
'MNT_TRANSACTION_ID',
|
||||
'MNT_OPERATION_ID',
|
||||
'MNT_AMOUNT',
|
||||
'MNT_CURRENCY_CODE',
|
||||
'MNT_SUBSCRIBER_ID',
|
||||
'MNT_TEST_MODE',
|
||||
];
|
||||
|
||||
$fields = isset($data['MNT_COMMAND']) && ('CHECK' === $data['MNT_COMMAND'])
|
||||
? array_merge(['MNT_COMMAND'], $baseFields)
|
||||
: $baseFields;
|
||||
|
||||
$signatureString = array_reduce($fields, static function ($carry, $field) use ($data) {
|
||||
return $carry . $data[$field];
|
||||
}, '') . $this->integrityCode;
|
||||
|
||||
return hash_equals(md5($signatureString), $this->callbackData['MNT_SIGNATURE']);
|
||||
}
|
||||
|
||||
private function getDeliveryPrice(): ?float
|
||||
{
|
||||
$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 null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $products
|
||||
* @return false|string
|
||||
*/
|
||||
private function getInventoryJson(array $products): false|string
|
||||
{
|
||||
$inventory = [];
|
||||
foreach ($products as $product) {
|
||||
$inventory[] = [
|
||||
'name' => $this->validateString($product['name'] ?? '', self::INVENTORY_ITEM_NAME_MAX_LENGTH),
|
||||
'price' => $this->formatPrice((float)($product['price'] ?? 0)),
|
||||
'quantity' => $this->formatQuantity((string)($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,
|
||||
];
|
||||
}
|
||||
|
||||
return json_encode($inventory, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $orderStatusId
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getOrderStatusById(int $orderStatusId): string
|
||||
{
|
||||
$this->load->model('localisation/order_status');
|
||||
/**@var array<int, array{order_status_id: string, name: string}> $orderStatuses */
|
||||
$orderStatuses = $this->model_localisation_order_status->getOrderStatuses();
|
||||
|
||||
$orderStatus = 'unknown';
|
||||
foreach ($orderStatuses as $orderStatus) {
|
||||
$statusId = isset($orderStatus['order_status_id']) ? (int)$orderStatus['order_status_id'] : null;
|
||||
if ($statusId === $orderStatusId) {
|
||||
$orderStatus = $orderStatus['name'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $orderStatus;
|
||||
}
|
||||
|
||||
private function formatQuantity(string $quantity): string
|
||||
{
|
||||
$quantity = str_replace(',', '.', $quantity);
|
||||
|
||||
if (!str_contains($quantity, '.') && ctype_digit($quantity)) {
|
||||
return (string)(int)$quantity;
|
||||
}
|
||||
|
||||
return number_format((float)$quantity, 3, '.', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $data
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function buildXMLResponse(array $data): false|string
|
||||
{
|
||||
$dom = new \DOMDocument('1.0', 'UTF-8');
|
||||
$dom->formatOutput = false;
|
||||
|
||||
$root = $dom->createElement('MNT_RESPONSE');
|
||||
$dom->appendChild($root);
|
||||
|
||||
$requiredFields = [
|
||||
'MNT_ID',
|
||||
'MNT_TRANSACTION_ID',
|
||||
'MNT_RESULT_CODE',
|
||||
'MNT_DESCRIPTION',
|
||||
'MNT_AMOUNT',
|
||||
'MNT_CURRENCY_CODE',
|
||||
'MNT_SIGNATURE',
|
||||
];
|
||||
|
||||
foreach ($requiredFields as $field) {
|
||||
if (isset($data[$field]) && ($data[$field] !== '')) {
|
||||
$root->appendChild($dom->createElement($field, (string)$data[$field]));
|
||||
}
|
||||
}
|
||||
|
||||
$attributes = $dom->createElement('MNT_ATTRIBUTES');
|
||||
$root->appendChild($attributes);
|
||||
|
||||
$attributeMap = [
|
||||
'INVENTORY' => $data['inventory'] ?? null,
|
||||
'CLIENT' => $data['client'] ?? null,
|
||||
'SNO' => $data['sno'] ?? null,
|
||||
'DELIVERY' => $data['delivery'] ?? null,
|
||||
];
|
||||
|
||||
foreach ($attributeMap as $key => $value) {
|
||||
if (!empty($value)) {
|
||||
$attrElement = $dom->createElement('ATTRIBUTE');
|
||||
$attrElement->appendChild($dom->createElement('KEY', $key));
|
||||
$attrElement->appendChild($dom->createElement('VALUE', (string)$value));
|
||||
$attributes->appendChild($attrElement);
|
||||
}
|
||||
}
|
||||
|
||||
return $dom->saveXML();
|
||||
}
|
||||
|
||||
/** @param array<string, mixed> $params */
|
||||
private function getOptionalStringWithDefault(array $params, string $key, string $default = ''): string
|
||||
{
|
||||
return $this->getOptionalString($params, $key) ?? $default;
|
||||
}
|
||||
|
||||
/** @param array<string, mixed> $params */
|
||||
private function getOptionalString(array $params, string $key): ?string
|
||||
{
|
||||
$value = $params[$key] ?? null;
|
||||
if (!is_string($value) || trim($value) === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $params
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
private function getRequiredString(array $params, string $key): string
|
||||
{
|
||||
if (!isset($params[$key])) {
|
||||
throw new \InvalidArgumentException('Missing required field: ' . $key);
|
||||
}
|
||||
|
||||
$value = $params[$key];
|
||||
if (!is_string($value)) {
|
||||
throw new \InvalidArgumentException(
|
||||
sprintf('Field "%s" must be a string, %s given', $key, gettype($value)),
|
||||
);
|
||||
}
|
||||
|
||||
if ('' === $value) {
|
||||
throw new \InvalidArgumentException(sprintf('Field "%s" must be a non-empty string', $key));
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
Executable
+141
@@ -0,0 +1,141 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Opencart\Catalog\Controller\Extension\PayAnyWay\Payment;
|
||||
|
||||
require_once DIR_EXTENSION . '/payanyway/catalog/library/payanyway/traits/payanyway.php';
|
||||
|
||||
class PayAnyWay extends \Opencart\System\Engine\Controller
|
||||
{
|
||||
use \Opencart\Catalog\Library\Extension\PayAnyWay\Traits\PayAnyWay;
|
||||
|
||||
private const PAYMENT_SERVER_PROD = 'prod';
|
||||
private const PAYMENT_URL_PROD = 'https://www.payanyway.ru/assistant.htm';
|
||||
private const PAYMENT_URL_DEMO = 'https://demo.moneta.ru/assistant.htm';
|
||||
private const DESCRIPTION_MAX_LENGTH = 500;
|
||||
|
||||
private int $mntId;
|
||||
private string $integrityCode;
|
||||
private string $server;
|
||||
private string $vatRate;
|
||||
private string $currency;
|
||||
private int $pendingOrderStatusId;
|
||||
private int $successOrderStatusId;
|
||||
private int $errorOrderStatusId;
|
||||
private int $geoZoneId;
|
||||
private int $sortOrder;
|
||||
private string $cmsModuleVersion;
|
||||
|
||||
private array $orderInfo;
|
||||
private ?int $orderId;
|
||||
|
||||
public function __construct(\Opencart\System\Engine\Registry $registry)
|
||||
{
|
||||
parent::__construct($registry);
|
||||
|
||||
if (!$this->validateOrder()) {
|
||||
$this->response->redirect($this->url->link('account/login', '', true));
|
||||
}
|
||||
|
||||
$this->initializeModule();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function index(): string
|
||||
{
|
||||
$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_TRANSACTION_ID'] = $this->createTransactionId($this->orderId);
|
||||
$data['MNT_AMOUNT'] = $this->formatPrice((float)($this->orderInfo['total'] ?? 0));
|
||||
$data['MNT_CURRENCY_CODE'] = $this->currency;
|
||||
$data['MNT_TEST_MODE'] = '0';
|
||||
$data['MNT_DESCRIPTION'] = $this->getDescription();
|
||||
$data['MNT_SUBSCRIBER_ID'] = $this->getSubscriberId();
|
||||
|
||||
$signature = 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,
|
||||
);
|
||||
$data['MNT_SIGNATURE'] = $signature;
|
||||
$data['MNT_SUCCESS_URL'] = $this->url->link('extension/payanyway/payment/payanyway.success', 'language=' . $this->config->get('config_language'), true,);
|
||||
$data['MNT_FAIL_URL'] = $this->url->link('extension/payanyway/payment/payanyway.fail', 'language=' . $this->config->get('config_language'), true,);
|
||||
$data['MNT_RETURN_URL'] = $this->url->link('checkout/checkout', 'language=' . $this->config->get('config_language'), true,);
|
||||
$data['MNT_CMS'] = $this->cmsModuleVersion;
|
||||
|
||||
$data['btn_confirm'] = $this->language->get('text_paw_pay');
|
||||
|
||||
$data['confirm_url'] = $this->url->link('extension/payanyway/payment/payanyway.confirm', 'language=' . $this->config->get('config_language'), true,);
|
||||
|
||||
return $this->load->view('extension/payanyway/payment/payanyway', $data);
|
||||
}
|
||||
|
||||
public function confirm(): void
|
||||
{
|
||||
$this->load->language('extension/payanyway/payment/payanyway');
|
||||
$this->addOrderHistory($this->orderId, $this->pendingOrderStatusId, $this->language->get('text_order_confirmed'),);
|
||||
|
||||
$json['success'] = true;
|
||||
|
||||
$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->addOrderHistory($this->orderId, $this->pendingOrderStatusId, $this->language->get('text_payment_processing'),);
|
||||
$this->response->redirect($this->url->link('checkout/success', '', true));
|
||||
}
|
||||
|
||||
public function fail(): void
|
||||
{
|
||||
$this->load->language('extension/payanyway/payment/payanyway');
|
||||
$this->addOrderHistory($this->orderId, $this->errorOrderStatusId, $this->language->get('text_payment_error'),);
|
||||
$this->response->redirect($this->url->link('checkout/failure', '', true));
|
||||
}
|
||||
|
||||
private function getDescription(): string
|
||||
{
|
||||
$clientName = trim(($this->orderInfo['firstname'] ?? '') . ' ' . ($this->orderInfo['lastname'] ?? ''));
|
||||
|
||||
$orderId = $this->orderInfo['order_id'] ?? null;
|
||||
$description = "Оплата заказа" . (!empty($orderId) ? " №{$orderId}" : '');
|
||||
|
||||
if ('' !== $clientName) {
|
||||
$description .= " от {$clientName}";
|
||||
}
|
||||
|
||||
$comment = $this->orderInfo['comment'] ?? '';
|
||||
if ($comment !== '') {
|
||||
$description .= ': ' . htmlspecialchars($comment, ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
|
||||
return $this->validateString($description, self::DESCRIPTION_MAX_LENGTH);
|
||||
}
|
||||
|
||||
private function getSubscriberId(): string
|
||||
{
|
||||
$email = filter_var($this->orderInfo['email'] ?? '', FILTER_SANITIZE_EMAIL);
|
||||
if ('' !== $email) {
|
||||
return $email;
|
||||
}
|
||||
|
||||
$phoneNumber = preg_replace('/[^0-9+]/', '', $this->orderInfo['telephone'] ?? '');
|
||||
|
||||
return $phoneNumber ?: '';
|
||||
}
|
||||
}
|
||||
Executable
+14
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
// Text
|
||||
$_['text_title'] = 'PayAnyWay (MIR, VISA, MasterCard and 30+ other ways)';
|
||||
$_['text_ap_pay_with'] = 'Pay with';
|
||||
$_['text_ap_and_other'] = 'or another payment method';
|
||||
$_['text_ap_or'] = 'or';
|
||||
$_['text_paw_pay'] = 'Pay';
|
||||
$_['text_paw_list_methods'] = 'List of payment methods';
|
||||
$_['text_paw_back_to_card'] = 'Back to payment by card';
|
||||
|
||||
$_['text_order_confirmed'] = 'Order confirmed';
|
||||
$_['text_payment_processing'] = 'Payment processing';
|
||||
$_['text_payment_completed'] = 'Order is paid';
|
||||
$_['text_payment_error'] = 'Payment error';
|
||||
Executable
+14
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
// Text
|
||||
$_['text_title'] = 'PayAnyWay (МИР, Visa, MasterCard и более 30-и способов оплаты)';
|
||||
$_['text_ap_pay_with'] = 'Оплатить с';
|
||||
$_['text_ap_and_other'] = 'или другой способ оплаты';
|
||||
$_['text_ap_or'] = 'или';
|
||||
$_['text_paw_pay'] = 'Оплатить';
|
||||
$_['text_paw_list_methods'] = 'Список способов оплаты';
|
||||
$_['text_paw_back_to_card'] = 'Вернуться к оплате картой';
|
||||
|
||||
$_['text_order_confirmed'] = 'Заказ подтвержден';
|
||||
$_['text_payment_processing'] = 'Обработка платежа';
|
||||
$_['text_payment_completed'] = 'Заказ оплачен';
|
||||
$_['text_payment_error'] = 'Ошибка оплаты';
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Opencart\Catalog\Library\Extension\PayAnyWay\Traits;
|
||||
|
||||
trait PayAnyWay
|
||||
{
|
||||
private function initializeModule(): void
|
||||
{
|
||||
$this->mntId = (int)$this->config->get('payment_payanyway_account');
|
||||
$this->integrityCode = (string)$this->config->get('payment_payanyway_integrity_code');
|
||||
$this->server = (string)$this->config->get('payment_payanyway_server');
|
||||
$this->vatRate = (string)$this->config->get('payment_payanyway_vat_rate');
|
||||
$this->currency = $this->config->get('payment_payanyway_currency');
|
||||
$this->pendingOrderStatusId = (int)$this->config->get('payment_payanyway_pending_order_status_id');
|
||||
$this->successOrderStatusId = (int)$this->config->get('payment_payanyway_success_order_status_id');
|
||||
$this->errorOrderStatusId = (int)$this->config->get('payment_payanyway_error_order_status_id');
|
||||
$this->cmsModuleVersion = (string)$this->config->get('payment_payanyway_cms_module_version');
|
||||
}
|
||||
|
||||
private function validateOrder(): bool
|
||||
{
|
||||
$this->orderId = $this->getOrderIdFromTransactionId() ?? $this->getOrderIdFromSession();
|
||||
if (null === $this->orderId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->load->model('checkout/order');
|
||||
$this->orderInfo = $this->model_checkout_order->getOrder($this->orderId);
|
||||
|
||||
return ([] !== $this->orderInfo);
|
||||
}
|
||||
|
||||
private string $transactionIdDelimiter = '|';
|
||||
|
||||
private function validateString(string $value, int $maxLength): string
|
||||
{
|
||||
$maxLength = max(0, $maxLength);
|
||||
$value = $this->sanitizeString($value);
|
||||
|
||||
if (mb_strlen($value, 'UTF-8') <= $maxLength) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$trimLength = max(0, $maxLength - 3);
|
||||
return mb_substr($value, 0, $trimLength, 'UTF-8') . ($maxLength > 3 ? '...' : '');
|
||||
}
|
||||
|
||||
private function sanitizeString(string $value): string
|
||||
{
|
||||
$decoded = html_entity_decode($value, ENT_QUOTES | ENT_HTML5, 'UTF-8');
|
||||
$cleaned = preg_replace('/[^\p{L}\p{N}\s.,()_№+-]/u', '', $decoded);
|
||||
$cleaned = str_replace(['&', '/', '\\', ';', '%', '#', '"', "'"], '', $cleaned);
|
||||
|
||||
return trim(preg_replace('/\s+/', ' ', $cleaned));
|
||||
}
|
||||
|
||||
private function formatPrice(float $price): string
|
||||
{
|
||||
return number_format($price, 2, '.', '');
|
||||
}
|
||||
|
||||
private function getOrderIdFromTransactionId(): ?int
|
||||
{
|
||||
if (!isset($_REQUEST['MNT_TRANSACTION_ID'])) {
|
||||
return null;
|
||||
}
|
||||
$transactionIdData = explode($this->transactionIdDelimiter, $_REQUEST['MNT_TRANSACTION_ID']);
|
||||
|
||||
return isset($transactionIdData[0]) ? (int)$transactionIdData[0] : null;
|
||||
}
|
||||
|
||||
private function getOrderIdFromSession(): ?int
|
||||
{
|
||||
return isset($this->session->data['order_id']) ? (int)$this->session->data['order_id'] : null;
|
||||
}
|
||||
|
||||
private function createTransactionId(int $orderId): string
|
||||
{
|
||||
$date = (new \DateTimeImmutable())->format('YmdHis');
|
||||
return $orderId . $this->transactionIdDelimiter . $date;
|
||||
}
|
||||
|
||||
private function addOrderHistory(int $orderId, int $orderStatusId, string $comment): void
|
||||
{
|
||||
$this->load->model('checkout/order');
|
||||
$this->model_checkout_order->addHistory($orderId, $orderStatusId, $comment);
|
||||
}
|
||||
}
|
||||
Executable
+69
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Opencart\Catalog\Model\Extension\PayAnyWay\Payment;
|
||||
|
||||
class PayAnyWay extends \Opencart\System\Engine\Model
|
||||
{
|
||||
/**
|
||||
* @param array<string, mixed> $address
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function getMethods(array $address = []): array
|
||||
{
|
||||
$this->load->language('extension/payanyway/payment/payanyway');
|
||||
|
||||
if (!$this->isModuleEnabled() ||
|
||||
!$this->isCurrencySupported() ||
|
||||
!$this->isGeoZoneValid($address)
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
'code' => 'payanyway',
|
||||
'name' => $this->language->get('text_title'),
|
||||
'option' => [
|
||||
'payanyway' => [
|
||||
'code' => 'payanyway.payanyway',
|
||||
'name' => $this->language->get('text_title')
|
||||
]
|
||||
],
|
||||
'sort_order' => $this->config->get('payment_payanyway_sort_order'),
|
||||
];
|
||||
}
|
||||
|
||||
private function isModuleEnabled(): bool
|
||||
{
|
||||
return (bool)$this->config->get('payment_payanyway_status');
|
||||
}
|
||||
|
||||
private function isCurrencySupported(): bool
|
||||
{
|
||||
$shopCurrency = $this->session->data['currency'] ?? '';
|
||||
|
||||
return $shopCurrency === $this->config->get('payment_payanyway_currency');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $address
|
||||
*/
|
||||
private function isGeoZoneValid(array $address): bool
|
||||
{
|
||||
$geoZoneId = (int)$this->config->get('payment_payanyway_geo_zone_id');
|
||||
|
||||
if ($geoZoneId === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$sql = "SELECT * FROM " . DB_PREFIX . "zone_to_geo_zone
|
||||
WHERE geo_zone_id = '" . (int)$geoZoneId . "'
|
||||
AND country_id = '" . (int)($address['country_id'] ?? 0) . "'
|
||||
AND (zone_id = '" . (int)($address['zone_id'] ?? 0) . "' OR zone_id = '0')";
|
||||
|
||||
$query = $this->db->query($sql);
|
||||
|
||||
return $query->num_rows > 0;
|
||||
}
|
||||
}
|
||||
Executable
+44
@@ -0,0 +1,44 @@
|
||||
<form id="payment-form" action="{{ action }}" method="post">
|
||||
<input type="hidden" name="MNT_ID" value="{{ MNT_ID }}"/>
|
||||
<input type="hidden" name="MNT_TRANSACTION_ID" value="{{ MNT_TRANSACTION_ID }}"/>
|
||||
<input type="hidden" name="MNT_AMOUNT" value="{{ MNT_AMOUNT }}"/>
|
||||
<input type="hidden" name="MNT_CURRENCY_CODE" value="{{ MNT_CURRENCY_CODE }}"/>
|
||||
<input type="hidden" name="MNT_TEST_MODE" value="{{ MNT_TEST_MODE }}"/>
|
||||
<input type="hidden" name="MNT_DESCRIPTION" value="{{ MNT_DESCRIPTION }}"/>
|
||||
<input type="hidden" name="MNT_SUBSCRIBER_ID" value="{{ MNT_SUBSCRIBER_ID }}"/>
|
||||
<input type="hidden" name="MNT_SIGNATURE" value="{{ MNT_SIGNATURE }}"/>
|
||||
<input type="hidden" name="MNT_SUCCESS_URL" value="{{ MNT_SUCCESS_URL }}"/>
|
||||
<input type="hidden" name="MNT_FAIL_URL" value="{{ MNT_FAIL_URL }}"/>
|
||||
<input type="hidden" name="MNT_RETURN_URL" value="{{ MNT_RETURN_URL }}"/>
|
||||
<input type="hidden" name="MNT_CMS" value="{{ MNT_CMS }}"/>
|
||||
<div class="buttons">
|
||||
<div class="{% if ap_use %} text-center {% else %} pull-right {% endif %}">
|
||||
<input type="submit" value="{{ btn_confirm }}" class="btn btn-primary"/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
$('#payment-form').on('submit', function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
$.ajax({
|
||||
url: '{{ confirm_url }}',
|
||||
type: 'POST',
|
||||
data: $(this).serialize(),
|
||||
dataType: 'json',
|
||||
success: function (response) {
|
||||
if (response.success) {
|
||||
$('#payment-form').off('submit').submit();
|
||||
} else {
|
||||
window.location.href = '{{ MNT_FAIL_URL }}';
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
window.location.href = '{{ MNT_FAIL_URL }}';
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
Reference in New Issue
Block a user