1276 lines
37 KiB
PHP
Executable File
1276 lines
37 KiB
PHP
Executable File
<?php
|
|
|
|
/**
|
|
* @property ModelCheckoutOrder $model_checkout_order
|
|
* @property ModelAccountOrder $model_account_order
|
|
* @property ModelLocalisationOrderStatus $model_localisation_order_status
|
|
*/
|
|
class ControllerExtensionPaymentPayanyway extends Controller {
|
|
const MODULE_NAME = 'payanyway';
|
|
const MODULE_VERSION = '1.0.4';
|
|
const CMS_NAME = 'OpenCart';
|
|
|
|
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 FAIL_RESPONSE = 'FAIL';
|
|
const XML_CONTENT_TYPE = 'application/xml';
|
|
const JSON_CONTENT_TYPE = 'application/json';
|
|
const TEXT_CONTENT_TYPE = 'text/plain; charset=UTF-8';
|
|
|
|
const TRANSACTION_ID_DELIMITER = '_';
|
|
const LEGACY_TRANSACTION_ID_DELIMITER = '|';
|
|
const TRANSACTION_ID_PATTERN = '/^([1-9][0-9]*)[%s]([0-9]{14})([A-Z0-9+-]{1,6})?$/';
|
|
const DESCRIPTION_MAX_LENGTH = 500;
|
|
|
|
const CLIENT_NAME_MAX_LENGTH = 256;
|
|
const CLIENT_EMAIL_MAX_LENGTH = 64;
|
|
const CLIENT_PHONE_MAX_LENGTH = 19;
|
|
const CHECK_CLIENT_PHONE_MAX_LENGTH = 13;
|
|
const ITEM_NAME_MAX_LENGTH = 128;
|
|
const DEFAULT_PAYMENT_METHOD = 'full_payment';
|
|
const DEFAULT_PAYMENT_OBJECT = 'commodity';
|
|
const SERVICE_PAYMENT_OBJECT = 'service';
|
|
const VOUCHER_PAYMENT_METHOD = 'advance';
|
|
const VOUCHER_PAYMENT_OBJECT = 'payment';
|
|
const PAYMENT_ITEM_NAME = 'Оплата заказа №';
|
|
const DEFAULT_MEASURE = 'unit';
|
|
|
|
const DEFAULT_CANCELED_ORDER_STATUS_IDS = '7,9,16';
|
|
|
|
const JSON_NUMBER_PREFIX = '__payanyway_number:';
|
|
|
|
/** @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 bool */
|
|
private $testMode;
|
|
|
|
/** @var bool */
|
|
private $fiscalization;
|
|
|
|
/** @var string «Email для чеков»: подставляется, если у покупателя нет ни email, ни телефона */
|
|
private $receiptEmail;
|
|
|
|
/** @var string */
|
|
private $paymentMethod;
|
|
|
|
/** @var string */
|
|
private $paymentObject;
|
|
|
|
/** @var int[] */
|
|
private $canceledOrderStatusIds;
|
|
|
|
/** @var string */
|
|
private $cmsName;
|
|
|
|
/** @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->getOrderIdFromSession();
|
|
$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;
|
|
|
|
$data['MNT_ID'] = $this->mntId;
|
|
$data['MNT_TRANSACTION_ID'] = $this->createTransactionId();
|
|
$data['MNT_AMOUNT'] = $this->getOrderAmount();
|
|
$data['MNT_CURRENCY_CODE'] = $this->currency;
|
|
$data['MNT_TEST_MODE'] = $this->testMode ? '1' : '0';
|
|
$data['MNT_DESCRIPTION'] = $this->getDescription();
|
|
$data['MNT_SUBSCRIBER_ID'] = $this->getSubscriberId();
|
|
|
|
$data['MNT_SIGNATURE'] = $this->buildFormSignature($data);
|
|
$data['MNT_SUCCESS_URL'] = $this->url->link('extension/payment/payanyway/success', '', true);
|
|
$data['MNT_FAIL_URL'] = $this->url->link('extension/payment/payanyway/fail', '', true);
|
|
$data['MNT_RETURN_URL'] = $this->url->link('checkout/checkout', '', true);
|
|
$data['moneta_locale'] = $this->language->get('text_locale');
|
|
$data['MNT_CMS'] = $this->getCms();
|
|
|
|
$data['btn_confirm'] = $this->language->get('text_paw_pay');
|
|
|
|
$data['confirm_url'] = $this->url->link('extension/payment/payanyway/confirm', '', true);
|
|
|
|
return $this->load->view('extension/payment/payanyway', $data);
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed> $data
|
|
* @return string
|
|
*/
|
|
private function buildFormSignature(array $data) {
|
|
return 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
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @return void
|
|
* @throws \Exception
|
|
*/
|
|
public function confirm() {
|
|
$json = array();
|
|
|
|
$this->load->language('extension/payment/payanyway');
|
|
|
|
$orderStatusId = isset($this->orderInfo['order_status_id']) ? (int)$this->orderInfo['order_status_id'] : null;
|
|
$paymentCode = isset($this->orderInfo['payment_code']) ? $this->orderInfo['payment_code'] : '';
|
|
if (self::MODULE_NAME !== $paymentCode) {
|
|
$json['success'] = false;
|
|
} else {
|
|
if ((null !== $orderStatusId) && ($orderStatusId !== $this->successOrderStatusId)) {
|
|
$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');
|
|
|
|
$orderStatusId = isset($this->orderInfo['order_status_id']) ? (int)$this->orderInfo['order_status_id'] : null;
|
|
if ((null !== $orderStatusId) && ($orderStatusId !== $this->successOrderStatusId)) {
|
|
$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');
|
|
|
|
$orderStatusId = isset($this->orderInfo['order_status_id']) ? (int)$this->orderInfo['order_status_id'] : null;
|
|
if ((null !== $orderStatusId) && ($orderStatusId !== $this->successOrderStatusId)) {
|
|
$this->addOrderHistory($this->errorOrderStatusId, $this->language->get('text_payment_error'));
|
|
}
|
|
|
|
$this->response->redirect($this->url->link('checkout/failure', '', true));
|
|
}
|
|
|
|
/**
|
|
* @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() {
|
|
return $this->orderId . self::TRANSACTION_ID_DELIMITER . date('YmdHisT');
|
|
}
|
|
|
|
/**
|
|
* @param float $price
|
|
* @return string
|
|
*/
|
|
private function formatPrice($price) {
|
|
return number_format($price, 2, '.', '');
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
private function getOrderAmount() {
|
|
return $this->formatPrice($this->toKopecks(isset($this->orderInfo['total']) ? $this->orderInfo['total'] : 0) / 100);
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
private function getCms() {
|
|
return sprintf('%s v%s_PHP %s_%s v%s',
|
|
$this->cmsName,
|
|
defined('VERSION') ? constant('VERSION') : 'unknown',
|
|
PHP_VERSION,
|
|
self::MODULE_NAME,
|
|
self::MODULE_VERSION
|
|
);
|
|
}
|
|
|
|
/** @return string */
|
|
private function getDescription() {
|
|
$orderId = isset($this->orderInfo['order_id']) ? $this->orderInfo['order_id'] : null;
|
|
$description = $this->language->get('text_description');
|
|
if (!empty($orderId)) {
|
|
$description .= ' ' . sprintf($this->language->get('text_description_number'), $orderId);
|
|
}
|
|
|
|
$clientName = $this->getClientName();
|
|
if (null !== $clientName) {
|
|
$description .= ' ' . sprintf($this->language->get('text_description_customer'), $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 = $this->getClientEmail();
|
|
if (null !== $email) {
|
|
return $email;
|
|
}
|
|
|
|
$phone = $this->getClientPhone();
|
|
|
|
return (null !== $phone) ? $phone : '';
|
|
}
|
|
|
|
/**
|
|
* @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() {
|
|
try {
|
|
$callback = $this->getCallbackData($this->getCallbackRequestData());
|
|
} catch (\InvalidArgumentException $exception) {
|
|
$this->log->write('PayAnyWay callback: ' . $exception->getMessage());
|
|
$this->sendResponse(self::FAIL_RESPONSE);
|
|
}
|
|
|
|
$this->validateCallback($callback);
|
|
|
|
$isCheck = 'CHECK' === $callback['MNT_COMMAND'];
|
|
if ($isCheck && !$this->checkSignature($callback, $callback['MNT_SIGNATURE'])) {
|
|
$this->sendResponse(self::FAIL_RESPONSE);
|
|
}
|
|
|
|
$this->orderId = $this->parseOrderIdFromTransactionId($callback['MNT_TRANSACTION_ID']);
|
|
if (null === $this->orderId) {
|
|
$this->log->write('PayAnyWay callback: MNT_TRANSACTION_ID has unexpected format');
|
|
$this->sendResponse(self::FAIL_RESPONSE);
|
|
}
|
|
|
|
$this->orderInfo = $this->getOrderInfo();
|
|
if (empty($this->orderInfo)) {
|
|
$this->sendResponse($isCheck ? $this->buildUnknownOrderResponse($callback) : self::FAIL_RESPONSE);
|
|
}
|
|
|
|
$isCheck ? $this->handleCheckCallback($callback) : $this->handlePayCallback($callback);
|
|
}
|
|
|
|
/**
|
|
* @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 array<string, mixed>
|
|
*/
|
|
private function getCallbackRequestData() {
|
|
return array_merge($_GET, $_POST);
|
|
}
|
|
|
|
/**
|
|
* @param mixed $transactionId
|
|
* @return int|null
|
|
*/
|
|
private function parseOrderIdFromTransactionId($transactionId) {
|
|
if (!is_string($transactionId)) {
|
|
return null;
|
|
}
|
|
|
|
$delimiters = preg_quote(self::TRANSACTION_ID_DELIMITER . self::LEGACY_TRANSACTION_ID_DELIMITER, '/');
|
|
if (!preg_match(sprintf(self::TRANSACTION_ID_PATTERN, $delimiters), $transactionId, $matches)) {
|
|
return null;
|
|
}
|
|
|
|
return (int)$matches[1];
|
|
}
|
|
|
|
/** @return int|null */
|
|
private function getOrderIdFromSession() {
|
|
return isset($this->session->data['order_id']) ? (int)$this->session->data['order_id'] : null;
|
|
}
|
|
|
|
/**
|
|
* @param array|string $response
|
|
* @param int $statusCode
|
|
* @return void
|
|
*/
|
|
private function sendResponse($response, $statusCode = 200)
|
|
{
|
|
http_response_code($statusCode);
|
|
if (is_string($response)) {
|
|
header('Content-Type: ' . self::TEXT_CONTENT_TYPE);
|
|
echo $response;
|
|
} else {
|
|
header('Content-Type: ' . $response['contentType']);
|
|
echo $response['data'];
|
|
}
|
|
|
|
exit();
|
|
}
|
|
|
|
/**
|
|
* @param array $callback
|
|
* @return void
|
|
*/
|
|
private function validateCallback(array $callback)
|
|
{
|
|
if (empty($this->mntId) || empty($this->integrityCode)) {
|
|
$this->sendResponse(self::FAIL_RESPONSE);
|
|
}
|
|
|
|
if ($this->mntId !== (int)$callback['MNT_ID']) {
|
|
$this->sendResponse(self::FAIL_RESPONSE);
|
|
}
|
|
|
|
if ($callback['MNT_TEST_MODE'] !== ($this->testMode ? '1' : '0')) {
|
|
$this->log->write('PayAnyWay callback: MNT_TEST_MODE does not match module settings');
|
|
$this->sendResponse(self::FAIL_RESPONSE);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @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, 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) {
|
|
list($resultCode, $description) = $this->determineCheckResult($callbackData);
|
|
$responseData = $this->buildBaseResponseData($callbackData, $resultCode, $description);
|
|
|
|
if ($this->fiscalization) {
|
|
$responseData['client'] = $this->getClientInfo(true);
|
|
$responseData['items'] = $this->getCheckItems($this->getReceiptLines());
|
|
|
|
$this->sendResponse($this->buildJsonResponse($responseData));
|
|
}
|
|
|
|
$this->sendResponse($this->buildXmlResponse($responseData + $this->getResponseAttributes()));
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed> $callbackData
|
|
* @return void
|
|
* @throws \Exception
|
|
*/
|
|
private function handlePayCallback(array $callbackData) {
|
|
$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->getOrderAmount(),
|
|
'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(self::FAIL_RESPONSE);
|
|
}
|
|
|
|
$isPayedOrder = $this->isOrderPaid();
|
|
if (!$isPayedOrder) {
|
|
$this->load->language('extension/payment/payanyway');
|
|
$this->addOrderHistory($this->successOrderStatusId, $this->getPaymentCompletedComment($callbackData['MNT_OPERATION_ID']));
|
|
}
|
|
|
|
$resultCode = 200;
|
|
$description = $isPayedOrder ? 'Order already paid' : 'Order success paid';
|
|
$responseData = $this->buildBaseResponseData($callbackData, $resultCode, $description);
|
|
|
|
$this->sendResponse($this->buildXmlResponse($responseData + $this->getResponseAttributes()));
|
|
}
|
|
|
|
/**
|
|
* @param string $operationId
|
|
* @return string
|
|
*/
|
|
private function getPaymentCompletedComment($operationId) {
|
|
$comment = $this->language->get('text_payment_completed');
|
|
if (preg_match('/^[0-9]{1,20}$/', $operationId)) {
|
|
$comment .= '. ' . sprintf($this->language->get('text_payment_operation'), $operationId);
|
|
}
|
|
|
|
return $comment;
|
|
}
|
|
|
|
/**
|
|
* @return array<string, string|null>
|
|
* @throws \Exception
|
|
*/
|
|
private function getResponseAttributes() {
|
|
$inventory = $this->getInventoryJson($this->getReceiptLines());
|
|
$client = $this->getClientInfo($this->fiscalization);
|
|
|
|
return array(
|
|
'inventory' => $inventory ?: null,
|
|
'client' => $client ? json_encode(array($client), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) : null,
|
|
'customer' => isset($client['email']) ? $client['email'] : null,
|
|
'phone' => isset($client['phone']) ? ltrim($client['phone'], '+') : null,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @param array $callbackData
|
|
* @param int $resultCode
|
|
* @param string $description
|
|
* @return array<string, mixed>
|
|
* @throws \Exception
|
|
*/
|
|
private function buildBaseResponseData(array $callbackData, $resultCode, $description) {
|
|
$data = array(
|
|
'MNT_ID' => $this->mntId,
|
|
'MNT_TRANSACTION_ID' => $callbackData['MNT_TRANSACTION_ID'],
|
|
'MNT_RESULT_CODE' => $resultCode,
|
|
'MNT_DESCRIPTION' => $this->validateString($description, self::DESCRIPTION_MAX_LENGTH),
|
|
'MNT_AMOUNT' => $this->getOrderAmount(),
|
|
'MNT_CURRENCY_CODE' => $callbackData['MNT_CURRENCY_CODE'],
|
|
'MNT_CMS' => $this->getCms(),
|
|
);
|
|
|
|
$signatureSource = $resultCode . $this->mntId . $callbackData['MNT_TRANSACTION_ID'] . $this->integrityCode;
|
|
$data['MNT_SIGNATURE'] = md5($signatureSource);
|
|
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed> $callbackData
|
|
* @return array
|
|
* @throws \Exception
|
|
*/
|
|
private function buildUnknownOrderResponse(array $callbackData) {
|
|
$responseData = $this->buildBaseResponseData($callbackData, 500, 'Order not found');
|
|
unset($responseData['MNT_AMOUNT']);
|
|
|
|
return $this->buildXmlResponse($responseData, false);
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed> $callbackData
|
|
* @return array<int|string>
|
|
* @throws \Exception
|
|
*/
|
|
private function determineCheckResult(array $callbackData) {
|
|
$orderStatusId = isset($this->orderInfo['order_status_id']) ? (int)$this->orderInfo['order_status_id'] : null;
|
|
if (null === $orderStatusId) {
|
|
return array(500, 'Order status not set');
|
|
}
|
|
$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, $this->canceledOrderStatusIds, true)) {
|
|
return array(500, "Order status is {$orderStatus}");
|
|
}
|
|
|
|
return array(402, 'Order created, but not paid');
|
|
}
|
|
|
|
/** @return bool */
|
|
private function isOrderPaid() {
|
|
if (!isset($this->orderInfo['order_status_id'])) {
|
|
return false;
|
|
}
|
|
|
|
return $this->successOrderStatusId === (int)$this->orderInfo['order_status_id'];
|
|
}
|
|
|
|
/**
|
|
* @return array<int, array{name: string, unit: int, quantity: int, pm: string, po: string, goods: bool}> цены в копейках
|
|
* @throws \Exception
|
|
*/
|
|
private function getReceiptLines() {
|
|
$target = $this->toKopecks(isset($this->orderInfo['total']) ? $this->orderInfo['total'] : 0);
|
|
if ($target <= 0) {
|
|
return array();
|
|
}
|
|
|
|
$lines = $this->balanceReceiptLines($this->collectReceiptLines(), $target);
|
|
if (!empty($lines)) {
|
|
return $lines;
|
|
}
|
|
|
|
return array(
|
|
array(
|
|
'name' => self::PAYMENT_ITEM_NAME . $this->orderId,
|
|
'unit' => $target,
|
|
'quantity' => 1,
|
|
'pm' => $this->paymentMethod,
|
|
'po' => $this->paymentObject,
|
|
'goods' => true
|
|
),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @return array<int, array{name: string, unit: int, quantity: int, pm: string, po: string, goods: bool}>
|
|
* @throws \Exception
|
|
*/
|
|
private function collectReceiptLines() {
|
|
$this->load->model('account/order');
|
|
|
|
$lines = array();
|
|
foreach ($this->model_account_order->getOrderProducts($this->orderId) as $product) {
|
|
$unit = $this->toKopecks((float)$product['price'] + (float)$product['tax']);
|
|
$quantity = (int)$product['quantity'];
|
|
if (($unit > 0) && ($quantity > 0)) {
|
|
$lines[] = array(
|
|
'name' => $product['name'],
|
|
'unit' => $unit,
|
|
'quantity' => $quantity,
|
|
'pm' => $this->paymentMethod,
|
|
'po' => $this->paymentObject,
|
|
'goods' => true
|
|
);
|
|
}
|
|
}
|
|
|
|
foreach ($this->model_account_order->getOrderVouchers($this->orderId) as $voucher) {
|
|
$unit = $this->toKopecks($voucher['amount']);
|
|
if ($unit > 0) {
|
|
$lines[] = array(
|
|
'name' => $voucher['description'],
|
|
'unit' => $unit,
|
|
'quantity' => 1,
|
|
'pm' => self::VOUCHER_PAYMENT_METHOD,
|
|
'po' => self::VOUCHER_PAYMENT_OBJECT,
|
|
'goods' => false
|
|
);
|
|
}
|
|
}
|
|
|
|
foreach ($this->model_account_order->getOrderTotals($this->orderId) as $orderTotal) {
|
|
if (in_array($orderTotal['code'], array('sub_total', 'tax', 'total'), true)) {
|
|
continue;
|
|
}
|
|
|
|
$unit = $this->toKopecks($orderTotal['value']);
|
|
if ($unit > 0) {
|
|
$lines[] = array(
|
|
'name' => $orderTotal['title'],
|
|
'unit' => $unit,
|
|
'quantity' => 1,
|
|
'pm' => $this->paymentMethod,
|
|
'po' => self::SERVICE_PAYMENT_OBJECT,
|
|
'goods' => false
|
|
);
|
|
}
|
|
}
|
|
|
|
return $lines;
|
|
}
|
|
|
|
/**
|
|
* @param array<int, array{name: string, unit: int, quantity: int, pm: string, po: string, goods: bool}> $lines
|
|
* @param int $target сумма заказа в копейках
|
|
* @return array<int, array{name: string, unit: int, quantity: int, pm: string, po: string, goods: bool}>
|
|
*/
|
|
private function balanceReceiptLines(array $lines, $target) {
|
|
if (empty($lines)) {
|
|
return array();
|
|
}
|
|
|
|
$totals = array();
|
|
foreach ($lines as $index => $line) {
|
|
$totals[$index] = $line['unit'] * $line['quantity'];
|
|
}
|
|
if (array_sum($totals) === $target) {
|
|
return $lines;
|
|
}
|
|
|
|
foreach (array(true, false) as $goodsOnly) {
|
|
$adjustable = array();
|
|
$fixed = 0;
|
|
foreach ($lines as $index => $line) {
|
|
if (!$goodsOnly || $line['goods']) {
|
|
$adjustable[] = $index;
|
|
} else {
|
|
$fixed += $totals[$index];
|
|
}
|
|
}
|
|
if (empty($adjustable) || ($target - $fixed <= 0)) {
|
|
continue;
|
|
}
|
|
|
|
return $this->distributeReceiptLines($lines, $totals, $adjustable, $target - $fixed);
|
|
}
|
|
|
|
return array();
|
|
}
|
|
|
|
/**
|
|
* Разнос суммы $goal по строкам $adjustable пропорционально их сумме, методом наибольших
|
|
* остатков: доли округляются вниз, недостающие копейки — строкам с большим остатком.
|
|
* Целочисленное деление без intdiv() — модуль работает на PHP 5.6.
|
|
*
|
|
* @param array<int, array{name: string, unit: int, quantity: int, pm: string, po: string, goods: bool}> $lines
|
|
* @param array<int, int> $totals суммы строк в копейках
|
|
* @param array<int, int> $adjustable индексы строк, которые можно менять
|
|
* @param int $goal
|
|
* @return array<int, array{name: string, unit: int, quantity: int, pm: string, po: string, goods: bool}>
|
|
*/
|
|
private function distributeReceiptLines(array $lines, array $totals, array $adjustable, $goal) {
|
|
$base = 0;
|
|
foreach ($adjustable as $index) {
|
|
$base += $totals[$index];
|
|
}
|
|
if ($base <= 0) {
|
|
return array();
|
|
}
|
|
|
|
$shares = array();
|
|
$remainders = array();
|
|
foreach ($adjustable as $index) {
|
|
if ($totals[$index] > (int)(PHP_INT_MAX / $goal)) {
|
|
return array();
|
|
}
|
|
|
|
$product = $totals[$index] * $goal;
|
|
$remainders[$index] = $product % $base;
|
|
$shares[$index] = ($product - $remainders[$index]) / $base;
|
|
}
|
|
arsort($remainders);
|
|
$left = $goal - array_sum($shares);
|
|
foreach (array_keys($remainders) as $index) {
|
|
if ($left <= 0) {
|
|
break;
|
|
}
|
|
$shares[$index]++;
|
|
$left--;
|
|
}
|
|
|
|
$balanced = array();
|
|
foreach ($lines as $index => $line) {
|
|
if (!isset($shares[$index])) {
|
|
$balanced[] = $line;
|
|
continue;
|
|
}
|
|
|
|
$parts = $this->splitReceiptLine($line, $shares[$index]);
|
|
if (empty($parts)) {
|
|
return array();
|
|
}
|
|
foreach ($parts as $part) {
|
|
$balanced[] = $part;
|
|
}
|
|
}
|
|
|
|
return $balanced;
|
|
}
|
|
|
|
/**
|
|
* Строка с новой суммой: целая цена за штуку, остаток копеек — второй строкой той же
|
|
* позиции с ценой на копейку выше. Нулевые части отбрасываются.
|
|
*
|
|
* @param array{name: string, unit: int, quantity: int, pm: string, po: string, goods: bool} $line
|
|
* @param int $total
|
|
* @return array<int, array{name: string, unit: int, quantity: int, pm: string, po: string, goods: bool}>
|
|
*/
|
|
private function splitReceiptLine(array $line, $total) {
|
|
$pieces = $line['quantity'];
|
|
$rest = $total % $pieces;
|
|
$unit = ($total - $rest) / $pieces;
|
|
|
|
$parts = array();
|
|
if ($rest > 0) {
|
|
$parts[] = array('unit' => $unit + 1, 'quantity' => $rest) + $line;
|
|
}
|
|
if (($unit > 0) && ($pieces - $rest > 0)) {
|
|
$parts[] = array('unit' => $unit, 'quantity' => $pieces - $rest) + $line;
|
|
}
|
|
|
|
return $parts;
|
|
}
|
|
|
|
/**
|
|
* @param float|string $amount
|
|
* @return int
|
|
*/
|
|
private function toKopecks($amount) {
|
|
$currencyValue = isset($this->orderInfo['currency_value']) ? (float)$this->orderInfo['currency_value'] : 0.0;
|
|
if ($currencyValue <= 0) {
|
|
$currencyValue = 1.0;
|
|
}
|
|
|
|
return (int)round((float)$amount * $currencyValue * 100);
|
|
}
|
|
|
|
/**
|
|
* @param array<int, array{name: string, unit: int, quantity: int, pm: string, po: string, goods: bool}> $lines
|
|
* @return false|string
|
|
*/
|
|
private function getInventoryJson(array $lines) {
|
|
if (empty($lines)) {
|
|
return false;
|
|
}
|
|
|
|
$inventory = array();
|
|
foreach ($lines as $line) {
|
|
$position = array(
|
|
'name' => $this->validateString($line['name'], self::ITEM_NAME_MAX_LENGTH),
|
|
'price' => $this->formatPrice($line['unit'] / 100),
|
|
'quantity' => $this->formatQuantity($line['quantity']),
|
|
'measure' => self::DEFAULT_MEASURE,
|
|
);
|
|
|
|
if ($this->fiscalization) {
|
|
$position += array(
|
|
'vatTag' => $this->vatRate,
|
|
'pm' => $line['pm'],
|
|
'po' => $line['po'],
|
|
);
|
|
}
|
|
$inventory[] = $position;
|
|
}
|
|
|
|
$name = isset($deliveryInfo['title']) ? $deliveryInfo['title'] : '';
|
|
$price = isset($deliveryInfo['value']) ? (float)$deliveryInfo['value'] : 0;
|
|
$inventory[] = array(
|
|
'name' => $this->validateString($name, self::ITEM_NAME_MAX_LENGTH),
|
|
'price' => $this->formatPrice($price),
|
|
'quantity' => '1',
|
|
'vatTag' => $this->vatRate,
|
|
'pm' => self::DEFAULT_PAYMENT_METHOD,
|
|
'po' => self::SHIPPING_PAYMENT_OBJECT,
|
|
'measure' => self::DEFAULT_MEASURE,
|
|
);
|
|
|
|
return json_encode($inventory, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
|
}
|
|
|
|
/**
|
|
* Покупатель в чеке: хотя бы один из email/phone, ФИО — только вместе с контактом.
|
|
* Нет ни email, ни телефона — «Email для чеков» (если разрешено и задан), иначе пусто.
|
|
*
|
|
* @param bool $withReceiptEmail
|
|
* @return array<string, string>
|
|
*/
|
|
private function getClientInfo($withReceiptEmail) {
|
|
$email = $this->getClientEmail();
|
|
$phoneNumber = $this->getClientPhone();
|
|
if (
|
|
(null === $email)
|
|
&& (null === $phoneNumber)
|
|
&& $withReceiptEmail
|
|
&& ('' !== $this->receiptEmail)
|
|
) {
|
|
$email = $this->receiptEmail;
|
|
}
|
|
if ((null === $email) && (null === $phoneNumber)) {
|
|
return array();
|
|
}
|
|
|
|
$clientInfo = array();
|
|
$name = $this->getClientName();
|
|
if (null !== $name) {
|
|
$clientInfo['name'] = $name;
|
|
}
|
|
if (null !== $email) {
|
|
$clientInfo['email'] = $email;
|
|
}
|
|
if (null !== $phoneNumber) {
|
|
$clientInfo['phone'] = $phoneNumber;
|
|
}
|
|
|
|
return $clientInfo;
|
|
}
|
|
|
|
/**
|
|
* @param array<int, array{name: string, unit: int, quantity: int, pm: string, po: string, goods: bool}> $lines
|
|
* @return array
|
|
*/
|
|
private function getCheckItems(array $lines) {
|
|
$checkItems = array();
|
|
foreach ($lines as $line) {
|
|
$checkItems[] = array(
|
|
'name' => $this->validateString($line['name'], self::ITEM_NAME_MAX_LENGTH),
|
|
'price' => $this->jsonNumber($line['unit']),
|
|
'quantity' => $line['quantity'],
|
|
'vat' => $this->getVat($this->vatRate),
|
|
'measure' => self::DEFAULT_MEASURE,
|
|
'paymentMethod' => $line['pm'],
|
|
'paymentObject' => $line['po'],
|
|
);
|
|
}
|
|
|
|
return $checkItems;
|
|
}
|
|
|
|
/**
|
|
* @param string $vatRate
|
|
* @return string
|
|
*/
|
|
public function getVat($vatRate) {
|
|
$map = array(
|
|
'1104' => 'vat0',
|
|
'1108' => 'vat5',
|
|
'1109' => 'vat7',
|
|
'1103' => 'vat10',
|
|
'1102' => 'vat20',
|
|
'1113' => 'vat22',
|
|
'1110' => 'vat105',
|
|
'1111' => 'vat107',
|
|
'1107' => 'vat110',
|
|
'1106' => 'vat120',
|
|
'1114' => 'vat122',
|
|
);
|
|
|
|
return isset($map[$vatRate]) ? $map[$vatRate] : 'none';
|
|
}
|
|
|
|
/**
|
|
* @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();
|
|
|
|
foreach ($orderStatuses as $orderStatus) {
|
|
$statusId = isset($orderStatus['order_status_id']) ? (int)$orderStatus['order_status_id'] : null;
|
|
if ($statusId === $orderStatusId) {
|
|
return $orderStatus['name'];
|
|
}
|
|
}
|
|
|
|
return 'unknown';
|
|
}
|
|
|
|
/**
|
|
* @param float $quantity
|
|
* @return string
|
|
*/
|
|
private function formatQuantity($quantity) {
|
|
$quantityString = number_format($quantity, 3, '.', '');
|
|
return rtrim(rtrim($quantityString, '0'), '.');
|
|
}
|
|
|
|
/**
|
|
* @param array<non-empty-string, mixed> $data
|
|
* @return array
|
|
* @throws \JsonException
|
|
*/
|
|
public function buildJsonResponse(array $data) {
|
|
$client = $data['client'];
|
|
if (isset($client['phone']) && (strlen($client['phone']) > self::CHECK_CLIENT_PHONE_MAX_LENGTH)) {
|
|
unset($client['phone']);
|
|
}
|
|
if (!isset($client['email']) && !isset($client['phone'])) {
|
|
$client = array();
|
|
}
|
|
|
|
$jsonData = array(
|
|
'id' => (string)$data['MNT_ID'],
|
|
'transactionId' => $data['MNT_TRANSACTION_ID'],
|
|
'amount' => self::JSON_NUMBER_PREFIX . $data['MNT_AMOUNT'],
|
|
'signature' => $data['MNT_SIGNATURE'],
|
|
'resultCode' => (string)$data['MNT_RESULT_CODE'],
|
|
'description' => $data['MNT_DESCRIPTION'],
|
|
'externalId' => $data['MNT_TRANSACTION_ID'],
|
|
'cms' => $data['MNT_CMS'],
|
|
'receipt' => array(
|
|
'client' => (object)$client,
|
|
'items' => $data['items'],
|
|
),
|
|
);
|
|
|
|
$json = json_encode($jsonData, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
|
if (false !== $json) {
|
|
$json = preg_replace('/"' . preg_quote(self::JSON_NUMBER_PREFIX, '/') . '([0-9]+\.[0-9]{2})"/', '$1', $json);
|
|
}
|
|
|
|
return array(
|
|
'data' => $json ?: '',
|
|
'contentType' => self::JSON_CONTENT_TYPE,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @param int $kopecks
|
|
* @return string
|
|
*/
|
|
private function jsonNumber($kopecks) {
|
|
return self::JSON_NUMBER_PREFIX . $this->formatPrice($kopecks / 100);
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed> $data
|
|
* @param bool $withAttributes
|
|
* @return array
|
|
* @throws \Exception
|
|
*/
|
|
private function buildXmlResponse(array $data, $withAttributes = true) {
|
|
$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',
|
|
'MNT_CMS',
|
|
);
|
|
|
|
foreach ($requiredFields as $field) {
|
|
if (isset($data[$field]) && ($data[$field] !== '')) {
|
|
$root->appendChild($dom->createElement($field, (string)$data[$field]));
|
|
}
|
|
}
|
|
|
|
if (!$withAttributes) {
|
|
return $this->saveXmlResponse($dom);
|
|
}
|
|
|
|
$attributes = $dom->createElement('MNT_ATTRIBUTES');
|
|
$root->appendChild($attributes);
|
|
|
|
$attributeMap = array(
|
|
'INVENTORY' => isset($data['inventory']) ? $data['inventory'] : null,
|
|
'CLIENT' => isset($data['client']) ? $data['client'] : null,
|
|
'CUSTOMER' => isset($data['customer']) ? $data['customer'] : null,
|
|
'PHONE' => isset($data['phone']) ? $data['phone'] : 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 $this->saveXmlResponse($dom);
|
|
}
|
|
|
|
/**
|
|
* @param \DOMDocument $dom
|
|
* @return array
|
|
*/
|
|
private function saveXmlResponse(\DOMDocument $dom) {
|
|
$xml = $dom->saveXML();
|
|
|
|
return array(
|
|
'data' => (false !== $xml) ? $xml : self::FAIL_RESPONSE,
|
|
'contentType' => (false !== $xml) ? self::XML_CONTENT_TYPE : self::TEXT_CONTENT_TYPE,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @return non-empty-string|null
|
|
*/
|
|
private function getClientName()
|
|
{
|
|
$firstname = isset($this->orderInfo['firstname']) ? $this->orderInfo['firstname'] : '';
|
|
$lastname = isset($this->orderInfo['lastname']) ? $this->orderInfo['lastname'] : '';
|
|
|
|
$name = trim($firstname . ' ' . $lastname);
|
|
|
|
return ('' !== $name) ? $this->validateString($name, self::CLIENT_NAME_MAX_LENGTH) : null;
|
|
}
|
|
|
|
/**
|
|
* @return non-empty-string|null
|
|
*/
|
|
private function getClientEmail()
|
|
{
|
|
$email = isset($this->orderInfo['email']) ? $this->orderInfo['email'] : '';
|
|
|
|
return $this->normalizeEmail($email);
|
|
}
|
|
|
|
/**
|
|
* @return non-empty-string|null
|
|
*/
|
|
private function getClientPhone()
|
|
{
|
|
$telephone = isset($this->orderInfo['telephone']) ? $this->orderInfo['telephone'] : '';
|
|
$digits = preg_replace('/\D/', '', trim((string)$telephone));
|
|
|
|
return (('' !== $digits) && (strlen($digits) + 1 <= self::CLIENT_PHONE_MAX_LENGTH)) ? '+' . $digits : null;
|
|
}
|
|
|
|
/**
|
|
* @param mixed $email
|
|
* @return string|null
|
|
*/
|
|
private function normalizeEmail($email) {
|
|
$email = trim((string)$email);
|
|
if (
|
|
('' === $email)
|
|
|| (strlen($email) > self::CLIENT_EMAIL_MAX_LENGTH)
|
|
|| (false === filter_var($email, FILTER_VALIDATE_EMAIL))
|
|
) {
|
|
return null;
|
|
}
|
|
|
|
return $email;
|
|
}
|
|
|
|
/**
|
|
* @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->testMode = (bool)$this->config->get('payment_payanyway_test_mode');
|
|
|
|
$fiscalization = $this->config->get('payment_payanyway_fiscalization');
|
|
$this->fiscalization = (null === $fiscalization) ? true : (bool)$fiscalization;
|
|
$receiptEmail = $this->normalizeEmail($this->config->get('payment_payanyway_receipt_email'));
|
|
$this->receiptEmail = (null !== $receiptEmail) ? $receiptEmail : '';
|
|
|
|
$paymentMethod = (string)$this->config->get('payment_payanyway_payment_method');
|
|
$this->paymentMethod = ('' !== $paymentMethod) ? $paymentMethod : self::DEFAULT_PAYMENT_METHOD;
|
|
$paymentObject = (string)$this->config->get('payment_payanyway_payment_object');
|
|
$this->paymentObject = ('' !== $paymentObject) ? $paymentObject : self::DEFAULT_PAYMENT_OBJECT;
|
|
|
|
$canceled = $this->config->get('payment_payanyway_canceled_order_status_ids');
|
|
if (null === $canceled) {
|
|
$canceled = explode(',', self::DEFAULT_CANCELED_ORDER_STATUS_IDS);
|
|
}
|
|
$this->canceledOrderStatusIds = array_map('intval', (array)$canceled);
|
|
|
|
$this->cmsName = $this->detectCmsName();
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
private function detectCmsName() {
|
|
$cmsName = (string)$this->config->get('payment_payanyway_cms_name');
|
|
if ('' !== $cmsName) {
|
|
return $cmsName;
|
|
}
|
|
|
|
$legacy = (string)$this->config->get('payment_payanyway_cms_module_version');
|
|
if (preg_match('/^(.+?) v/', $legacy, $matches)) {
|
|
return $matches[1];
|
|
}
|
|
|
|
return self::CMS_NAME;
|
|
}
|
|
|
|
/**
|
|
* @return array
|
|
* @throws \Exception
|
|
*/
|
|
public function getAccountOrderProducts() {
|
|
$this->load->model('account/order');
|
|
return $this->model_account_order->getOrderProducts($this->orderId);
|
|
}
|
|
}
|