Merge branch 'master' into release

# Conflicts:
#	README.md
#	modules/gateways/callback/payanyway.php
#	modules/gateways/payanyway.php
This commit is contained in:
Ivan Sadovin 2026-05-18 11:30:38 +03:00
commit c3f7257472
3 changed files with 399 additions and 332 deletions

View File

@ -1,6 +1,5 @@
# 🌟 Платёжный модуль PayAnyWay для WHMCS # 🌟 Платёжный модуль PayAnyWay для WHMCS
![WHMCS](https://img.shields.io/badge/WHMCS-Gateway%20Module-0070C0?logo=php&logoColor=white) ![WHMCS](https://img.shields.io/badge/WHMCS-Gateway%20Module-0070C0?logo=php&logoColor=white)
![PHP](https://img.shields.io/badge/PHP-5.6%2B-purple) ![PHP](https://img.shields.io/badge/PHP-5.6%2B-purple)
![License](https://img.shields.io/badge/License-MIT-green) ![License](https://img.shields.io/badge/License-MIT-green)
@ -12,27 +11,28 @@ WHMCS предоставляет мощный функционал, которы
> Данный модуль работает **только с валютой RUB (Российский рубль)**. > Данный модуль работает **только с валютой RUB (Российский рубль)**.
> Перед активацией модуля убедитесь, что в WHMCS в качестве базовой валюты установлен RUB, либо у пользователя выбран счёт в RUB. Использование других валют не поддерживается. > Перед активацией модуля убедитесь, что в WHMCS в качестве базовой валюты установлен RUB, либо у пользователя выбран счёт в RUB. Использование других валют не поддерживается.
> **📦 Версия модуля: 1.1.0** > **📦 Версия модуля: 1.0.0**
## 🏗️ Структура модуля ## 🏗️ Структура модуля
``` ```
└── modules ├── modules
└── gateways │ └── gateways
├── callback │ ├── callback
│ └── payanyway.php │ │ └── payanyway.php
└── payanyway.php │ └── payanyway.php
└── README.md
``` ```
## 🚀 Установка и настройка модуля ## 🚀 Установка и настройка модуля
1. 📥 [Скачайте архив модуля](https://www.payanyway.ru/info/public/cms/whmcs.zip) и распакуйте его. 1. 📥 [Скачайте архив модуля](https://git.pub.moneta.ru/CMS/WHMCS/releases) и распакуйте его.
2. 📂 Скопируйте содержимое архива в корень сайта. 2. 📂 Скопируйте содержимое архива в корень сайта.
3. ⚙️ В списке модулей оплаты WHMCS найдите **PayAnyWay** и активируйте его. 3. ⚙️ В списке модулей оплаты WHMCS найдите **PayAnyWay** и активируйте его.
4. ✏️ Заполните соответствующие поля в настройках способа оплаты: 4. ✏️ Заполните соответствующие поля в настройках способа оплаты:
| Поле | Значение | | Поле | Значение |
|------|----------| |-----------------------------------------|----------------------------------------------------------------|
| **Show on Order Form** | ✅ Поставить галочку | | **Show on Order Form** | ✅ Поставить галочку |
| **Display Name** | `PayAnyWay` | | **Display Name** | `PayAnyWay` |
| **Account Number** | Номер расширенного счета в PayAnyWay (Moneta) | | **Account Number** | Номер расширенного счета в PayAnyWay (Moneta) |
@ -48,7 +48,7 @@ WHMCS предоставляет мощный функционал, которы
2. ⚙️ Заполните настройки расширенного счета (раздел **«Мой счет» → «Управление счетами» → «Редактировать счет»**): 2. ⚙️ Заполните настройки расширенного счета (раздел **«Мой счет» → «Управление счетами» → «Редактировать счет»**):
| Параметр | Значение | | Параметр | Значение |
|----------|----------| |------------------------------------------|------------------------------------------------------------------|
| **Тестовый режим** | `Нет` | | **Тестовый режим** | `Нет` |
| **Check URL** | `https://your_site_name/modules/gateways/callback/payanyway.php` | | **Check URL** | `https://your_site_name/modules/gateways/callback/payanyway.php` |
| **Pay URL** | `https://your_site_name/modules/gateways/callback/payanyway.php` | | **Pay URL** | `https://your_site_name/modules/gateways/callback/payanyway.php` |

View File

@ -16,26 +16,20 @@ require_once __DIR__ . '/../../../includes/invoicefunctions.php';
require_once __DIR__ . '/../payanyway.php'; require_once __DIR__ . '/../payanyway.php';
$requestData = ($_SERVER['REQUEST_METHOD'] === 'POST') ? $_POST : $_GET; $requestData = ($_SERVER['REQUEST_METHOD'] === 'POST') ? $_POST : $_GET;
$gatewayModuleName = 'payanyway';
try { try {
$gatewayModuleName = 'payanyway';
$gatewayParams = getGatewayVariables($gatewayModuleName); $gatewayParams = getGatewayVariables($gatewayModuleName);
if (!$gatewayParams['type']) { if (!$gatewayParams || !isset($gatewayParams['type'])) {
payanyway_sendResponse('FAIL', 500); // Module Not Activated payanyway_sendResponse('FAIL');
} }
$gatewayName = payanyway_getWhmcsVariableOrFail($gatewayParams, 'name');
$integrityCode = payanyway_getPayAnyWayIntegrityCodeOrFail($gatewayParams);
$callbackData = payanyway_getCallbackData($requestData); $callbackData = payanyway_getCallbackData($requestData);
('CHECK' === $callbackData['MNT_COMMAND']) (isset($callbackData['MNT_COMMAND'])) && ('CHECK' === $callbackData['MNT_COMMAND'])
? payanyway_handleCheckCallback($callbackData, $gatewayName, $integrityCode) ? payanyway_handleCheckCallback($callbackData, $gatewayParams)
: payanyway_handlePayCallback($callbackData, $gatewayName, $integrityCode, $gatewayModuleName); : payanyway_handlePayCallback($callbackData, $gatewayParams, $gatewayModuleName);
} catch (\PayAnyWayException $e) {
logTransaction($gatewayModuleName, $requestData, 'Error: ' . $e->getMessage());
payanyway_sendResponse('FAIL', 500);
} catch (\Exception $e) { } catch (\Exception $e) {
logTransaction($gatewayModuleName, $requestData, 'Error: ' . $e->getMessage()); logTransaction($gatewayModuleName, $requestData, 'Error: ' . $e->getMessage());
payanyway_sendResponse('FAIL', 500); payanyway_sendResponse('FAIL');
} }

View File

@ -1,4 +1,5 @@
<?php <?php
/** /**
* PayAnyWay - WHMCS Payment Gateway Module * PayAnyWay - WHMCS Payment Gateway Module
* *
@ -7,18 +8,25 @@
* @see https://developers.whmcs.com/payment-gateways/ * @see https://developers.whmcs.com/payment-gateways/
*/ */
const PAYANYWAY_PAYMENT_GATEWAY_MODULE_VERSION = 'PAYANYWAY_1.1.0'; const CMS_NAME = 'WHMCS';
const PAYANYWAY_API_URL_PROD = 'https://www.payanyway.ru/assistant.htm'; const MODULE_NAME = 'payanyway';
const PAYANYWAY_API_URL_DEMO = 'https://demo.moneta.ru/assistant.htm'; const MODULE_VERSION = '1.0.0';
const PAYANYWAY_SUPPORTED_CURRENCIES = ['RUB'];
const PAYANYWAY_DESCRIPTION_MAX_LENGTH = 500; const TRANSACTION_ID_STRING_DELIMITER = '|';
const PAYANYWAY_INVENTORY_ITEM_NAME_MAX_LENGTH = 128;
const PAYANYWAY_INVENTORY_ITEM_DEFAULT_PAYMENT_METHOD = 'full_payment'; const PAYMENT_URL_PROD = 'https://www.payanyway.ru/assistant.htm';
const PAYANYWAY_INVENTORY_ITEM_DEFAULT_PAYMENT_OBJECT = 'service'; const PAYMENT_URL_DEMO = 'https://demo.moneta.ru/assistant.htm';
const PAYANYWAY_INVENTORY_ITEM_DEFAULT_QUANTITY = 1;
const PAYANYWAY_INVENTORY_ITEM_DEFAULT_MEASURE = 'unit'; const DESCRIPTION_MAX_LENGTH = 500;
const PAYANYWAY_VAT_TAG_NONE = '1105'; // none
const PAYANYWAY_PAYMENT_FEE = 0.0; const INVENTORY_ITEM_NAME_MAX_LENGTH = 128;
const INVENTORY_ITEM_DEFAULT_PAYMENT_METHOD = 'full_payment';
const INVENTORY_ITEM_DEFAULT_PAYMENT_OBJECT = 'service';
const INVENTORY_ITEM_DEFAULT_QUANTITY = 1;
const INVENTORY_ITEM_DEFAULT_MEASURE = 'unit';
const INVENTORY_ITEM_DEFAULT_VAT_TAG = '1105'; // none
const MODULE_PAYMENT_FEE = 0.0;
if (!defined('WHMCS')) { if (!defined('WHMCS')) {
die('This file cannot be accessed directly'); die('This file cannot be accessed directly');
@ -27,7 +35,7 @@ if (!defined('WHMCS')) {
/** /**
* Module metadata for WHMCS gateway registration. * Module metadata for WHMCS gateway registration.
* *
* @return array * @return array<string, mixed>
*/ */
function payanyway_MetaData() function payanyway_MetaData()
{ {
@ -46,7 +54,7 @@ function payanyway_MetaData()
* presented to administrator users when activating and configuring your * presented to administrator users when activating and configuring your
* payment gateway module for use. * payment gateway module for use.
* *
* @return array * @return array<string, mixed>
*/ */
function payanyway_config() function payanyway_config()
{ {
@ -84,11 +92,11 @@ function payanyway_config()
* anchor tag (<a>) pointing to the hosted checkout page. WHMCS will render * anchor tag (<a>) pointing to the hosted checkout page. WHMCS will render
* this HTML inside the invoice payment section. * this HTML inside the invoice payment section.
* *
* @param array $params Standard WHMCS gateway params array. * @param array<string, mixed> $params Standard WHMCS gateway params array.
* See: https://developers.whmcs.com/payment-gateways/third-party-gateway/ * See: https://developers.whmcs.com/payment-gateways/third-party-gateway/
* @return string HTML output to display on the invoice page. * @return string HTML output to display on the invoice page.
*/ */
function payanyway_link($params) function payanyway_link(array $params)
{ {
try { try {
// Gateway Configuration Parameters // Gateway Configuration Parameters
@ -101,32 +109,27 @@ function payanyway_link($params)
$amount = payanyway_getWhmcsInvoiceAmountOrFail($params); $amount = payanyway_getWhmcsInvoiceAmountOrFail($params);
$formatAmount = payanyway_formatAmount($amount); $formatAmount = payanyway_formatAmount($amount);
$currency = payanyway_getWhmcsInvoiceCurrencyOrFail($params); $currency = payanyway_getWhmcsInvoiceCurrencyOrFail($params);
} catch (\PayAnyWayException $e) {
return '<p style="color:red;"><strong>Error:</strong> '
. htmlspecialchars($e->getMessage(), ENT_QUOTES | ENT_HTML5, 'UTF-8') . '</p>';
}
// System Parameters // System Parameters
$langPayNow = payanyway_getOptionalStringWithDefault($params, 'langpaynow', 'Pay Now'); $langPayNow = payanyway_getOptionalStringWithDefault($params, 'langpaynow', 'Pay Now');
$whmcsVersionNumber = payanyway_getOptionalString($params, 'whmcsVersion'); $whmcsVersion = payanyway_getOptionalString($params, 'whmcsVersion');
$whmcsVersion = isset($whmcsVersionNumber) ? 'WHMCS_' . $whmcsVersionNumber : 'WHMCS_unknown_version';
$url = ('1' === $testMode) ? PAYANYWAY_API_URL_DEMO : PAYANYWAY_API_URL_PROD; $url = ('1' === $testMode) ? PAYMENT_URL_DEMO : PAYMENT_URL_PROD;
$subscriberId = payanyway_getSubscriberId($params); $subscriberId = payanyway_getSubscriberId($params);
$signature = md5($mntId . $invoiceId . $formatAmount . $currency . $subscriberId . $testMode . $integrityCode); $signature = md5($mntId . $invoiceId . $formatAmount . $currency . $subscriberId . $testMode . $integrityCode);
$postFields = [ $postFields = array(
'MNT_ID' => $mntId, 'MNT_ID' => $mntId,
'MNT_TRANSACTION_ID' => $invoiceId, 'MNT_TRANSACTION_ID' => payanyway_createTransactionId($invoiceId),
'MNT_AMOUNT' => $formatAmount, 'MNT_AMOUNT' => $formatAmount,
'MNT_CURRENCY_CODE' => $currency, 'MNT_CURRENCY_CODE' => $currency,
'MNT_TEST_MODE' => $testMode, 'MNT_TEST_MODE' => '0',
'MNT_DESCRIPTION' => payanyway_getDescription($invoiceId, $params), 'MNT_DESCRIPTION' => payanyway_getDescription($invoiceId, $params),
'MNT_SUBSCRIBER_ID' => $subscriberId, 'MNT_SUBSCRIBER_ID' => $subscriberId,
'MNT_SIGNATURE' => $signature, 'MNT_SIGNATURE' => $signature,
'MNT_CMS' => $whmcsVersion . '|' . PAYANYWAY_PAYMENT_GATEWAY_MODULE_VERSION, 'MNT_CMS' => payanyway_getCmsModuleVersion($whmcsVersion),
]; );
$htmlOutput = '<form method="post" action="' . $url . '">'; $htmlOutput = '<form method="post" action="' . $url . '">';
foreach ($postFields as $key => $value) { foreach ($postFields as $key => $value) {
@ -138,6 +141,10 @@ function payanyway_link($params)
$htmlOutput .= '</form>'; $htmlOutput .= '</form>';
return $htmlOutput; return $htmlOutput;
} catch (\Exception $e) {
return '<p style="color:red;"><strong>Error:</strong> '
. htmlspecialchars($e->getMessage(), ENT_QUOTES | ENT_HTML5, 'UTF-8') . '</p>';
}
} }
/** /**
@ -145,15 +152,15 @@ function payanyway_link($params)
*/ */
/** /**
* @param array<non-empty-string, mixed> $params * @param array<string, mixed> $data
* @return string * @return int
* @throws PayAnyWayException * @throws \InvalidArgumentException
*/ */
function payanyway_getPayAnyWayMntIdOrFail($params) function payanyway_getPayAnyWayMntIdOrFail(array $data)
{ {
$mntId = payanyway_getOptionalString($params, 'mnt_id'); $mntId = payanyway_getOptionalInt($data, 'mnt_id');
if (null === $mntId) { if (null === $mntId) {
throw new \PayAnyWayException( throw new \InvalidArgumentException(
'PayAnyWay module is not configured properly. ' 'PayAnyWay module is not configured properly. '
. 'Please set Account Number in WHMCS Admin → Payment Gateways.' . 'Please set Account Number in WHMCS Admin → Payment Gateways.'
); );
@ -163,15 +170,15 @@ function payanyway_getPayAnyWayMntIdOrFail($params)
} }
/** /**
* @param array<non-empty-string, mixed> $params * @param array<string, mixed> $data
* @return string * @return string
* @throws PayAnyWayException * @throws \InvalidArgumentException
*/ */
function payanyway_getPayAnyWayIntegrityCodeOrFail($params) function payanyway_getPayAnyWayIntegrityCodeOrFail(array $data)
{ {
$integrityCode = payanyway_getOptionalString($params, 'mnt_dataintegrity_code'); $integrityCode = payanyway_getOptionalString($data, 'mnt_dataintegrity_code');
if (null === $integrityCode) { if (null === $integrityCode) {
throw new \PayAnyWayException( throw new \InvalidArgumentException(
'PayAnyWay module is not configured properly. ' 'PayAnyWay module is not configured properly. '
. 'Please set Code of Data Integrity Verification in WHMCS Admin → Payment Gateways.' . 'Please set Code of Data Integrity Verification in WHMCS Admin → Payment Gateways.'
); );
@ -181,54 +188,56 @@ function payanyway_getPayAnyWayIntegrityCodeOrFail($params)
} }
/** /**
* @param array<non-empty-string, mixed> $params * @param array<string, mixed> $data
* @return int * @return int
* @throws PayAnyWayException * @throws \InvalidArgumentException
*/ */
function payanyway_getWhmcsInvoiceIdOrFail($params) function payanyway_getWhmcsInvoiceIdOrFail(array $data)
{ {
$invoiceId = payanyway_getOptionalInt($params, 'invoiceid'); $invoiceId = payanyway_getOptionalInt($data, 'invoiceid');
if (null === $invoiceId) { if (null === $invoiceId) {
throw new \PayAnyWayException('Missing or invalid invoice ID (integer required)'); throw new \InvalidArgumentException('Missing or invalid invoice ID (integer required)');
} }
return $invoiceId; return $invoiceId;
} }
/** /**
* @param array<non-empty-string, mixed> $params * @param array<string, mixed> $data
* @return float * @return float
* @throws PayAnyWayException * @throws \InvalidArgumentException
*/ */
function payanyway_getWhmcsInvoiceAmountOrFail($params) function payanyway_getWhmcsInvoiceAmountOrFail(array $data)
{ {
$amount = payanyway_getOptionalFloat($params, 'amount'); $amount = payanyway_getOptionalFloat($data, 'amount');
if (null === $amount) { if (null === $amount) {
throw new \PayAnyWayException('Missing or invalid invoice amount (float required).'); throw new \InvalidArgumentException('Missing or invalid invoice amount (float required).');
} }
return $amount; return $amount;
} }
/** /**
* @param array<non-empty-string, mixed> $params * @param array<string, mixed> $data
* @return non-empty-string * @return string
* @throws PayAnyWayException * @throws \InvalidArgumentException
*/ */
function payanyway_getWhmcsInvoiceCurrencyOrFail($params) function payanyway_getWhmcsInvoiceCurrencyOrFail(array $data)
{ {
$currency = payanyway_getOptionalString($params, 'currency'); $supportedCurrencies = array('RUB');
$currency = payanyway_getOptionalString($data, 'currency');
if (null === $currency) { if (null === $currency) {
$supported = implode(', ', PAYANYWAY_SUPPORTED_CURRENCIES); $supported = implode(', ', $supportedCurrencies);
throw new \PayAnyWayException( throw new \InvalidArgumentException(
'Missing currency code. Module supports only: ' . $supported 'Missing currency code. Module supports only: ' . $supported
); );
} }
$currency = strtoupper($currency); $currency = strtoupper($currency);
if (!in_array($currency, PAYANYWAY_SUPPORTED_CURRENCIES, true)) { if (!in_array($currency, $supportedCurrencies, true)) {
$supported = implode(', ', PAYANYWAY_SUPPORTED_CURRENCIES); $supported = implode(', ', $supportedCurrencies);
throw new \PayAnyWayException( throw new \InvalidArgumentException(
"Unsupported currency code: '{$currency}'. Module supports only: {$supported}" "Unsupported currency code: '{$currency}'. Module supports only: {$supported}"
); );
} }
@ -237,63 +246,64 @@ function payanyway_getWhmcsInvoiceCurrencyOrFail($params)
} }
/** /**
* @param array<non-empty-string, mixed> $params * @param array<string, mixed> $data
* @param string $name
* @return string * @return string
* @throws PayAnyWayException * @throws \InvalidArgumentException
*/ */
function payanyway_getWhmcsVariableOrFail($params, $name) function payanyway_getWhmcsVariableOrFail(array $data, $name)
{ {
$var = payanyway_getOptionalString($params, $name); $var = payanyway_getOptionalString($data, $name);
if (null === $var) { if (null === $var) {
throw new \PayAnyWayException(sprintf('Whmcs variable $params[\'%s\'] is not set.', $name)); throw new \InvalidArgumentException(sprintf('WHMCS variable $params[\'%s\'] is not set.', $name));
} }
return $var; return $var;
} }
/** /**
* @param array<non-empty-string, mixed> $array * @param array<string, mixed> $data
* @param string $key * @param string $key
* @return int|null * @return int|null
*/ */
function payanyway_getOptionalInt($array, $key) function payanyway_getOptionalInt(array $data, $key)
{ {
if (!isset($array[$key]) || !is_numeric($array[$key])) { if (!isset($data[$key]) || !is_numeric($data[$key])) {
return null; return null;
} }
return (int)$array[$key]; return (int)$data[$key];
} }
/** /**
* @param array<non-empty-string, mixed> $array * @param array<string, mixed> $data
* @param string $key * @param string $key
* @return float|null * @return float|null
*/ */
function payanyway_getOptionalFloat($array, $key) function payanyway_getOptionalFloat(array $data, $key)
{ {
if (!isset($array[$key]) || !is_numeric($array[$key])) { if (!isset($data[$key]) || !is_numeric($data[$key])) {
return null; return null;
} }
return (float)$array[$key]; return (float)$data[$key];
} }
/** /**
* @param array<non-empty-string, mixed> $array * @param array<string, mixed> $data
* @param string $key * @param string $key
* @return float * @return float
* @throws PayAnyWayException * @throws \InvalidArgumentException
*/ */
function payanyway_getRequiredFloat($array, $key) function payanyway_getRequiredFloat(array $data, $key)
{ {
if (!isset($array[$key])) { if (!isset($data[$key])) {
throw new \PayAnyWayException('Missing required field ' . $key); throw new \InvalidArgumentException('Missing required field ' . $key);
} }
$value = $array[$key]; $value = $data[$key];
if (!is_numeric($value)) { if (!is_numeric($value)) {
throw new \PayAnyWayException( throw new \InvalidArgumentException(
sprintf('Field "%s" must be a number, %s given', $key, gettype($value)) sprintf('Field "%s" must be a number, %s given', $key, gettype($value))
); );
} }
@ -302,63 +312,63 @@ function payanyway_getRequiredFloat($array, $key)
} }
/** /**
* @param array<non-empty-string, mixed> $array * @param array<string, mixed> $data
* @param string $key * @param string $key
* @return string * @return string
* @throws PayAnyWayException * @throws \InvalidArgumentException
*/ */
function payanyway_getRequiredString($array, $key) function payanyway_getRequiredString(array $data, $key)
{ {
if (!isset($array[$key])) { if (!isset($data[$key])) {
throw new \PayAnyWayException('Missing required field ' . $key); throw new \InvalidArgumentException('Missing required field ' . $key);
} }
$value = $array[$key]; $value = $data[$key];
if (!is_string($value)) { if (!is_string($value)) {
throw new \PayAnyWayException( throw new \InvalidArgumentException(
sprintf('Field "%s" must be a string, %s given', $key, gettype($value)) sprintf('Field "%s" must be a string, %s given', $key, gettype($value))
); );
} }
if ('' === $value) { if ('' === $value) {
throw new \PayAnyWayException(sprintf('Field "%s" must be a non-empty string', $key)); throw new \InvalidArgumentException(sprintf('Field "%s" must be a non-empty string', $key));
} }
return $value; return $value;
} }
/** /**
* @param array<non-empty-string, mixed> $array * @param array<string, mixed> $data
* @param string $key * @param string $key
* @return string|null * @return string|null
*/ */
function payanyway_getOptionalString($array, $key) function payanyway_getOptionalString(array $data, $key)
{ {
if (!isset($array[$key])) { if (!isset($data[$key])) {
return null; return null;
} }
$value = $array[$key]; $value = $data[$key];
return (is_string($value) && ('' !== trim($value))) ? $value : null; return (is_string($value) && ('' !== trim($value))) ? $value : null;
} }
/** /**
* @param array<non-empty-string, mixed> $params * @param array<string, mixed> $data
* @param non-empty-string $key * @param string $key
* @param string $default * @param string $default
* @return string * @return string
*/ */
function payanyway_getOptionalStringWithDefault($params, $key, $default = '') function payanyway_getOptionalStringWithDefault(array $data, $key, $default = '')
{ {
$value = payanyway_getOptionalString($params, $key); $value = payanyway_getOptionalString($data, $key);
return (null !== $value) ? $value : $default; return (null !== $value) ? $value : $default;
} }
/** /**
* @param float $amount * @param float $amount
* @return non-empty-string * @return string
*/ */
function payanyway_formatAmount($amount) function payanyway_formatAmount($amount)
{ {
@ -366,16 +376,16 @@ function payanyway_formatAmount($amount)
} }
/** /**
* @param array $params * @param array<string, mixed> $data
* @return string * @return string
*/ */
function payanyway_getSubscriberId($params) function payanyway_getSubscriberId(array $data)
{ {
$email = isset($params['clientdetails']['email']) $email = isset($data['clientdetails']['email'])
? filter_var($params['clientdetails']['email'], FILTER_SANITIZE_EMAIL) ? filter_var($data['clientdetails']['email'], FILTER_SANITIZE_EMAIL)
: ''; : '';
$phoneNumber = isset($params['clientdetails']['phonenumber']) $phoneNumber = isset($data['clientdetails']['phonenumber'])
? preg_replace('/[^0-9+]/', '', $params['clientdetails']['phonenumber']) ? preg_replace('/[^0-9+]/', '', $data['clientdetails']['phonenumber'])
: ''; : '';
return $email ?: $phoneNumber; return $email ?: $phoneNumber;
@ -383,41 +393,56 @@ function payanyway_getSubscriberId($params)
/** /**
* @param int $invoiceId * @param int $invoiceId
* @param array<non-empty-string, mixed> $params * @return string
* @return non-empty-string
*/ */
function payanyway_getDescription($invoiceId, $params) function payanyway_createTransactionId($invoiceId)
{
$date = date('YmdHis');
return $invoiceId . TRANSACTION_ID_STRING_DELIMITER . $date;
}
/**
* @param string $transactionId
* @return int|null
*/
function payanyway_getInvoiceIdFromTransactionId($transactionId) {
$transactionIdData = explode(TRANSACTION_ID_STRING_DELIMITER, $transactionId, 2);
return isset($transactionIdData[0]) ? (int)$transactionIdData[0] : null;
}
/**
* @param int $invoiceId
* @param array<string, mixed> $params
* @return string
*/
function payanyway_getDescription($invoiceId, array $params)
{ {
$clientFirstName = isset($params['clientdetails']['firstname']) ? $params['clientdetails']['firstname'] : ''; $clientFirstName = isset($params['clientdetails']['firstname']) ? $params['clientdetails']['firstname'] : '';
$clientLastName = isset($params['clientdetails']['lastname']) ? $params['clientdetails']['lastname'] : ''; $clientLastName = isset($params['clientdetails']['lastname']) ? $params['clientdetails']['lastname'] : '';
$clientName = trim($clientFirstName . ' ' . $clientLastName); $clientName = trim($clientFirstName . ' ' . $clientLastName);
$description = "Payment for Invoice #{$invoiceId}"; $description = "Payment for Invoice {$invoiceId}";
if ('' !== $clientName) { if ('' !== $clientName) {
$description .= " from {$clientName}"; $description .= " from {$clientName}";
} }
$originalDescription = isset($params['description']) ? trim($params['description']) : ''; $originalDescription = isset($params['description']) ? $params['description'] : '';
if ('' !== $originalDescription) { if ('' !== $originalDescription) {
$description .= ': ' . $originalDescription; $description .= ': ' . htmlspecialchars($originalDescription, ENT_QUOTES, 'UTF-8');
} }
if (mb_strlen($description, 'UTF-8') > PAYANYWAY_DESCRIPTION_MAX_LENGTH) { return payanyway_validateString($description, DESCRIPTION_MAX_LENGTH);
$description = mb_substr($description, 0, 497, 'UTF-8') . '...';
}
return $description;
} }
/** /**
* @param array<non-empty-string, mixed> $requestData * @param array<string, mixed> $requestData
* @return array<non-empty-string, mixed> * @return array<string, mixed>
* @throws PayAnyWayException
*/ */
function payanyway_getCallbackData($requestData) function payanyway_getCallbackData(array $requestData)
{ {
return [ return array(
'MNT_COMMAND' => payanyway_getOptionalStringWithDefault($requestData, 'MNT_COMMAND'), 'MNT_COMMAND' => payanyway_getOptionalStringWithDefault($requestData, 'MNT_COMMAND'),
'MNT_ID' => payanyway_getRequiredString($requestData, 'MNT_ID'), 'MNT_ID' => payanyway_getRequiredString($requestData, 'MNT_ID'),
'MNT_TRANSACTION_ID' => payanyway_getRequiredString($requestData, 'MNT_TRANSACTION_ID'), 'MNT_TRANSACTION_ID' => payanyway_getRequiredString($requestData, 'MNT_TRANSACTION_ID'),
@ -427,134 +452,156 @@ function payanyway_getCallbackData($requestData)
'MNT_SUBSCRIBER_ID' => payanyway_getOptionalStringWithDefault($requestData, 'MNT_SUBSCRIBER_ID'), 'MNT_SUBSCRIBER_ID' => payanyway_getOptionalStringWithDefault($requestData, 'MNT_SUBSCRIBER_ID'),
'MNT_TEST_MODE' => payanyway_getRequiredString($requestData, 'MNT_TEST_MODE'), 'MNT_TEST_MODE' => payanyway_getRequiredString($requestData, 'MNT_TEST_MODE'),
'MNT_SIGNATURE' => payanyway_getRequiredString($requestData, 'MNT_SIGNATURE'), 'MNT_SIGNATURE' => payanyway_getRequiredString($requestData, 'MNT_SIGNATURE'),
]; );
} }
/** /**
* @param array<non-empty-string, mixed> $callbackData * @param array<string, mixed> $callbackData
* @param non-empty-string $gatewayName * @param array<string, mixed> $gatewayParams
* @param non-empty-string $integrityCode
* @return void * @return void
* @throws DOMException * @throws \Exception
*/ */
function payanyway_handleCheckCallback($callbackData, $gatewayName, $integrityCode) function payanyway_handleCheckCallback(array $callbackData, array $gatewayParams)
{ {
if (!payanyway_checkSignature($callbackData, $integrityCode, $callbackData['MNT_COMMAND'])) { $integrityCode = payanyway_getPayAnyWayIntegrityCodeOrFail($gatewayParams);
payanyway_sendResponse('FAIL', 500); if (!payanyway_checkSignature($callbackData, $callbackData['MNT_SIGNATURE'], $integrityCode)) {
payanyway_sendResponse('FAIL');
} }
$rawInvoiceId = (int)$callbackData['MNT_TRANSACTION_ID']; $gatewayName = payanyway_getWhmcsVariableOrFail($gatewayParams, 'name');
$rawInvoiceId = payanyway_getInvoiceIdFromTransactionId($callbackData['MNT_TRANSACTION_ID']);
if (null === $rawInvoiceId) {
payanyway_sendResponse('FAIL');
}
$invoiceId = checkCbInvoiceID($rawInvoiceId, $gatewayName); $invoiceId = checkCbInvoiceID($rawInvoiceId, $gatewayName);
$invoiceDetails = payanyway_getInvoiceDetails($invoiceId); $invoiceDetails = payanyway_getInvoiceDetails($invoiceId);
if (null === $invoiceDetails) { if (null === $invoiceDetails) {
payanyway_sendResponse('FAIL', 500); payanyway_sendResponse('FAIL');
} }
$invoiceItems = payanyway_getInvoiceItems($invoiceDetails); $invoiceItems = payanyway_getInvoiceItems($invoiceDetails);
$invoiceTotal = payanyway_formatAmount($invoiceDetails['total']); $invoicePaymentStatus = (string)$invoiceDetails['status'];
$invoicePaymentStatus = $invoiceDetails['status'];
$xmlData = [ $xmlData = array(
'MNT_ID' => $callbackData['MNT_ID'], 'MNT_ID' => payanyway_getPayAnyWayMntIdOrFail($gatewayParams),
'MNT_TRANSACTION_ID' => $callbackData['MNT_TRANSACTION_ID'], 'MNT_TRANSACTION_ID' => $callbackData['MNT_TRANSACTION_ID'],
'MNT_AMOUNT' => $invoiceTotal, 'MNT_AMOUNT' => payanyway_formatAmount($invoiceDetails['total']),
'MNT_CURRENCY_CODE' => $callbackData['MNT_CURRENCY_CODE'], 'MNT_CURRENCY_CODE' => $callbackData['MNT_CURRENCY_CODE'],
'inventory' => payanyway_getInventoryJson($invoiceItems) ?: null, 'inventory' => payanyway_getInventoryJson($invoiceItems) ?: null,
'client' => $callbackData['MNT_SUBSCRIBER_ID'], 'client' => $callbackData['MNT_SUBSCRIBER_ID'],
'sno' => null, 'sno' => null,
'delivery' => $invoiceTotal, 'delivery' => null,
];
switch (true) {
case empty($callbackData['MNT_AMOUNT']):
$xmlData['MNT_RESULT_CODE'] = 100;
$xmlData['MNT_DESCRIPTION'] = 'Invoice created, but amount not set';
break;
case ('Paid' === $invoicePaymentStatus):
$xmlData['MNT_RESULT_CODE'] = 200;
$xmlData['MNT_DESCRIPTION'] = 'Invoice Paid';
break;
default:
$xmlData['MNT_RESULT_CODE'] = 402;
$xmlData['MNT_DESCRIPTION'] = 'Invoice Unpaid';
}
$xmlData['MNT_SIGNATURE'] = md5(
$xmlData['MNT_RESULT_CODE']
. $xmlData['MNT_ID']
. $xmlData['MNT_TRANSACTION_ID']
. $integrityCode
); );
payanyway_sendResponse(payanyway_buildXMLResponse($xmlData), 200); list($xmlData['MNT_RESULT_CODE'], $xmlData['MNT_DESCRIPTION']) = payanyway_determineCheckResult($callbackData, $invoicePaymentStatus);
$xmlData['MNT_SIGNATURE'] = md5(
$xmlData['MNT_RESULT_CODE'] .
$xmlData['MNT_ID'] .
$xmlData['MNT_TRANSACTION_ID'] .
payanyway_getPayAnyWayIntegrityCodeOrFail($gatewayParams)
);
payanyway_sendResponse(payanyway_buildXMLResponse($xmlData));
} }
/** /**
* @param array<non-empty-string, mixed> $callbackData * @param array<string, mixed> $callbackData
* @param non-empty-string $gatewayName * @param string $invoicePaymentStatus
* @param non-empty-string $integrityCode * @return array
* @param non-empty-string $gatewayModuleName * @throws \Exception
* @return void
* @throws DOMException
*/ */
function payanyway_handlePayCallback($callbackData, $gatewayName, $integrityCode, $gatewayModuleName) function payanyway_determineCheckResult(array $callbackData, $invoicePaymentStatus)
{ {
if (!payanyway_checkSignature($callbackData, $integrityCode)) { if (empty($callbackData['MNT_AMOUNT'])) {
payanyway_sendResponse('FAIL', 500); return array(100, "Invoice status is '{$invoicePaymentStatus}'");
} }
$rawInvoiceId = $callbackData['MNT_TRANSACTION_ID']; if ('Paid' === $invoicePaymentStatus) {
$invoiceId = checkCbInvoiceID($rawInvoiceId, $gatewayName); return array(200, 'Invoice Paid');
$paidAmount = $callbackData['MNT_AMOUNT']; }
$invoiceDetails = payanyway_getInvoiceDetailsOrFail($invoiceId); return array(402, "Invoice status is '{$invoicePaymentStatus}'");
}
/**
* @param array<string, mixed> $callbackData
* @param array<string, mixed> $gatewayParams
* @param string $gatewayModuleName
* @return void
* @throws \Exception
*/
function payanyway_handlePayCallback(array $callbackData, array $gatewayParams, $gatewayModuleName)
{
$gatewayName = payanyway_getWhmcsVariableOrFail($gatewayParams, 'name');
$rawInvoiceId = payanyway_getInvoiceIdFromTransactionId($callbackData['MNT_TRANSACTION_ID']);
if (null === $rawInvoiceId) {
payanyway_sendResponse('FAIL');
}
$invoiceId = checkCbInvoiceID($rawInvoiceId, $gatewayName);
$invoiceDetails = payanyway_getInvoiceDetails($invoiceId);
if (null === $invoiceDetails) { if (null === $invoiceDetails) {
payanyway_sendResponse('FAIL', 500); payanyway_sendResponse('FAIL');
}
$invoiceTotal = payanyway_formatAmount($invoiceDetails['total']);
$shopData = array(
'MNT_ID' => payanyway_getPayAnyWayMntIdOrFail($gatewayParams),
'MNT_TRANSACTION_ID' => $callbackData['MNT_TRANSACTION_ID'],
'MNT_OPERATION_ID' => $callbackData['MNT_OPERATION_ID'],
'MNT_AMOUNT' => $invoiceTotal,
'MNT_CURRENCY_CODE' => $callbackData['MNT_CURRENCY_CODE'],
'MNT_SUBSCRIBER_ID' => $callbackData['MNT_SUBSCRIBER_ID'],
'MNT_TEST_MODE' => $callbackData['MNT_TEST_MODE'],
);
$integrityCode = payanyway_getPayAnyWayIntegrityCodeOrFail($gatewayParams);
if (!payanyway_checkSignature($shopData, $callbackData['MNT_SIGNATURE'], $integrityCode)) {
payanyway_sendResponse('FAIL');
} }
$invoiceItems = payanyway_getInvoiceItems($invoiceDetails); $invoiceItems = payanyway_getInvoiceItems($invoiceDetails);
$invoicePaymentStatus = $invoiceDetails['status']; $invoicePaymentStatus = (string)$invoiceDetails['status'];
$mntId = payanyway_getPayAnyWayMntIdOrFail($gatewayParams);
$resultCode = 200; $resultCode = 200;
$xmlData = [ $xmlData = array(
'MNT_ID' => $callbackData['MNT_ID'], 'MNT_ID' => $mntId,
'MNT_TRANSACTION_ID' => $callbackData['MNT_TRANSACTION_ID'], 'MNT_TRANSACTION_ID' => $callbackData['MNT_TRANSACTION_ID'],
'MNT_RESULT_CODE' => $resultCode, 'MNT_RESULT_CODE' => $resultCode,
'MNT_SIGNATURE' => md5( 'MNT_SIGNATURE' => md5($resultCode . $mntId . $callbackData['MNT_TRANSACTION_ID'] . $integrityCode),
$resultCode . $callbackData['MNT_ID'] . $callbackData['MNT_TRANSACTION_ID'] . $integrityCode 'MNT_AMOUNT' => $invoiceTotal,
),
'MNT_AMOUNT' => $paidAmount,
'MNT_CURRENCY_CODE' => $callbackData['MNT_CURRENCY_CODE'], 'MNT_CURRENCY_CODE' => $callbackData['MNT_CURRENCY_CODE'],
'MNT_DESCRIPTION' => ('Paid' !== $invoicePaymentStatus) 'MNT_DESCRIPTION' => ('Paid' !== $invoicePaymentStatus) ? 'Invoice success paid' : 'Invoice already paid',
? 'Invoice success paid' 'inventory' => payanyway_getInventoryJson($invoiceItems) ?: null,
: 'Invoice already paid',
'inventory' => payanyway_getInventoryJson($invoiceItems),
'client' => $callbackData['MNT_SUBSCRIBER_ID'], 'client' => $callbackData['MNT_SUBSCRIBER_ID'],
'sno' => null, 'sno' => null,
'delivery' => $paidAmount, 'delivery' => null,
]; );
if ('Paid' !== $invoicePaymentStatus) { if ('Paid' !== $invoicePaymentStatus) {
$transactionId = $callbackData['MNT_OPERATION_ID']; $transactionId = $callbackData['MNT_OPERATION_ID'];
checkCbTransID($transactionId); // if $transactionId exist, script die() checkCbTransID($transactionId); // if $transactionId exist, script die()
if (!payanyway_existInvoiceTransaction($invoiceId, $transactionId)) { if (!payanyway_existInvoiceTransaction($invoiceId, $transactionId)) {
addInvoicePayment($invoiceId, $transactionId, $paidAmount, PAYANYWAY_PAYMENT_FEE, $gatewayModuleName); addInvoicePayment($invoiceId, $transactionId, $invoiceTotal, MODULE_PAYMENT_FEE, $gatewayModuleName);
logTransaction($gatewayName, $callbackData, 'Success'); logTransaction($gatewayName, $callbackData, 'Success');
} }
} }
payanyway_sendResponse(payanyway_buildXMLResponse($xmlData), 200); payanyway_sendResponse(payanyway_buildXMLResponse($xmlData));
} }
/** /**
* @param array<non-empty-string, mixed> $callbackData * @param array<string, mixed> $data
* @param string $callbackSignature
* @param string $integrityCode * @param string $integrityCode
* @param non-empty-string|null $command
* @return bool * @return bool
*/ */
function payanyway_checkSignature($callbackData, $integrityCode, $command = null) function payanyway_checkSignature(array $data, $callbackSignature, $integrityCode)
{ {
$baseFields = [ $baseFields = array(
'MNT_ID', 'MNT_ID',
'MNT_TRANSACTION_ID', 'MNT_TRANSACTION_ID',
'MNT_OPERATION_ID', 'MNT_OPERATION_ID',
@ -562,28 +609,30 @@ function payanyway_checkSignature($callbackData, $integrityCode, $command = null
'MNT_CURRENCY_CODE', 'MNT_CURRENCY_CODE',
'MNT_SUBSCRIBER_ID', 'MNT_SUBSCRIBER_ID',
'MNT_TEST_MODE', 'MNT_TEST_MODE',
]; );
$fields = ('CHECK' === $command) ? array_merge(['MNT_COMMAND'], $baseFields) : $baseFields; $fields = isset($data['MNT_COMMAND']) && ('CHECK' === $data['MNT_COMMAND'])
? array_merge(['MNT_COMMAND'], $baseFields)
: $baseFields;
$signatureString = array_reduce($fields, static function ($carry, $field) use ($callbackData) { $signatureString = array_reduce($fields, function ($carry, $field) use ($data) {
return $carry . $callbackData[$field]; return $carry . $data[$field];
}, '') . $integrityCode; }, '') . $integrityCode;
return hash_equals(md5($signatureString), $callbackData['MNT_SIGNATURE']); return hash_equals(md5($signatureString), $callbackSignature);
} }
/** /**
* @see https://developers.whmcs.com/api-reference/getinvoice/ * @see https://developers.whmcs.com/api-reference/getinvoice/
* @param int $invoiceId * @param int $invoiceId
* @return array<non-empty-string, mixed>|null * @return array<string, mixed>|null
*/ */
function payanyway_getInvoiceDetails($invoiceId) function payanyway_getInvoiceDetails($invoiceId)
{ {
$command = 'GetInvoice'; $command = 'GetInvoice';
$postData = [ $postData = array(
'invoiceid' => $invoiceId, 'invoiceid' => $invoiceId,
]; );
$results = localAPI($command, $postData); $results = localAPI($command, $postData);
if ($results['result'] !== 'success') { if ($results['result'] !== 'success') {
@ -594,10 +643,10 @@ function payanyway_getInvoiceDetails($invoiceId)
} }
/** /**
* @param array<non-empty-string, mixed> $invoiceDetails * @param array<string, mixed> $invoiceDetails
* @return array<non-empty-string, mixed>|null * @return array<string, mixed>|null
*/ */
function payanyway_getInvoiceItems($invoiceDetails) function payanyway_getInvoiceItems(array $invoiceDetails)
{ {
return (isset($invoiceDetails['items']['item']) && is_array($invoiceDetails['items']['item'])) return (isset($invoiceDetails['items']['item']) && is_array($invoiceDetails['items']['item']))
? $invoiceDetails['items']['item'] ? $invoiceDetails['items']['item']
@ -607,22 +656,22 @@ function payanyway_getInvoiceItems($invoiceDetails)
/** /**
* @see https://developers.whmcs.com/api-reference/gettransactions/ * @see https://developers.whmcs.com/api-reference/gettransactions/
* @param int $invoiceId * @param int $invoiceId
* @param non-empty-string $transactionId * @param string $transactionId
* @return bool * @return bool
*/ */
function payanyway_existInvoiceTransaction($invoiceId, $transactionId) function payanyway_existInvoiceTransaction($invoiceId, $transactionId)
{ {
$results = localAPI('GetTransactions', [ $results = localAPI('GetTransactions', array(
'invoiceid' => $invoiceId, 'invoiceid' => $invoiceId,
'transid' => $transactionId, 'transid' => $transactionId,
]); ));
if ($results['result'] !== 'success') { if ($results['result'] !== 'success') {
return false; return false;
} }
$transactions = isset($results['transactions']['transaction']) ? $results['transactions']['transaction'] : []; $transactions = isset($results['transactions']['transaction']) ? $results['transactions']['transaction'] : array();
if ([] === $transactions) { if (empty($transactions)) {
return false; return false;
} }
@ -636,47 +685,45 @@ function payanyway_existInvoiceTransaction($invoiceId, $transactionId)
} }
/** /**
* @param array<non-empty-string, mixed> $items * @param array<string, mixed> $items
* @return false|string * @return false|string
*/ */
function payanyway_getInventoryJson($items) function payanyway_getInventoryJson(array $items)
{ {
$inventory = []; $inventory = array();
foreach ($items as $item) { foreach ($items as $item) {
$description = html_entity_decode($item['description'], ENT_QUOTES | ENT_HTML5, 'UTF-8'); $name = isset($item['description']) ? $item['description'] : '';
$positionName = trim(preg_replace('/[^\p{L}\p{N}\s\-]/u', '', $description)); $price = isset($item['amount']) ? (float)$item['amount'] : 0;
if (empty($positionName)) { $name = payanyway_validateString($name, INVENTORY_ITEM_NAME_MAX_LENGTH);
$positionName = 'Service';
}
$inventory[] = [ $inventory[] = array(
'name' => mb_substr($positionName, 0, PAYANYWAY_INVENTORY_ITEM_NAME_MAX_LENGTH, 'UTF-8'), 'name' => !empty($name) ? $name : 'Service',
'price' => (float)payanyway_formatAmount($item['amount']), 'price' => (float)payanyway_formatAmount($price),
'quantity' => PAYANYWAY_INVENTORY_ITEM_DEFAULT_QUANTITY, 'quantity' => INVENTORY_ITEM_DEFAULT_QUANTITY,
'vatTag' => PAYANYWAY_VAT_TAG_NONE, 'vatTag' => INVENTORY_ITEM_DEFAULT_VAT_TAG,
'pm' => PAYANYWAY_INVENTORY_ITEM_DEFAULT_PAYMENT_METHOD, 'pm' => INVENTORY_ITEM_DEFAULT_PAYMENT_METHOD,
'po' => PAYANYWAY_INVENTORY_ITEM_DEFAULT_PAYMENT_OBJECT, 'po' => INVENTORY_ITEM_DEFAULT_PAYMENT_OBJECT,
'measure' => PAYANYWAY_INVENTORY_ITEM_DEFAULT_MEASURE, 'measure' => INVENTORY_ITEM_DEFAULT_MEASURE,
]; );
} }
return json_encode($inventory, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); return json_encode($inventory, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
} }
/** /**
* @param array<non-empty-string, mixed> $data * @param array<string, mixed> $data
* @return false|string * @return false|string
* @throws DOMException * @throws \Exception
*/ */
function payanyway_buildXMLResponse($data) function payanyway_buildXMLResponse(array $data)
{ {
$dom = new DOMDocument('1.0', 'UTF-8'); $dom = new \DOMDocument('1.0', 'UTF-8');
$dom->formatOutput = false; $dom->formatOutput = false;
$root = $dom->createElement('MNT_RESPONSE'); $root = $dom->createElement('MNT_RESPONSE');
$dom->appendChild($root); $dom->appendChild($root);
$requiredFields = [ $requiredFields = array(
'MNT_ID', 'MNT_ID',
'MNT_TRANSACTION_ID', 'MNT_TRANSACTION_ID',
'MNT_RESULT_CODE', 'MNT_RESULT_CODE',
@ -684,50 +731,84 @@ function payanyway_buildXMLResponse($data)
'MNT_AMOUNT', 'MNT_AMOUNT',
'MNT_CURRENCY_CODE', 'MNT_CURRENCY_CODE',
'MNT_SIGNATURE', 'MNT_SIGNATURE',
]; );
foreach ($requiredFields as $field) { foreach ($requiredFields as $field) {
if (isset($data[$field])) { if (isset($data[$field]) && ($data[$field] !== '')) {
$element = $dom->createElement($field, $data[$field]); $root->appendChild($dom->createElement($field, (string)$data[$field]));
$root->appendChild($element);
} }
} }
$attributes = $dom->createElement('MNT_ATTRIBUTES'); $attributes = $dom->createElement('MNT_ATTRIBUTES');
$root->appendChild($attributes); $root->appendChild($attributes);
$addAttribute = static function ($key, $value) use ($dom, $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)) { if (!empty($value)) {
$attrElement = $dom->createElement('ATTRIBUTE'); $attrElement = $dom->createElement('ATTRIBUTE');
$keyElement = $dom->createElement('KEY', $key); $attrElement->appendChild($dom->createElement('KEY', $key));
$valueElement = $dom->createElement('VALUE', $value); $attrElement->appendChild($dom->createElement('VALUE', (string)$value));
$attrElement->appendChild($keyElement);
$attrElement->appendChild($valueElement);
$attributes->appendChild($attrElement); $attributes->appendChild($attrElement);
} }
};
$addAttribute('INVENTORY', json_encode($data['inventory'], JSON_UNESCAPED_UNICODE));
$addAttribute('CLIENT', $data['client']);
if (!empty($data['sno'])) {
$addAttribute('SNO', $data['sno']);
}
if (!empty($data['delivery'])) {
$addAttribute('DELIVERY', $data['delivery']);
} }
return $dom->saveXML(); return $dom->saveXML();
} }
/**
* @param string|null $whmcsVersion
* @return string
*/
function payanyway_getCmsModuleVersion($whmcsVersion)
{
$whmcsVersion = isset($whmcsVersion) ? $whmcsVersion : '.unknown';
return sprintf('%s v%s | %s v%s', CMS_NAME, $whmcsVersion, MODULE_NAME, MODULE_VERSION);
}
/**
* @param string $value
* @param int $maxLength
* @return string
*/
function payanyway_validateString($value, $maxLength)
{
$maxLength = max(0, $maxLength);
$value = payanyway_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
*/
function payanyway_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));
}
/** /**
* @param string $response * @param string $response
* @param int $statusCode * @param int $statusCode
* @return void * @return void
*/ */
function payanyway_sendResponse($response, $statusCode) function payanyway_sendResponse($response, $statusCode = 200)
{ {
http_response_code($statusCode); http_response_code($statusCode);
@ -738,13 +819,5 @@ function payanyway_sendResponse($response, $statusCode)
} }
echo $response; echo $response;
die(); exit();
}
class PayAnyWayException extends \Exception
{
public function __construct($message = '', $code = 0, \Exception $previous = null)
{
parent::__construct($message, $code, $previous);
}
} }