v1.0.0
This commit is contained in:
+333
@@ -0,0 +1,333 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @property ModelSettingSetting $model_setting_setting
|
||||
* @property ModelLocalisationCurrency $model_localisation_currency
|
||||
* @property ModelLocalisationOrderStatus $model_localisation_order_status
|
||||
* @property ModelLocalisationGeoZone $model_localisation_geo_zone
|
||||
*/
|
||||
class ControllerExtensionPaymentPayanyway extends Controller {
|
||||
const CMS_NAME = 'OpenCart';
|
||||
const MODULE_NAME = 'payanyway';
|
||||
const MODULE_VERSION = '1.0.0';
|
||||
const MODULE_DEFAULT_SORT_ORDER = 0;
|
||||
|
||||
private $supportedCurrencies = array('RUB');
|
||||
private $availableVatRates = array(
|
||||
'vat_none' => '1105',
|
||||
'vat0' => '1104',
|
||||
'vat5' => '1108',
|
||||
'vat7' => '1109',
|
||||
'vat10' => '1103',
|
||||
'vat22' => '1113',
|
||||
'vat105' => '1110',
|
||||
'vat107' => '1111',
|
||||
'vat110' => '1107',
|
||||
'vat122' => '1114'
|
||||
);
|
||||
|
||||
private $error = array();
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function index() {
|
||||
$this->load->language('extension/payment/payanyway');
|
||||
$this->document->setTitle($this->language->get('heading_title'));
|
||||
|
||||
if (($this->request->server['REQUEST_METHOD'] === 'POST') && $this->validate($this->request)) {
|
||||
$this->load->model('setting/setting');
|
||||
$this->model_setting_setting->editSetting('payment_payanyway', $this->request->post);
|
||||
|
||||
$this->session->data['success'] = $this->language->get('text_success');
|
||||
|
||||
$this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=payment', true));
|
||||
}
|
||||
|
||||
$data = array();
|
||||
$this->applyValidationErrors($data);
|
||||
|
||||
$data['breadcrumbs'] = $this->getBreadcrumbs();
|
||||
$data['action'] = $this->url->link('extension/payment/payanyway', 'user_token=' . $this->session->data['user_token'], true);
|
||||
$data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=payment', true);
|
||||
|
||||
$data['payment_payanyway_status'] = $this->retrieveValue('payment_payanyway_status');
|
||||
$data['payment_payanyway_account'] = $this->retrieveValue('payment_payanyway_account');
|
||||
$data['payment_payanyway_integrity_code'] = $this->getIntegrityCode('payment_payanyway_integrity_code');
|
||||
$data['payment_payanyway_server'] = $this->retrieveValue('payment_payanyway_server');
|
||||
$data['payment_payanyway_vat_rates'] = $this->getModuleVatRates();
|
||||
$data['payment_payanyway_vat_rate'] = $this->retrieveValue('payment_payanyway_vat_rate');
|
||||
$data['payment_payanyway_currencies'] = $this->getModuleCurrencies();
|
||||
$data['payment_payanyway_currency'] = $this->retrieveValue('payment_payanyway_currency');
|
||||
$data['order_statuses'] = $this->getAvailableOrderStatuses();
|
||||
$data['payment_payanyway_pending_order_status_id'] = $this->retrieveValue('payment_payanyway_pending_order_status_id');
|
||||
$data['payment_payanyway_success_order_status_id'] = $this->retrieveValue('payment_payanyway_success_order_status_id');
|
||||
$data['payment_payanyway_error_order_status_id'] = $this->retrieveValue('payment_payanyway_error_order_status_id');
|
||||
$data['geo_zones'] = $this->getAvailableGeoZones();
|
||||
$data['payment_payanyway_geo_zone_id'] = $this->retrieveValue('payment_payanyway_geo_zone_id');
|
||||
$data['payment_payanyway_sort_order'] = $this->retrieveValue('payment_payanyway_sort_order');
|
||||
$data['payment_payanyway_cms_module_version'] = $this->getCmsModuleVersion();
|
||||
|
||||
$data['header'] = $this->load->controller('common/header');
|
||||
$data['column_left'] = $this->load->controller('common/column_left');
|
||||
$data['footer'] = $this->load->controller('common/footer');
|
||||
|
||||
$this->response->setOutput($this->load->view('extension/payment/payanyway', $data));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function install() {
|
||||
$defaultSettings = array(
|
||||
'payment_payanyway_status' => 0,
|
||||
'payment_payanyway_account' => '',
|
||||
'payment_payanyway_integrity_code' => '',
|
||||
'payment_payanyway_server' => 'prod',
|
||||
'payment_payanyway_sort_order' => 0,
|
||||
'payment_payanyway_cms_module_version' => $this->getCmsModuleVersion(),
|
||||
);
|
||||
|
||||
$orderStatuses = $this->getAvailableOrderStatuses();
|
||||
|
||||
$pendingStatus = array_filter($orderStatuses, function ($status) {
|
||||
return (int)$status['order_status_id'] === 1;
|
||||
});
|
||||
if (!empty($pendingStatus)) {
|
||||
$pendingKey = key($pendingStatus);
|
||||
$defaultSettings['payment_payanyway_pending_order_status_id'] = $pendingStatus[$pendingKey]['order_status_id'];
|
||||
}
|
||||
|
||||
$successStatus = array_filter($orderStatuses, function ($status) {
|
||||
return (int)$status['order_status_id'] === 5;
|
||||
});
|
||||
if (!empty($successStatus)) {
|
||||
$successKey = key($successStatus);
|
||||
$defaultSettings['payment_payanyway_success_order_status_id'] = $successStatus[$successKey]['order_status_id'];
|
||||
}
|
||||
|
||||
$errorStatus = array_filter($orderStatuses, function ($status) {
|
||||
return (int)$status['order_status_id'] === 10;
|
||||
});
|
||||
if (!empty($errorStatus)) {
|
||||
$errorKey = key($errorStatus);
|
||||
$defaultSettings['payment_payanyway_error_order_status_id'] = $errorStatus[$errorKey]['order_status_id'];
|
||||
}
|
||||
|
||||
$this->load->model('setting/setting');
|
||||
$this->model_setting_setting->editSetting('payment_payanyway', $defaultSettings);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function uninstall() {
|
||||
$this->load->model('setting/setting');
|
||||
$this->model_setting_setting->deleteSetting('payment_payanyway');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Request $request
|
||||
* @return bool
|
||||
*/
|
||||
private function validate($request) {
|
||||
if (!$this->user->hasPermission('modify', 'extension/payment/payanyway')) {
|
||||
$this->error['warning'] = $this->language->get('error_permission');
|
||||
}
|
||||
|
||||
if (!isset($request->post['payment_payanyway_account']) ||
|
||||
((int)$request->post['payment_payanyway_account'] <= 0)
|
||||
) {
|
||||
$this->error['account'] = $this->language->get('error_account');
|
||||
}
|
||||
|
||||
if (empty($request->post['payment_payanyway_integrity_code'])) {
|
||||
$this->error['integrity_code'] = $this->language->get('error_integrity_code');
|
||||
}
|
||||
|
||||
if (empty($request->post['payment_payanyway_pending_order_status_id'])) {
|
||||
$this->error['pending_order_status'] = $this->language->get('error_order_status');
|
||||
}
|
||||
if (empty($request->post['payment_payanyway_success_order_status_id'])) {
|
||||
$this->error['success_order_status'] = $this->language->get('error_order_status');
|
||||
}
|
||||
if (empty($request->post['payment_payanyway_error_order_status_id'])) {
|
||||
$this->error['error_order_status'] = $this->language->get('error_order_status');
|
||||
}
|
||||
|
||||
$sortOrder = isset($request->post['payment_payanyway_sort_order'])
|
||||
? (int)$request->post['payment_payanyway_sort_order']
|
||||
: self::MODULE_DEFAULT_SORT_ORDER;
|
||||
$request->post['payment_payanyway_sort_order'] = $sortOrder;
|
||||
$request->post['payment_payanyway_cms_module_version'] = $this->getCmsModuleVersion();
|
||||
|
||||
return empty($this->error);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $data
|
||||
* @return void
|
||||
*/
|
||||
private function applyValidationErrors(array &$data) {
|
||||
$data['error_warning'] = isset($this->error['warning']) ? $this->error['warning'] : '';
|
||||
$data['error_account'] = isset($this->error['account']) ? $this->error['account'] : '';
|
||||
$data['error_integrity_code'] = isset($this->error['integrity_code']) ? $this->error['integrity_code'] : '';
|
||||
$data['error_pending_order_status'] = isset($this->error['pending_order_status']) ? $this->error['pending_order_status'] : '';
|
||||
$data['error_success_order_status'] = isset($this->error['success_order_status']) ? $this->error['success_order_status'] : '';
|
||||
$data['error_error_order_status'] = isset($this->error['error_order_status']) ? $this->error['error_order_status'] : '';
|
||||
}
|
||||
|
||||
/** @return array<int, array{text: string, href: string}> */
|
||||
private function getBreadcrumbs() {
|
||||
return array(
|
||||
array(
|
||||
'text' => $this->language->get('text_home'),
|
||||
'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true),
|
||||
),
|
||||
array(
|
||||
'text' => $this->language->get('text_extension'),
|
||||
'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=payment', true),
|
||||
),
|
||||
array(
|
||||
'text' => $this->language->get('heading_title'),
|
||||
'href' => $this->url->link('extension/payment/payanyway', 'user_token=' . $this->session->data['user_token'], true),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @return string
|
||||
*/
|
||||
private function getIntegrityCode($key) {
|
||||
$value = $this->retrieveValue($key);
|
||||
if (null === $value) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
|
||||
/** @return array<string, mixed> */
|
||||
private function getModuleVatRates() {
|
||||
$result = array();
|
||||
foreach ($this->availableVatRates as $vat => $value) {
|
||||
$label = 'entry_' . $vat . '_label';
|
||||
$result[$value] = $this->language->get($label);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function getModuleCurrencies() {
|
||||
$this->load->model('localisation/currency');
|
||||
$shopCurrencies = $this->model_localisation_currency->getCurrencies();
|
||||
|
||||
$result = array();
|
||||
foreach ($this->supportedCurrencies as $currencyCode) {
|
||||
$result[$currencyCode] = isset($shopCurrencies[$currencyCode])
|
||||
? $shopCurrencies[$currencyCode]
|
||||
: $this->getDefaultCurrencyDefinition($currencyCode);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $currencyCode
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function getDefaultCurrencyDefinition($currencyCode) {
|
||||
$definitions = array(
|
||||
'RUB' => array(
|
||||
'currency_id' => 0,
|
||||
'title' => 'Рубль',
|
||||
'code' => 'RUB',
|
||||
'symbol_left' => '',
|
||||
'symbol_right' => '₽',
|
||||
'decimal_place' => '2',
|
||||
'value' => 1.00000000,
|
||||
'status' => '1',
|
||||
'date_modified' => date('Y-m-d H:i:s'),
|
||||
),
|
||||
);
|
||||
|
||||
return isset($definitions[$currencyCode]) ? $definitions[$currencyCode] : array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function getAvailableOrderStatuses() {
|
||||
$this->load->model('localisation/order_status');
|
||||
return $this->model_localisation_order_status->getOrderStatuses();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function getAvailableGeoZones() {
|
||||
$this->load->model('localisation/geo_zone');
|
||||
return $this->model_localisation_geo_zone->getGeoZones();
|
||||
}
|
||||
|
||||
/** @return string */
|
||||
private function getCmsModuleVersion() {
|
||||
$this->load->language('common/footer');
|
||||
$textFooter = $this->language->get('text_footer');
|
||||
$textFooter = is_string($textFooter) ? $textFooter : '';
|
||||
|
||||
$cmsName = $this->detectCmsByPatterns($textFooter);
|
||||
$cmsVersion = defined('VERSION') ? constant('VERSION') : '.unknown';
|
||||
|
||||
return sprintf('%s v%s | %s v%s',
|
||||
$cmsName,
|
||||
$cmsVersion,
|
||||
self::MODULE_NAME,
|
||||
self::MODULE_VERSION
|
||||
);
|
||||
}
|
||||
|
||||
private function detectCmsByPatterns($textFooter) {
|
||||
$patterns = [
|
||||
'ocStore' => '/ocStore/i',
|
||||
'Russian OpenCart' => '/opencart-russia/i',
|
||||
self::CMS_NAME => '/opencart\.com/i'
|
||||
];
|
||||
|
||||
if (!empty($textFooter)) {
|
||||
foreach ($patterns as $cms => $pattern) {
|
||||
if (preg_match($pattern, $textFooter)) {
|
||||
return $cms;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return self::CMS_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @return mixed
|
||||
*/
|
||||
private function retrieveValue($name) {
|
||||
if (!is_string($name)) {
|
||||
throw new \InvalidArgumentException("The name must be a string");
|
||||
}
|
||||
|
||||
if (isset($this->request->post[$name])) {
|
||||
return $this->request->post[$name];
|
||||
}
|
||||
|
||||
return $this->config->get($name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
// Heading
|
||||
$_['heading_title'] = 'PayAnyWay';
|
||||
|
||||
// Text
|
||||
$_['text_extension'] = 'Extensions';
|
||||
$_['text_success'] = 'Settings for the PayAnyWay payment method have been saved successfully';
|
||||
$_['text_edit'] = 'Editing module settings';
|
||||
$_['text_payanyway'] = '<a href="https://www.payanyway.ru/" target="_blank"><img src="view/image/payment/payanyway.png" alt="PayAnyWay" title="PayAnyWay" width="163" height="18"></a>';
|
||||
|
||||
// Entry
|
||||
$_['entry_status_label'] = 'Enable payment module';
|
||||
|
||||
$_['entry_account_label'] = 'Account number';
|
||||
$_['entry_account_description'] = 'Your extended account number in the MONETA.RU system';
|
||||
$_['entry_integrity_code_label'] = 'Integrity Code';
|
||||
$_['entry_integrity_code_description'] = 'Data integrity verification code specified in the extended account settings in the MONETA.RU system';
|
||||
$_['entry_server_label'] = 'Payment server';
|
||||
$_['entry_server_prod_label'] = 'Production';
|
||||
$_['entry_server_demo_label'] = 'Demo';
|
||||
|
||||
$_['entry_vat_rate_label'] = 'VAT';
|
||||
$_['entry_vat_none_label'] = 'Without VAT';
|
||||
$_['entry_vat0_label'] = '0%';
|
||||
$_['entry_vat5_label'] = '5%';
|
||||
$_['entry_vat7_label'] = '7%';
|
||||
$_['entry_vat10_label'] = '10%';
|
||||
$_['entry_vat22_label'] = '22%';
|
||||
$_['entry_vat105_label'] = '5% (calculated rate 5/105)';
|
||||
$_['entry_vat107_label'] = '7% (calculated rate 7/107)';
|
||||
$_['entry_vat110_label'] = '10% (calculated rate 10/110)';
|
||||
$_['entry_vat122_label'] = '22% (calculated rate 22/122)';
|
||||
|
||||
$_['entry_currency_label'] = 'Payment acceptance';
|
||||
$_['entry_currency_rub_label'] = 'Russian Ruble';
|
||||
$_['entry_currency_description'] = 'The currencies in the MONETA.RU system and in the store must match';
|
||||
|
||||
$_['entry_select_order_status_label'] = ' --- Select status --- ';
|
||||
$_['entry_order_statuses_label'] = 'Order Statuses';
|
||||
$_['entry_pending_order_status_label'] = 'Pending';
|
||||
$_['entry_success_order_status_label'] = 'Success';
|
||||
$_['entry_error_order_status_label'] = 'Error';
|
||||
|
||||
$_['entry_geo_zone_label'] = 'Area of display';
|
||||
$_['entry_sort_order_label'] = 'Sort Order';
|
||||
|
||||
// Help
|
||||
$_['help_pending_order_status'] = 'Specify the order status that is set when the order is created';
|
||||
$_['help_success_order_status'] = 'Specify the order status that is set upon successful payment';
|
||||
$_['help_error_order_status'] = 'Specify the order status that is set upon payment error';
|
||||
$_['help_geo_zone'] = 'Geographic area where the payment method will be displayed';
|
||||
$_['help_sort_order'] = 'Determines the display order of the payment method. The smaller the number, the higher the position in the list';
|
||||
|
||||
// Error
|
||||
$_['error_permission'] = 'Warning: You do not have permission to modify payment PayAnyWay!';
|
||||
$_['error_account'] = 'Account Number is required and must be greater than 0';
|
||||
$_['error_integrity_code'] = 'Account Integrity Code Required';
|
||||
$_['error_order_status'] = 'Select order status';
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
// Heading
|
||||
$_['heading_title'] = 'PayAnyWay';
|
||||
|
||||
// Text
|
||||
$_['text_extension'] = 'Расширения';
|
||||
$_['text_success'] = 'Настройки для способа оплаты PayAnyWay успешно сохранены';
|
||||
$_['text_edit'] = 'Редактирование настроек модуля';
|
||||
$_['text_payanyway'] = '<a href="https://www.payanyway.ru/" target="_blank"><img src="view/image/payment/payanyway.png" alt="PayAnyWay" title="PayAnyWay" width="163" height="18"></a>';
|
||||
|
||||
// Entry
|
||||
$_['entry_status_label'] = 'Включить модуль оплаты';
|
||||
|
||||
$_['entry_account_label'] = 'Номер счёта';
|
||||
$_['entry_account_description'] = 'Ваш номер расширенного счёта в системе MONETA.RU';
|
||||
$_['entry_integrity_code_label'] = 'Код проверки целостности данных';
|
||||
$_['entry_integrity_code_description'] = 'Код проверки целостности данных, указанный в настройках расширенного счёта в системе MONETA.RU';
|
||||
$_['entry_server_label'] = 'Сервер платежей';
|
||||
$_['entry_server_prod_label'] = 'Боевой';
|
||||
$_['entry_server_demo_label'] = 'Тестовый';
|
||||
|
||||
$_['entry_vat_rate_label'] = 'НДС';
|
||||
$_['entry_vat_none_label'] = 'Без НДС';
|
||||
$_['entry_vat0_label'] = '0%';
|
||||
$_['entry_vat5_label'] = '5%';
|
||||
$_['entry_vat7_label'] = '7%';
|
||||
$_['entry_vat10_label'] = '10%';
|
||||
$_['entry_vat22_label'] = '22%';
|
||||
$_['entry_vat105_label'] = '5% (расчётная ставка 5/105)';
|
||||
$_['entry_vat107_label'] = '7% (расчётная ставка 7/107)';
|
||||
$_['entry_vat110_label'] = '10% (расчётная ставка 10/110)';
|
||||
$_['entry_vat122_label'] = '22% (расчётная ставка 22/122)';
|
||||
|
||||
$_['entry_currency_label'] = 'Валюта платежа';
|
||||
$_['entry_currency_rub_label'] = 'Рубль';
|
||||
$_['entry_currency_description'] = 'Валюты в системе MONETA.RU и в магазине должны совпадать';
|
||||
|
||||
$_['entry_select_order_status_label'] = ' --- Выберите статус --- ';
|
||||
$_['entry_order_statuses_label'] = 'Статусы заказа';
|
||||
$_['entry_pending_order_status_label'] = 'Обрабатываемый';
|
||||
$_['entry_success_order_status_label'] = 'Успешный';
|
||||
$_['entry_error_order_status_label'] = 'Неуспешный';
|
||||
|
||||
$_['entry_geo_zone_label'] = 'Регион отображения';
|
||||
$_['entry_sort_order_label'] = 'Порядок сортировки';
|
||||
|
||||
// Help
|
||||
$_['help_pending_order_status'] = 'Укажите статус заказа, который выставляется при его создании';
|
||||
$_['help_success_order_status'] = 'Укажите статус заказа, который выставляется при успешной оплате';
|
||||
$_['help_error_order_status'] = 'Укажите статус заказа, который выставляется при ошибке оплаты';
|
||||
$_['help_geo_zone'] = 'Географическая зона, в которой будет отображаться способ оплаты';
|
||||
$_['help_sort_order'] = 'Определяет порядок отображения платежного метода в списке методов оплаты при оформлении заказа. Чем меньше число, тем выше позиция в списке';
|
||||
|
||||
// Error
|
||||
$_['error_permission'] = 'Недостаточно прав для изменения настроек способа оплаты PayAnyWay';
|
||||
$_['error_account'] = 'Номер счёта обязателен';
|
||||
$_['error_integrity_code'] = 'Код проверки целостности данных обязателен';
|
||||
$_['error_order_status'] = 'Выберите статус заказа';
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 150 KiB |
+237
@@ -0,0 +1,237 @@
|
||||
{{ header }} {{ column_left }}
|
||||
<div id="content">
|
||||
<div class="page-header">
|
||||
<div class="container-fluid">
|
||||
<div class="pull-right">
|
||||
<button type="submit" form="form-payment" data-toggle="tooltip" title="{{ button_save }}"
|
||||
class="btn btn-primary"><i class="fa fa-save"></i></button>
|
||||
<a href="{{ cancel }}" data-toggle="tooltip" title="{{ button_cancel }}" class="btn btn-default"><i
|
||||
class="fa fa-reply"></i></a></div>
|
||||
<h1>{{ heading_title }}</h1>
|
||||
<ul class="breadcrumb">
|
||||
{% for breadcrumb in breadcrumbs %}
|
||||
<li><a href="{{ breadcrumb.href }}">{{ breadcrumb.text }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container-fluid">
|
||||
{% if error_warning %}
|
||||
<div class="alert alert-danger"><i class="fa fa-exclamation-circle"></i> {{ error_warning }}
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title"><i class="fa fa-pencil"></i> {{ text_edit }}</h3>
|
||||
</div>
|
||||
<div class="panel-heading text-center">
|
||||
<a href="https://www.payanyway.ru/" target="_blank">
|
||||
<img src="view/image/payment/payanyway.png" alt="PayAnyWay" title="PayAnyWay" width="250" height="29"/>
|
||||
</a>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<form action="{{ action }}" method="post" enctype="multipart/form-data" id="form-payment"
|
||||
class="form-horizontal">
|
||||
|
||||
{# Вкл/выкл платежного модуля #}
|
||||
<div class="form-group">
|
||||
<div class="col-sm-10 col-sm-offset-2">
|
||||
<div class="form-check" style="display: flex; column-gap: 8px;">
|
||||
<input type="hidden" name="payment_payanyway_status" value="0"/>
|
||||
<input class="form-check-input" type="checkbox" name="payment_payanyway_status" id="input-status"
|
||||
value="1"
|
||||
{% if payment_payanyway_status == "1" %} checked{% endif %}>
|
||||
<label class="form-check-label" for="input-status">{{ entry_status_label }}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Номер счета #}
|
||||
<div class="form-group required">
|
||||
<label class="col-sm-2 control-label" for="input-account">{{ entry_account_label }}</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="number" oninput="this.value = this.value.replace(/[^0-9]/g, '')" step="1"
|
||||
name="payment_payanyway_account" value="{{ payment_payanyway_account }}"
|
||||
placeholder="{{ entry_account_description }}" id="input-account" class="form-control"/>
|
||||
{% if error_account %}
|
||||
<div class="text-danger">{{ error_account }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Код проверки целостности данных #}
|
||||
<div class="form-group required">
|
||||
<label class="col-sm-2 control-label" for="input-integrity-code">{{ entry_integrity_code_label }}</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" name="payment_payanyway_integrity_code" value="{{ payment_payanyway_integrity_code }}"
|
||||
placeholder="{{ entry_integrity_code_description }}" id="input-integrity-code"
|
||||
class="form-control"/>
|
||||
{% if error_integrity_code %}
|
||||
<div class="text-danger">{{ error_integrity_code }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Сервер платежей #}
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="input-server">{{ entry_server_label }}</label>
|
||||
<div class="col-sm-10">
|
||||
<select name="payment_payanyway_server" id="input-server" class="form-control">
|
||||
<option value="prod" {% if payment_payanyway_server == 'prod' %} selected="selected" {% endif %}>{{ entry_server_prod_label }}</option>
|
||||
<option value="demo" {% if payment_payanyway_server == 'demo' %} selected="selected" {% endif %}>{{ entry_server_demo_label }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# НДС #}
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="input-vat-rate">{{ entry_vat_rate_label }}</label>
|
||||
<div class="col-sm-10">
|
||||
<select name="payment_payanyway_vat_rate" id="input-vat-rate" class="form-control">
|
||||
{% for vat_id, label in payment_payanyway_vat_rates %}
|
||||
<option value="{{ vat_id }}"
|
||||
{% if vat_id == payment_payanyway_vat_rate %}selected="selected"{% endif %}>
|
||||
{{ label }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Валюта платежа #}
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="input-currency">{{ entry_currency_label }}</label>
|
||||
<div class="col-sm-10">
|
||||
<select name="payment_payanyway_currency" id="input-currency" class="form-control">
|
||||
{% for code, data in payment_payanyway_currencies %}
|
||||
<option value="{{ code }}" {% if code == payment_payanyway_currency %}selected="selected"{% endif %}>
|
||||
{{ data.code }} ({{ entry_currency_rub_label }})
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<p class="help-block">{{ entry_currency_description }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Статусы заказа #}
|
||||
<div class="form-group required">
|
||||
<label class="col-sm-2 control-label">{{ entry_order_statuses_label }}</label>
|
||||
<div class="col-sm-10">
|
||||
|
||||
<div class="panel panel-default" style="margin-bottom: 0;">
|
||||
<div class="panel-body">
|
||||
{# pending #}
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label" for="input-pending-order-status">
|
||||
<span data-toggle="tooltip"
|
||||
title="{{ help_pending_order_status }}">{{ entry_pending_order_status_label }}</span>
|
||||
</label>
|
||||
<div class="col-sm-9">
|
||||
<select name="payment_payanyway_pending_order_status_id" id="input-pending-order-status"
|
||||
class="form-control">
|
||||
<option value="" disabled
|
||||
selected="selected">{{ entry_select_order_status_label|default(text_select) }}</option>
|
||||
{% for order_status in order_statuses %}
|
||||
<option value="{{ order_status.order_status_id }}"
|
||||
{% if order_status.order_status_id == payment_payanyway_pending_order_status_id %}selected="selected"{% endif %}>
|
||||
{{ order_status.name }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
{% if error_pending_order_status %}
|
||||
<div class="text-danger">{{ error_order_status }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# success #}
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label" for="input-success-order-status">
|
||||
<span data-toggle="tooltip"
|
||||
title="{{ help_success_order_status }}">{{ entry_success_order_status_label }}</span>
|
||||
</label>
|
||||
<div class="col-sm-9">
|
||||
<select name="payment_payanyway_success_order_status_id" id="input-success-order-status"
|
||||
class="form-control">
|
||||
<option value="" disabled
|
||||
selected="selected">{{ entry_select_order_status_label|default(text_select) }}</option>
|
||||
{% for order_status in order_statuses %}
|
||||
<option value="{{ order_status.order_status_id }}"
|
||||
{% if order_status.order_status_id == payment_payanyway_success_order_status_id %}selected="selected"{% endif %}>
|
||||
{{ order_status.name }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
{% if error_success_order_status %}
|
||||
<div class="text-danger">{{ error_order_status }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# error #}
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label" for="input-error-order-status">
|
||||
<span data-toggle="tooltip"
|
||||
title="{{ help_error_order_status }}">{{ entry_error_order_status_label }}</span>
|
||||
</label>
|
||||
<div class="col-sm-9">
|
||||
<select name="payment_payanyway_error_order_status_id" id="input-error-order-status"
|
||||
class="form-control">
|
||||
<option value="" disabled
|
||||
selected="selected">{{ entry_select_order_status_label|default(text_select) }}</option>
|
||||
{% for order_status in order_statuses %}
|
||||
<option value="{{ order_status.order_status_id }}"
|
||||
{% if order_status.order_status_id == payment_payanyway_error_order_status_id %}selected="selected"{% endif %}>
|
||||
{{ order_status.name }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
{% if error_error_order_status %}
|
||||
<div class="text-danger">{{ error_order_status }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# module_geo_zone #}
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="input-geo-zone">
|
||||
<span data-toggle="tooltip" title="{{ help_geo_zone }}">{{ entry_geo_zone_label }}</span>
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<select name="payment_payanyway_geo_zone_id" id="input-geo-zone" class="form-control">
|
||||
<option value="0">{{ text_all_zones }}</option>
|
||||
{% for geo_zone in geo_zones %}
|
||||
{% if geo_zone.geo_zone_id == payment_payanyway_geo_zone_id %}
|
||||
<option value="{{ geo_zone.geo_zone_id }}" selected="selected">{{ geo_zone.name }}</option>
|
||||
{% else %}
|
||||
<option value="{{ geo_zone.geo_zone_id }}">{{ geo_zone.name }}</option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</select>
|
||||
<p class="help-block">{{ entry_geo_zone_description }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# sort-order #}
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="input-sort-order">
|
||||
<span data-toggle="tooltip" title="{{ help_sort_order }}">{{ entry_sort_order_label }}</span>
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="number" oninput="this.value = this.value.replace(/[^-0-9]/g, '').replace(/(?!^-)-/g, '')"
|
||||
step="1" name="payment_payanyway_sort_order" value="{{ payment_payanyway_sort_order }}"
|
||||
placeholder="{{ module_sort_order_label }}" id="input-sort-order" class="form-control"/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ footer }}
|
||||
+672
@@ -0,0 +1,672 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @property ModelCheckoutOrder $model_checkout_order
|
||||
* @property ModelAccountOrder $model_account_order
|
||||
* @property ModelLocalisationOrderStatus $model_localisation_order_status
|
||||
*/
|
||||
class ControllerExtensionPaymentPayanyway extends Controller {
|
||||
const PAYMENT_SERVER_PROD = 'prod';
|
||||
const PAYMENT_URL_PROD = 'https://www.payanyway.ru/assistant.htm';
|
||||
const PAYMENT_URL_DEMO = 'https://demo.moneta.ru/assistant.htm';
|
||||
|
||||
const TRANSACTION_ID_STRING_DELIMITER = '|';
|
||||
const DESCRIPTION_MAX_LENGTH = 500;
|
||||
|
||||
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';
|
||||
|
||||
/** @var int */
|
||||
private $mntId;
|
||||
|
||||
/** @var string */
|
||||
private $integrityCode;
|
||||
|
||||
/** @var string */
|
||||
private $server;
|
||||
|
||||
/** @var string */
|
||||
private $vatRate;
|
||||
|
||||
/** @var string */
|
||||
private $currency;
|
||||
|
||||
/** @var int */
|
||||
private $pendingOrderStatusId;
|
||||
|
||||
/** @var int */
|
||||
private $successOrderStatusId;
|
||||
|
||||
/** @var int */
|
||||
private $errorOrderStatusId;
|
||||
|
||||
/** @var string */
|
||||
private $cmsModuleVersion;
|
||||
|
||||
/** @var int|null */
|
||||
private $orderId;
|
||||
|
||||
/** @var array<string, mixed> */
|
||||
private $orderInfo;
|
||||
|
||||
/** @throws \Exception */
|
||||
public function __construct($registry) {
|
||||
parent::__construct($registry);
|
||||
|
||||
$this->initializeModule();
|
||||
|
||||
$this->orderId = $this->getOrderId();
|
||||
$this->orderInfo = $this->getOrderInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function index() {
|
||||
$this->load->language('extension/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;
|
||||
|
||||
$orderTotal = (float)(isset($this->orderInfo['total']) ? $this->orderInfo['total'] : 0);
|
||||
|
||||
$data['MNT_ID'] = $this->mntId;
|
||||
$data['MNT_TRANSACTION_ID'] = $this->createTransactionId();
|
||||
$data['MNT_AMOUNT'] = $this->formatPrice($orderTotal);
|
||||
$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/payment/payanyway/success', 'language=' . $this->config->get('config_language'), true);
|
||||
$data['MNT_FAIL_URL'] = $this->url->link('extension/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/payment/payanyway/confirm', 'language=' . $this->config->get('config_language'), true);
|
||||
|
||||
return $this->load->view('extension/payment/payanyway', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function confirm() {
|
||||
$json = array();
|
||||
|
||||
$this->load->language('extension/payment/payanyway');
|
||||
$this->addOrderHistory($this->pendingOrderStatusId, $this->language->get('text_order_confirmed'));
|
||||
|
||||
$json['success'] = true;
|
||||
|
||||
$this->response->addHeader('Content-Type: application/json');
|
||||
$this->response->setOutput(json_encode($json));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function success() {
|
||||
$this->load->language('extension/payment/payanyway');
|
||||
$this->addOrderHistory($this->pendingOrderStatusId, $this->language->get('text_payment_processing'));
|
||||
|
||||
$this->response->redirect($this->url->link('checkout/success', '', true));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function fail() {
|
||||
$this->load->language('extension/payment/payanyway');
|
||||
$this->addOrderHistory($this->errorOrderStatusId, $this->language->get('text_payment_error'));
|
||||
|
||||
$this->response->redirect($this->url->link('checkout/failure', '', true));
|
||||
}
|
||||
|
||||
/** @return int|null */
|
||||
private function getOrderId() {
|
||||
$orderId = $this->getOrderIdFromTransactionId();
|
||||
if (null !== $orderId) {
|
||||
return $orderId;
|
||||
}
|
||||
|
||||
return $this->getOrderIdFromSession();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return false|array
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function getOrderInfo() {
|
||||
if (null === $this->orderId) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$this->load->model('checkout/order');
|
||||
return $this->model_checkout_order->getOrder($this->orderId);
|
||||
}
|
||||
|
||||
/** @return string */
|
||||
private function createTransactionId() {
|
||||
$date = (new \DateTimeImmutable())->format('YmdHis');
|
||||
return $this->orderId . self::TRANSACTION_ID_STRING_DELIMITER . $date;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param float $price
|
||||
* @return string
|
||||
*/
|
||||
private function formatPrice($price) {
|
||||
return number_format($price, 2, '.', '');
|
||||
}
|
||||
|
||||
/** @return string */
|
||||
private function getDescription() {
|
||||
$clientFirstName = isset($this->orderInfo['firstname']) ? $this->orderInfo['firstname'] : '';
|
||||
$clientLastName = isset($this->orderInfo['lastname']) ? $this->orderInfo['lastname'] : '';
|
||||
$clientName = trim($clientFirstName . ' ' . $clientLastName);
|
||||
|
||||
$orderId = isset($this->orderInfo['order_id']) ? $this->orderInfo['order_id'] : null;
|
||||
$description = "Оплата заказа" . (!empty($orderId) ? " №{$orderId}" : '');
|
||||
|
||||
if ('' !== $clientName) {
|
||||
$description .= " от {$clientName}";
|
||||
}
|
||||
|
||||
$comment = isset($this->orderInfo['comment']) ? $this->orderInfo['comment'] : '';
|
||||
if ('' !== $comment) {
|
||||
$description .= ': ' . htmlspecialchars($comment, ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
|
||||
return $this->validateString($description, self::DESCRIPTION_MAX_LENGTH);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
* @param int $maxLength
|
||||
* @return string
|
||||
*/
|
||||
private function validateString($value, $maxLength) {
|
||||
$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 ? '...' : '');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
private function sanitizeString($value) {
|
||||
$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));
|
||||
}
|
||||
|
||||
/** @return string */
|
||||
private function getSubscriberId() {
|
||||
$email = isset($this->orderInfo['email'])
|
||||
? filter_var($this->orderInfo['email'], FILTER_SANITIZE_EMAIL)
|
||||
: '';
|
||||
if ('' !== $email) {
|
||||
return $email;
|
||||
}
|
||||
|
||||
return isset($this->orderInfo['telephone'])
|
||||
? preg_replace('/[^0-9+]/', '', $this->orderInfo['telephone'])
|
||||
: '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $orderStatusId
|
||||
* @param string $comment
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function addOrderHistory($orderStatusId, $comment) {
|
||||
$this->load->model('checkout/order');
|
||||
$this->model_checkout_order->addOrderHistory($this->orderId, $orderStatusId, $comment);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @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);
|
||||
|
||||
(isset($callbackData['MNT_COMMAND'])) && ('CHECK' === $callbackData['MNT_COMMAND'])
|
||||
? $this->handleCheckCallback($callbackData)
|
||||
: $this->handlePayCallback($callbackData);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $requestData
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function getCallbackData(array $requestData) {
|
||||
return array(
|
||||
'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'),
|
||||
);
|
||||
}
|
||||
|
||||
/** @return int|null */
|
||||
private function getOrderIdFromTransactionId() {
|
||||
if (!isset($_REQUEST['MNT_TRANSACTION_ID'])) {
|
||||
return null;
|
||||
}
|
||||
$transactionIdData = explode(self::TRANSACTION_ID_STRING_DELIMITER, $_REQUEST['MNT_TRANSACTION_ID']);
|
||||
|
||||
return isset($transactionIdData[0]) ? (int)$transactionIdData[0] : null;
|
||||
}
|
||||
|
||||
/** @return int|null */
|
||||
private function getOrderIdFromSession() {
|
||||
return isset($this->session->data['order_id']) ? (int)$this->session->data['order_id'] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $response
|
||||
* @param int $statusCode
|
||||
* @return void
|
||||
*/
|
||||
private function sendResponse($response, $statusCode = 200) {
|
||||
http_response_code($statusCode);
|
||||
|
||||
if (strpos($response, '<?xml') === 0 || strpos($response, '<MNT_RESPONSE') !== false) {
|
||||
header('Content-Type: application/xml');
|
||||
} else {
|
||||
header('Content-Type: text/plain; charset=UTF-8');
|
||||
}
|
||||
|
||||
echo $response;
|
||||
exit();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $data
|
||||
* @return bool
|
||||
*/
|
||||
private function checkSignature(array $data, $callbackSignature) {
|
||||
$baseFields = array(
|
||||
'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), $callbackSignature);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $callbackData
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function handleCheckCallback(array $callbackData) {
|
||||
if (!$this->checkSignature($callbackData, $callbackData['MNT_SIGNATURE'])) {
|
||||
$this->sendResponse('FAIL');
|
||||
}
|
||||
|
||||
$this->load->model('account/order');
|
||||
$products = $this->model_account_order->getOrderProducts($this->orderId);
|
||||
$deliveryPrice = $this->getDeliveryPrice();
|
||||
|
||||
$orderTotal = (float)(isset($this->orderInfo['total']) ? $this->orderInfo['total'] : 0);
|
||||
|
||||
$xmlData = array(
|
||||
'MNT_ID' => $this->mntId,
|
||||
'MNT_TRANSACTION_ID' => $callbackData['MNT_TRANSACTION_ID'],
|
||||
'MNT_AMOUNT' => $this->formatPrice($orderTotal),
|
||||
'MNT_CURRENCY_CODE' => $callbackData['MNT_CURRENCY_CODE'],
|
||||
'inventory' => $this->getInventoryJson($products) ?: null,
|
||||
'client' => $callbackData['MNT_SUBSCRIBER_ID'],
|
||||
'sno' => null,
|
||||
'delivery' => (null !== $deliveryPrice) ? $this->formatPrice($deliveryPrice) : null,
|
||||
);
|
||||
|
||||
$orderStatusId = (int)(isset($this->orderInfo['order_status_id']) ? $this->orderInfo['order_status_id'] : 1);
|
||||
|
||||
list($xmlData['MNT_RESULT_CODE'], $xmlData['MNT_DESCRIPTION']) = $this->determineCheckResult($callbackData, $orderStatusId);
|
||||
|
||||
$xmlData['MNT_SIGNATURE'] = md5(
|
||||
$xmlData['MNT_RESULT_CODE'] .
|
||||
$xmlData['MNT_ID'] .
|
||||
$xmlData['MNT_TRANSACTION_ID'] .
|
||||
$this->integrityCode
|
||||
);
|
||||
|
||||
$this->sendResponse($this->buildXMLResponse($xmlData));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $callbackData
|
||||
* @param int $orderStatusId
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function determineCheckResult(array $callbackData, $orderStatusId) {
|
||||
$orderStatus = $this->getOrderStatusById($orderStatusId);
|
||||
|
||||
if (empty($callbackData['MNT_AMOUNT'])) {
|
||||
return array(100, "Order status is '{$orderStatus}'");
|
||||
}
|
||||
|
||||
if ($this->successOrderStatusId === $orderStatusId) {
|
||||
return array(200, 'Order Paid');
|
||||
}
|
||||
|
||||
if (in_array($orderStatusId, array(7, 9, 16), true)) {
|
||||
return array(500, "Order status is '{$orderStatus}'");
|
||||
}
|
||||
|
||||
return array(402, 'Order created, but not paid');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $callbackData
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function handlePayCallback(array $callbackData) {
|
||||
$orderTotal = (float)(isset($this->orderInfo['total']) ? $this->orderInfo['total'] : 0);
|
||||
|
||||
$currencyCode = isset($this->orderInfo['currency_code']) ? $this->orderInfo['currency_code'] : 'RUB';
|
||||
$shopData = array(
|
||||
'MNT_ID' => $this->mntId,
|
||||
'MNT_TRANSACTION_ID' => $callbackData['MNT_TRANSACTION_ID'],
|
||||
'MNT_OPERATION_ID' => $callbackData['MNT_OPERATION_ID'],
|
||||
'MNT_AMOUNT' => $this->formatPrice($orderTotal),
|
||||
'MNT_CURRENCY_CODE' => $currencyCode,
|
||||
'MNT_SUBSCRIBER_ID' => $callbackData['MNT_SUBSCRIBER_ID'],
|
||||
'MNT_TEST_MODE' => $callbackData['MNT_TEST_MODE'],
|
||||
);
|
||||
|
||||
if (!$this->checkSignature($shopData, $callbackData['MNT_SIGNATURE'])) {
|
||||
$this->sendResponse('FAIL');
|
||||
}
|
||||
|
||||
$isPayedOrder = $this->isOrderPaid();
|
||||
|
||||
$resultCode = 200;
|
||||
|
||||
$this->load->model('account/order');
|
||||
$products = $this->model_account_order->getOrderProducts($this->orderId);
|
||||
$deliveryPrice = $this->getDeliveryPrice();
|
||||
|
||||
$xmlData = array(
|
||||
'MNT_ID' => $this->mntId,
|
||||
'MNT_TRANSACTION_ID' => $callbackData['MNT_TRANSACTION_ID'],
|
||||
'MNT_RESULT_CODE' => $resultCode,
|
||||
'MNT_SIGNATURE' => md5($resultCode . $this->mntId . $callbackData['MNT_TRANSACTION_ID'] . $this->integrityCode),
|
||||
'MNT_AMOUNT' => $callbackData['MNT_AMOUNT'],
|
||||
'MNT_CURRENCY_CODE' => $callbackData['MNT_CURRENCY_CODE'],
|
||||
'MNT_DESCRIPTION' => $isPayedOrder ? 'Order already paid' : 'Order success paid',
|
||||
'inventory' => $this->getInventoryJson($products),
|
||||
'client' => $callbackData['MNT_SUBSCRIBER_ID'],
|
||||
'sno' => null,
|
||||
'delivery' => $deliveryPrice !== null ? $this->formatPrice($deliveryPrice) : null,
|
||||
);
|
||||
|
||||
if (!$isPayedOrder) {
|
||||
$this->load->language('extension/payment/payanyway');
|
||||
$this->addOrderHistory($this->successOrderStatusId, $this->language->get('text_payment_completed'));
|
||||
}
|
||||
|
||||
$this->sendResponse($this->buildXMLResponse($xmlData));
|
||||
}
|
||||
|
||||
/** @return bool */
|
||||
private function isOrderPaid() {
|
||||
if (!isset($this->orderInfo['order_status_id'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->successOrderStatusId === (int)$this->orderInfo['order_status_id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return float|null
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function getDeliveryPrice() {
|
||||
$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 null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $products
|
||||
* @return false|string
|
||||
*/
|
||||
private function getInventoryJson(array $products) {
|
||||
$inventory = array();
|
||||
foreach ($products as $product) {
|
||||
$name = isset($product['name']) ? $product['name'] : '';
|
||||
$price = isset($product['price']) ? (float)$product['price'] : 0;
|
||||
$quantity = isset($product['quantity']) ? (string)$product['quantity'] : '0';
|
||||
|
||||
$inventory[] = array(
|
||||
'name' => $this->validateString($name, self::INVENTORY_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,
|
||||
);
|
||||
}
|
||||
|
||||
return json_encode($inventory, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $orderStatusId
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getOrderStatusById($orderStatusId) {
|
||||
$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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $quantity
|
||||
* @return string
|
||||
*/
|
||||
private function formatQuantity($quantity) {
|
||||
$quantity = str_replace(',', '.', $quantity);
|
||||
if (strpos($quantity, '.') === false && ctype_digit($quantity)) {
|
||||
return (string)(int)$quantity;
|
||||
}
|
||||
|
||||
return number_format((float)$quantity, 3, '.', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $data
|
||||
* @return false|string
|
||||
* @return bool|string
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function buildXMLResponse(array $data) {
|
||||
$dom = new \DOMDocument('1.0', 'UTF-8');
|
||||
$dom->formatOutput = false;
|
||||
|
||||
$root = $dom->createElement('MNT_RESPONSE');
|
||||
$dom->appendChild($root);
|
||||
|
||||
$requiredFields = array(
|
||||
'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 = array(
|
||||
'INVENTORY' => isset($data['inventory']) ? $data['inventory'] : null,
|
||||
'CLIENT' => isset($data['client']) ? $data['client'] : null,
|
||||
'SNO' => isset($data['sno']) ? $data['sno'] : null,
|
||||
'DELIVERY' => isset($data['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
|
||||
* @param string $key
|
||||
* @param string $default
|
||||
* @return string
|
||||
*/
|
||||
private function getOptionalStringWithDefault(array $params, $key, $default = '') {
|
||||
$optionalString = $this->getOptionalString($params, $key);
|
||||
if (null !== $optionalString) {
|
||||
return $optionalString;
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $params
|
||||
* @param string $key
|
||||
* @return string|null
|
||||
*/
|
||||
private function getOptionalString(array $params, $key) {
|
||||
$value = isset($params[$key]) ? $params[$key] : null;
|
||||
if (!is_string($value) || trim($value) === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $params
|
||||
* @param string $key
|
||||
* @return string
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
private function getRequiredString(array $params, $key) {
|
||||
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;
|
||||
}
|
||||
|
||||
private function initializeModule() {
|
||||
$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');
|
||||
}
|
||||
}
|
||||
@@ -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';
|
||||
@@ -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'] = 'Ошибка оплаты';
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
class ModelExtensionPaymentPayanyway extends Model {
|
||||
/**
|
||||
* @param array<string, mixed> $address
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function getMethod(array $address) {
|
||||
$this->load->language('extension/payment/payanyway');
|
||||
|
||||
if (!$this->isModuleEnabled() ||
|
||||
!$this->isCurrencySupported() ||
|
||||
!$this->isGeoZoneValid($address)
|
||||
) {
|
||||
return array();
|
||||
}
|
||||
|
||||
return array(
|
||||
'code' => 'payanyway',
|
||||
'title' => $this->language->get('text_title'),
|
||||
'terms' => '',
|
||||
'sort_order' => $this->config->get('payment_payanyway_sort_order'),
|
||||
);
|
||||
}
|
||||
|
||||
/** @return bool */
|
||||
private function isModuleEnabled() {
|
||||
return (bool)$this->config->get('payment_payanyway_status');
|
||||
}
|
||||
|
||||
/** @return bool */
|
||||
private function isCurrencySupported() {
|
||||
$shopCurrency = isset($this->session->data['currency']) ? $this->session->data['currency'] : '';
|
||||
|
||||
return $shopCurrency === $this->config->get('payment_payanyway_currency');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $address
|
||||
* @return bool
|
||||
*/
|
||||
private function isGeoZoneValid(array $address) {
|
||||
$geoZoneId = (int)$this->config->get('payment_payanyway_geo_zone_id');
|
||||
|
||||
if ($geoZoneId === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$query = $this->db->query(
|
||||
"SELECT * FROM " . DB_PREFIX . "zone_to_geo_zone
|
||||
WHERE geo_zone_id = '" . $geoZoneId . "'
|
||||
AND country_id = '" . (int)$address['country_id'] . "'
|
||||
AND (zone_id = '" . (int)$address['zone_id'] . "' OR zone_id = '0')"
|
||||
);
|
||||
|
||||
return $query->num_rows > 0;
|
||||
}
|
||||
}
|
||||
+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>
|
||||
$(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