Files
OpenCart3/upload/admin/controller/extension/payment/payanyway.php
T

517 lines
18 KiB
PHP
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?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.5';
const MODULE_DEFAULT_SORT_ORDER = 0;
/** Маркер вставки OCMOD из install.xml в роутере витрины (приём Check/Pay URL методом POST). */
const OCMOD_ROUTER_MARKER = 'payanyway-post-route';
const OCMOD_ROUTER_FILE = 'catalog/controller/startup/router.php';
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'
);
/** Признак способа расчёта (kassaspecification.pdf, pm). */
private $paymentMethods = array(
'full_prepayment',
'prepayment',
'advance',
'full_payment',
'partial_payment',
'credit',
'credit_payment'
);
/** Признак предмета расчёта (kassaspecification.pdf, po). */
private $paymentObjects = array(
'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 $defaultCanceledOrderStatusIds = array(7, 9, 16);
const RECEIPT_EMAIL_MAX_LENGTH = 64;
const DEFAULT_PAYMENT_METHOD = 'full_payment';
const DEFAULT_PAYMENT_OBJECT = 'commodity';
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['warning_ocmod'] = $this->isRouterModificationApplied()
? ''
: sprintf(
$this->language->get('warning_ocmod_router'),
$this->url->link('marketplace/modification', 'user_token=' . $this->session->data['user_token'], true)
);
$data['warning_receipt_email'] = ($this->isFiscalizationEnabled() && ('' === (string)$this->config->get('payment_payanyway_receipt_email')))
? $this->language->get('warning_receipt_email')
: '';
$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_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->retrieveRawValue('payment_payanyway_receipt_email'), ENT_QUOTES, 'UTF-8');
$data['payment_payanyway_payment_methods'] = $this->getOptions($this->paymentMethods, '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($this->paymentObjects, '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();
$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['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['callback_url'] = HTTPS_CATALOG . 'index.php?route=extension/payment/payanyway/callback';
$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 bool
*/
private function isFiscalizationEnabled() {
$value = $this->retrieveValue('payment_payanyway_fiscalization');
return (null === $value) ? true : (bool)$value;
}
/**
* @return int[]
*/
private function getCanceledOrderStatusIds() {
$value = $this->retrieveValue('payment_payanyway_canceled_order_status_ids');
if (null === $value) {
return $this->defaultCanceledOrderStatusIds;
}
return array_map('intval', (array)$value);
}
/**
* @return bool
*/
private function isRouterModificationApplied() {
$file = DIR_MODIFICATION . self::OCMOD_ROUTER_FILE;
if (!is_file($file)) {
return false;
}
return false !== strpos((string)file_get_contents($file), self::OCMOD_ROUTER_MARKER);
}
/**
* @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_test_mode' => 0,
'payment_payanyway_fiscalization' => 1,
'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' => $this->defaultCanceledOrderStatusIds,
'payment_payanyway_sort_order' => 0,
'payment_payanyway_cms_name' => $this->getCmsName(),
);
$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']) ||
!preg_match('/^[0-9]{1,20}$/', (string)$request->post['payment_payanyway_account']) ||
((int)$request->post['payment_payanyway_account'] <= 0)
) {
$this->error['account'] = $this->language->get('error_account');
}
// OpenCart экранирует весь POST (htmlspecialchars в system/library/request.php): код с & " < >
// сохранился бы экранированным, и подписи не сошлись бы с MONETA.RU.
$integrityCode = $this->decodePostedString($request, 'payment_payanyway_integrity_code');
if ('' === $integrityCode) {
$this->error['integrity_code'] = $this->language->get('error_integrity_code');
}
$request->post['payment_payanyway_integrity_code'] = $integrityCode;
$fiscalization = !empty($request->post['payment_payanyway_fiscalization']);
$request->post['payment_payanyway_fiscalization'] = $fiscalization ? 1 : 0;
$request->post['payment_payanyway_test_mode'] = empty($request->post['payment_payanyway_test_mode']) ? 0 : 1;
$receiptEmail = $this->decodePostedString($request, 'payment_payanyway_receipt_email');
if (($fiscalization || ('' !== $receiptEmail)) && !$this->isValidReceiptEmail($receiptEmail)) {
$this->error['receipt_email'] = $this->language->get('error_receipt_email');
}
$request->post['payment_payanyway_receipt_email'] = $receiptEmail;
if (!isset($request->post['payment_payanyway_payment_method']) ||
!in_array($request->post['payment_payanyway_payment_method'], $this->paymentMethods, true)
) {
$request->post['payment_payanyway_payment_method'] = self::DEFAULT_PAYMENT_METHOD;
}
if (!isset($request->post['payment_payanyway_payment_object']) ||
!in_array($request->post['payment_payanyway_payment_object'], $this->paymentObjects, true)
) {
$request->post['payment_payanyway_payment_object'] = self::DEFAULT_PAYMENT_OBJECT;
}
$canceled = isset($request->post['payment_payanyway_canceled_order_status_ids'])
? (array)$request->post['payment_payanyway_canceled_order_status_ids']
: array();
$request->post['payment_payanyway_canceled_order_status_ids'] = array_values(array_unique(array_map('intval', $canceled)));
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_name'] = $this->getCmsName();
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_receipt_email'] = isset($this->error['receipt_email']) ? $this->error['receipt_email'] : '';
$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) {
return htmlspecialchars((string)$this->retrieveRawValue($key), ENT_QUOTES, 'UTF-8');
}
/**
* @param string $email
* @return bool
*/
private function isValidReceiptEmail($email) {
return ('' !== $email)
&& (strlen($email) <= self::RECEIPT_EMAIL_MAX_LENGTH)
&& (false !== filter_var($email, FILTER_VALIDATE_EMAIL));
}
/**
* @param string[] $values
* @param string $labelPrefix
* @return array<string, string>
*/
private function getOptions(array $values, $labelPrefix) {
$result = array();
foreach ($values as $value) {
$result[$value] = $this->language->get($labelPrefix . $value);
}
return $result;
}
/** @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 getCmsName() {
$this->load->language('common/footer');
$textFooter = $this->language->get('text_footer');
$textFooter = is_string($textFooter) ? $textFooter : '';
return $this->detectCmsByPatterns($textFooter);
}
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);
}
/**
* Строка с формы без экранирования OpenCart: htmlspecialchars_decode обращает ENT_COMPAT
* из system/library/request.php.
*
* @param \Request $request
* @param string $name
* @return string
*/
private function decodePostedString($request, $name) {
$value = isset($request->post[$name]) && is_string($request->post[$name]) ? $request->post[$name] : '';
return trim(htmlspecialchars_decode($value, ENT_COMPAT));
}
/**
* @param string $name
* @return string
*/
private function retrieveRawValue($name) {
if (isset($this->request->post[$name]) && is_string($this->request->post[$name])) {
return $this->request->post[$name];
}
return (string)$this->config->get($name);
}
/**
* @param string $name
* @param string $default
* @return string
*/
private function retrieveValueOrDefault($name, $default) {
$value = (string)$this->retrieveValue($name);
return ('' !== $value) ? $value : $default;
}
}