feat: add fiscalization settings, POST callbacks and fix callback issues
This commit is contained in:
@@ -10,10 +10,12 @@ namespace Opencart\Admin\Controller\Extension\PayAnyWay\Payment;
|
||||
class PayAnyWay extends \Opencart\System\Engine\Controller
|
||||
{
|
||||
public const CMS_NAME = 'OpenCart';
|
||||
public const MODULE_NAME = 'payanyway';
|
||||
public const MODULE_VERSION = '1.0.2';
|
||||
public const MODULE_VERSION = '1.0.3';
|
||||
public const MODULE_DEFAULT_SORT_ORDER = 0;
|
||||
|
||||
private const ROUTE_STARTUP_CODE = 'payanyway_post_route';
|
||||
private const ROUTE_STARTUP_ACTION = 'catalog/extension/payanyway/startup/payanyway';
|
||||
|
||||
private const SUPPORTED_CURRENCIES = ['RUB'];
|
||||
private const AVAILABLE_VAT_RATES = [
|
||||
'vat_none' => '1105',
|
||||
@@ -28,6 +30,45 @@ class PayAnyWay extends \Opencart\System\Engine\Controller
|
||||
'vat122' => '1114',
|
||||
];
|
||||
|
||||
private const PAYMENT_METHODS = [
|
||||
'full_prepayment',
|
||||
'prepayment',
|
||||
'advance',
|
||||
'full_payment',
|
||||
'partial_payment',
|
||||
'credit',
|
||||
'credit_payment',
|
||||
];
|
||||
|
||||
private const PAYMENT_OBJECTS = [
|
||||
'commodity',
|
||||
'excise',
|
||||
'job',
|
||||
'service',
|
||||
'gambling_bet',
|
||||
'gambling_prize',
|
||||
'lottery',
|
||||
'lottery_prize',
|
||||
'intellectual_activity',
|
||||
'payment',
|
||||
'agent_commission',
|
||||
'composite',
|
||||
'another',
|
||||
'property_right',
|
||||
'sales_tax',
|
||||
'resort_fee',
|
||||
'award',
|
||||
'non_operating',
|
||||
'insurance_premium',
|
||||
];
|
||||
|
||||
/** Canceled, Canceled Reversal, Voided штатной установки OpenCart. */
|
||||
private const DEFAULT_CANCELED_ORDER_STATUS_IDS = [7, 9, 16];
|
||||
|
||||
private const RECEIPT_EMAIL_MAX_LENGTH = 64;
|
||||
private const DEFAULT_PAYMENT_METHOD = 'full_payment';
|
||||
private const DEFAULT_PAYMENT_OBJECT = 'commodity';
|
||||
|
||||
private array $errors = [];
|
||||
|
||||
/**
|
||||
@@ -42,13 +83,25 @@ class PayAnyWay extends \Opencart\System\Engine\Controller
|
||||
$this->applyValidationErrors($data);
|
||||
|
||||
$data['breadcrumbs'] = $this->getBreadcrumbs();
|
||||
$warnings = $this->getWarnings($this->isFiscalizationEnabled(), (string)$this->config->get('payment_payanyway_receipt_email'));
|
||||
$data['warning_startup'] = $warnings['startup'];
|
||||
$data['warning_receipt_email'] = $warnings['receipt_email'];
|
||||
$data['save'] = $this->url->link('extension/payanyway/payment/payanyway.save', 'user_token=' . $this->session->data['user_token']);
|
||||
$data['back'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=payment');
|
||||
|
||||
$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();
|
||||
$data['integrity_code_placeholder'] = $this->hasIntegrityCode()
|
||||
? $this->language->get('text_integrity_code_saved')
|
||||
: $this->language->get('entry_integrity_code_description');
|
||||
$data['payment_payanyway_server'] = $this->retrieveValue('payment_payanyway_server');
|
||||
$data['payment_payanyway_test_mode'] = (string)$this->retrieveValue('payment_payanyway_test_mode');
|
||||
$data['payment_payanyway_fiscalization'] = $this->isFiscalizationEnabled() ? '1' : '0';
|
||||
$data['payment_payanyway_receipt_email'] = htmlspecialchars((string)$this->config->get('payment_payanyway_receipt_email'), ENT_QUOTES, 'UTF-8');
|
||||
$data['payment_payanyway_payment_methods'] = $this->getOptions(self::PAYMENT_METHODS, 'entry_payment_method_');
|
||||
$data['payment_payanyway_payment_method'] = $this->retrieveValueOrDefault('payment_payanyway_payment_method', self::DEFAULT_PAYMENT_METHOD);
|
||||
$data['payment_payanyway_payment_objects'] = $this->getOptions(self::PAYMENT_OBJECTS, 'entry_payment_object_');
|
||||
$data['payment_payanyway_payment_object'] = $this->retrieveValueOrDefault('payment_payanyway_payment_object', self::DEFAULT_PAYMENT_OBJECT);
|
||||
$data['payment_payanyway_vat_rates'] = $this->getModuleVatRates();
|
||||
$data['payment_payanyway_vat_rate'] = $this->retrieveValue('payment_payanyway_vat_rate');
|
||||
$data['payment_payanyway_currencies'] = $this->getModuleCurrencies();
|
||||
@@ -57,10 +110,11 @@ class PayAnyWay extends \Opencart\System\Engine\Controller
|
||||
$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['payment_payanyway_canceled_order_status_ids'] = $this->getCanceledOrderStatusIds();
|
||||
$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['callback_url'] = HTTP_CATALOG . 'index.php?route=extension/payanyway/payment/callback';
|
||||
|
||||
$data['header'] = $this->load->controller('common/header');
|
||||
$data['column_left'] = $this->load->controller('common/column_left');
|
||||
@@ -100,8 +154,15 @@ class PayAnyWay extends \Opencart\System\Engine\Controller
|
||||
if (!$json) {
|
||||
$this->load->model('setting/setting');
|
||||
$this->model_setting_setting->editSetting('payment_payanyway', $this->request->post);
|
||||
if (!$this->isRouteStartupRegistered()) {
|
||||
$this->registerRouteStartup();
|
||||
}
|
||||
|
||||
$json['success'] = $this->language->get('text_success');
|
||||
$json['payanyway_warnings'] = $this->getWarnings(
|
||||
(bool)$this->request->post['payment_payanyway_fiscalization'],
|
||||
(string)$this->request->post['payment_payanyway_receipt_email']
|
||||
);
|
||||
}
|
||||
|
||||
$this->response->addHeader('Content-Type: application/json');
|
||||
@@ -122,8 +183,15 @@ class PayAnyWay extends \Opencart\System\Engine\Controller
|
||||
'payment_payanyway_account' => '',
|
||||
'payment_payanyway_integrity_code' => '',
|
||||
'payment_payanyway_server' => 'prod',
|
||||
'payment_payanyway_test_mode' => 0,
|
||||
'payment_payanyway_fiscalization' => 0,
|
||||
'payment_payanyway_vat_rate' => self::AVAILABLE_VAT_RATES['vat_none'],
|
||||
'payment_payanyway_receipt_email' => '',
|
||||
'payment_payanyway_payment_method' => self::DEFAULT_PAYMENT_METHOD,
|
||||
'payment_payanyway_payment_object' => self::DEFAULT_PAYMENT_OBJECT,
|
||||
'payment_payanyway_canceled_order_status_ids' => self::DEFAULT_CANCELED_ORDER_STATUS_IDS,
|
||||
'payment_payanyway_sort_order' => 0,
|
||||
'payment_payanyway_cms_module_version' => $this->getCmsModuleVersion(),
|
||||
'payment_payanyway_cms_name' => $this->getCmsName(),
|
||||
];
|
||||
|
||||
$orderStatuses = $this->getAvailableOrderStatuses();
|
||||
@@ -154,6 +222,8 @@ class PayAnyWay extends \Opencart\System\Engine\Controller
|
||||
|
||||
$this->load->model('setting/setting');
|
||||
$this->model_setting_setting->editSetting('payment_payanyway', $defaultSettings);
|
||||
|
||||
$this->registerRouteStartup();
|
||||
}
|
||||
|
||||
public function uninstall(): void
|
||||
@@ -164,6 +234,69 @@ class PayAnyWay extends \Opencart\System\Engine\Controller
|
||||
|
||||
$this->load->model('setting/setting');
|
||||
$this->model_setting_setting->deleteSetting('payment_payanyway');
|
||||
|
||||
$this->load->model('setting/startup');
|
||||
$this->model_setting_startup->deleteStartupByCode(self::ROUTE_STARTUP_CODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Регистрирует startup витрины. Сигнатура addStartup() менялась: 4.0.0–4.0.1 —
|
||||
* позиционные аргументы, 4.0.2 — массив, 4.1 — массив с обязательным description
|
||||
* (лишний ключ 4.0.2 игнорирует).
|
||||
*/
|
||||
private function registerRouteStartup(): void
|
||||
{
|
||||
$this->load->model('setting/startup');
|
||||
$this->model_setting_startup->deleteStartupByCode(self::ROUTE_STARTUP_CODE);
|
||||
|
||||
if (version_compare(VERSION, '4.0.2.0', '<')) {
|
||||
$this->model_setting_startup->addStartup(self::ROUTE_STARTUP_CODE, self::ROUTE_STARTUP_ACTION, true, 0);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->model_setting_startup->addStartup([
|
||||
'code' => self::ROUTE_STARTUP_CODE,
|
||||
'description' => 'PayAnyWay: Check/Pay URL методом POST',
|
||||
'action' => self::ROUTE_STARTUP_ACTION,
|
||||
'status' => 1,
|
||||
'sort_order' => 0,
|
||||
]);
|
||||
}
|
||||
|
||||
private function isRouteStartupRegistered(): bool
|
||||
{
|
||||
$this->load->model('setting/startup');
|
||||
|
||||
return [] !== $this->model_setting_startup->getStartupByCode(self::ROUTE_STARTUP_CODE);
|
||||
}
|
||||
|
||||
private function isRouteStartupActive(): bool
|
||||
{
|
||||
$this->load->model('setting/startup');
|
||||
$startup = $this->model_setting_startup->getStartupByCode(self::ROUTE_STARTUP_CODE);
|
||||
|
||||
return !empty($startup['status']) && (($startup['action'] ?? '') === self::ROUTE_STARTUP_ACTION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Предупреждения над формой настроек; пустая строка — предупреждения нет.
|
||||
*
|
||||
* @return array{startup: string, receipt_email: string}
|
||||
*/
|
||||
private function getWarnings(bool $fiscalization, string $receiptEmail): array
|
||||
{
|
||||
return [
|
||||
'startup' => $this->isRouteStartupActive()
|
||||
? ''
|
||||
: sprintf(
|
||||
$this->language->get('warning_startup_route'),
|
||||
$this->url->link('marketplace/startup', 'user_token=' . $this->session->data['user_token'])
|
||||
),
|
||||
'receipt_email' => ($fiscalization && ('' === $receiptEmail))
|
||||
? $this->language->get('warning_receipt_email')
|
||||
: '',
|
||||
];
|
||||
}
|
||||
|
||||
private function validate(): bool
|
||||
@@ -171,14 +304,42 @@ class PayAnyWay extends \Opencart\System\Engine\Controller
|
||||
$this->errors = [];
|
||||
|
||||
if (!isset($this->request->post['payment_payanyway_account']) ||
|
||||
!preg_match('/^[0-9]{1,20}$/D', (string)$this->request->post['payment_payanyway_account']) ||
|
||||
((int)$this->request->post['payment_payanyway_account'] <= 0)
|
||||
) {
|
||||
$this->errors['account'] = $this->language->get('error_account');
|
||||
}
|
||||
|
||||
if (empty($this->request->post['payment_payanyway_integrity_code'])) {
|
||||
// OpenCart экранирует весь POST (htmlspecialchars в system/library/request.php): код с & " < >
|
||||
// сохранился бы экранированным, и подписи не сошлись бы с MONETA.RU.
|
||||
$integrityCode = $this->decodePostedString('payment_payanyway_integrity_code');
|
||||
if (('' === $integrityCode) && $this->hasIntegrityCode()) {
|
||||
$integrityCode = (string)$this->config->get('payment_payanyway_integrity_code');
|
||||
}
|
||||
if ('' === $integrityCode) {
|
||||
$this->errors['integrity_code'] = $this->language->get('error_integrity_code');
|
||||
}
|
||||
$this->request->post['payment_payanyway_integrity_code'] = $integrityCode;
|
||||
|
||||
$fiscalization = !empty($this->request->post['payment_payanyway_fiscalization']);
|
||||
$this->request->post['payment_payanyway_fiscalization'] = $fiscalization ? 1 : 0;
|
||||
$this->request->post['payment_payanyway_test_mode'] = empty($this->request->post['payment_payanyway_test_mode']) ? 0 : 1;
|
||||
|
||||
$receiptEmail = $this->decodePostedString('payment_payanyway_receipt_email');
|
||||
if (($fiscalization || ('' !== $receiptEmail)) && !$this->isValidReceiptEmail($receiptEmail)) {
|
||||
$this->errors['receipt_email'] = $this->language->get('error_receipt_email');
|
||||
}
|
||||
$this->request->post['payment_payanyway_receipt_email'] = $receiptEmail;
|
||||
|
||||
if (!in_array($this->request->post['payment_payanyway_payment_method'] ?? '', self::PAYMENT_METHODS, true)) {
|
||||
$this->request->post['payment_payanyway_payment_method'] = self::DEFAULT_PAYMENT_METHOD;
|
||||
}
|
||||
if (!in_array($this->request->post['payment_payanyway_payment_object'] ?? '', self::PAYMENT_OBJECTS, true)) {
|
||||
$this->request->post['payment_payanyway_payment_object'] = self::DEFAULT_PAYMENT_OBJECT;
|
||||
}
|
||||
|
||||
$canceled = (array)($this->request->post['payment_payanyway_canceled_order_status_ids'] ?? []);
|
||||
$this->request->post['payment_payanyway_canceled_order_status_ids'] = array_values(array_unique(array_filter(array_map('intval', $canceled), static fn (int $statusId): bool => $statusId > 0)));
|
||||
|
||||
if (empty($this->request->post['payment_payanyway_pending_order_status_id'])) {
|
||||
$this->errors['pending_order_status'] = $this->language->get('error_pending_order_status');
|
||||
@@ -190,11 +351,20 @@ class PayAnyWay extends \Opencart\System\Engine\Controller
|
||||
$this->errors['error_order_status'] = $this->language->get('error_error_order_status');
|
||||
}
|
||||
|
||||
$moduleStatuses = [
|
||||
(int)($this->request->post['payment_payanyway_pending_order_status_id'] ?? 0),
|
||||
(int)($this->request->post['payment_payanyway_success_order_status_id'] ?? 0),
|
||||
(int)($this->request->post['payment_payanyway_error_order_status_id'] ?? 0),
|
||||
];
|
||||
if ([] !== array_intersect($this->request->post['payment_payanyway_canceled_order_status_ids'], array_diff($moduleStatuses, [0]))) {
|
||||
$this->errors['canceled_order_status'] = $this->language->get('error_canceled_order_status');
|
||||
}
|
||||
|
||||
$sortOrder = isset($this->request->post['payment_payanyway_sort_order'])
|
||||
? (int)$this->request->post['payment_payanyway_sort_order']
|
||||
: self::MODULE_DEFAULT_SORT_ORDER;
|
||||
$this->request->post['payment_payanyway_sort_order'] = $sortOrder;
|
||||
$this->request->post['payment_payanyway_cms_module_version'] = $this->getCmsModuleVersion();
|
||||
$this->request->post['payment_payanyway_cms_name'] = $this->getCmsName();
|
||||
|
||||
return empty($this->errors);
|
||||
}
|
||||
@@ -205,6 +375,7 @@ class PayAnyWay extends \Opencart\System\Engine\Controller
|
||||
$data['error_warning'] = $this->errors['warning'] ?? '';
|
||||
$data['error_account'] = $this->errors['account'] ?? '';
|
||||
$data['error_integrity_code'] = $this->errors['integrity_code'] ?? '';
|
||||
$data['error_receipt_email'] = $this->errors['receipt_email'] ?? '';
|
||||
$data['error_pending_order_status'] = $this->errors['pending_order_status'] ?? '';
|
||||
$data['error_success_order_status'] = $this->errors['success_order_status'] ?? '';
|
||||
$data['error_error_order_status'] = $this->errors['error_order_status'] ?? '';
|
||||
@@ -229,14 +400,57 @@ class PayAnyWay extends \Opencart\System\Engine\Controller
|
||||
];
|
||||
}
|
||||
|
||||
private function getIntegrityCode(): string
|
||||
private function hasIntegrityCode(): bool
|
||||
{
|
||||
$value = $this->retrieveValue('payment_payanyway_integrity_code');
|
||||
if (null === $value) {
|
||||
return '';
|
||||
return '' !== (string)$this->config->get('payment_payanyway_integrity_code');
|
||||
}
|
||||
|
||||
/** Строка с формы без экранирования OpenCart: htmlspecialchars_decode обращает ENT_COMPAT из system/library/request.php. */
|
||||
private function decodePostedString(string $name): string
|
||||
{
|
||||
$value = $this->request->post[$name] ?? '';
|
||||
|
||||
return is_string($value) ? trim(htmlspecialchars_decode($value, ENT_COMPAT)) : '';
|
||||
}
|
||||
|
||||
private function isValidReceiptEmail(string $email): bool
|
||||
{
|
||||
return ('' !== $email)
|
||||
&& (strlen($email) <= self::RECEIPT_EMAIL_MAX_LENGTH)
|
||||
&& (false !== filter_var($email, FILTER_VALIDATE_EMAIL));
|
||||
}
|
||||
|
||||
private function isFiscalizationEnabled(): bool
|
||||
{
|
||||
// Config::get() в OpenCart 4 для отсутствующего ключа возвращает '' — наличие через has().
|
||||
return !$this->config->has('payment_payanyway_fiscalization')
|
||||
|| (bool)$this->config->get('payment_payanyway_fiscalization');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
*/
|
||||
private function getCanceledOrderStatusIds(): array
|
||||
{
|
||||
if (!$this->config->has('payment_payanyway_canceled_order_status_ids')) {
|
||||
return self::DEFAULT_CANCELED_ORDER_STATUS_IDS;
|
||||
}
|
||||
|
||||
return htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
|
||||
return array_map('intval', (array)$this->config->get('payment_payanyway_canceled_order_status_ids'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $values
|
||||
* @return array<string, string>
|
||||
*/
|
||||
private function getOptions(array $values, string $labelPrefix): array
|
||||
{
|
||||
$result = [];
|
||||
foreach ($values as $value) {
|
||||
$result[$value] = $this->language->get($labelPrefix . $value);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/** @return array<string, mixed> */
|
||||
@@ -308,23 +522,13 @@ class PayAnyWay extends \Opencart\System\Engine\Controller
|
||||
return $this->model_localisation_geo_zone->getGeoZones();
|
||||
}
|
||||
|
||||
/** @return string */
|
||||
private function getCmsModuleVersion(): string
|
||||
private function getCmsName(): string
|
||||
{
|
||||
$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|PHP %s|%s v%s',
|
||||
$cmsName,
|
||||
$cmsVersion,
|
||||
PHP_VERSION,
|
||||
self::MODULE_NAME,
|
||||
self::MODULE_VERSION
|
||||
);
|
||||
return $this->detectCmsByPatterns($textFooter);
|
||||
}
|
||||
|
||||
private function detectCmsByPatterns($textFooter): string
|
||||
@@ -350,4 +554,11 @@ class PayAnyWay extends \Opencart\System\Engine\Controller
|
||||
{
|
||||
return $this->request->post[$name] ?? $this->config->get($name);
|
||||
}
|
||||
|
||||
private function retrieveValueOrDefault(string $name, string $default): string
|
||||
{
|
||||
$value = (string)$this->retrieveValue($name);
|
||||
|
||||
return ('' !== $value) ? $value : $default;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user