Files
Moodle/classes/local/protocol/CallbackNotification.php
T

152 lines
4.6 KiB
PHP

<?php
declare(strict_types=1);
namespace paygw_moneta\local\protocol;
use InvalidArgumentException;
use paygw_moneta\local\Money;
/**
* @package paygw_moneta
* @copyright 2026 Moneta Labs {@link https://moneta.ru/}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
final class CallbackNotification
{
private const ACCOUNT_ID_MAX_LENGTH = 20;
private const TRANSACTION_ID_MAX_LENGTH = 255;
private const OPERATION_ID_MAX_LENGTH = 255;
private const SUBSCRIBER_ID_MAX_LENGTH = 255;
private readonly string $command;
private readonly string $accountId;
private readonly string $transactionId;
private readonly string $operationId;
private readonly string $rawAmount;
private readonly ?Money $amount;
private readonly string $currency;
private readonly string $subscriberId;
private readonly string $rawTestMode;
private readonly string $signature;
/**
* @param array<string, mixed> $fields
*/
public function __construct(array $fields)
{
$this->command = self::optional($fields, 'MNT_COMMAND');
if (!in_array($this->command, ['', AssistantProtocol::COMMAND_CHECK], true)) {
throw new InvalidArgumentException('Неизвестная команда уведомления.');
}
$this->accountId = self::required($fields, 'MNT_ID', self::ACCOUNT_ID_MAX_LENGTH);
$this->transactionId = self::required($fields, 'MNT_TRANSACTION_ID', self::TRANSACTION_ID_MAX_LENGTH);
$this->operationId = self::optional($fields, 'MNT_OPERATION_ID', self::OPERATION_ID_MAX_LENGTH);
$this->rawAmount = self::optional($fields, 'MNT_AMOUNT');
$this->amount = $this->rawAmount === '' ? null : Money::fromDecimal($this->rawAmount);
$this->currency = self::required($fields, 'MNT_CURRENCY_CODE', 3);
$this->subscriberId = self::optional($fields, 'MNT_SUBSCRIBER_ID', self::SUBSCRIBER_ID_MAX_LENGTH);
$this->rawTestMode = self::required($fields, 'MNT_TEST_MODE', 1);
if (!in_array($this->rawTestMode, ['0', '1'], true)) {
throw new InvalidArgumentException('Недопустимое значение MNT_TEST_MODE.');
}
$this->signature = self::required($fields, 'MNT_SIGNATURE', 32);
if (($this->amount === null || $this->operationId === '') && !$this->isCheck()) {
throw new InvalidArgumentException('Уведомлению об оплате нужны сумма и номер операции.');
}
}
public function isCheck(): bool
{
return $this->command === AssistantProtocol::COMMAND_CHECK;
}
public function getCommand(): string
{
return $this->command;
}
public function getAccountId(): string
{
return $this->accountId;
}
public function getTransactionId(): string
{
return $this->transactionId;
}
public function getOperationId(): string
{
return $this->operationId;
}
public function getRawAmount(): string
{
return $this->rawAmount;
}
public function getAmount(): ?Money
{
return $this->amount;
}
public function getCurrency(): string
{
return $this->currency;
}
public function getSubscriberId(): string
{
return $this->subscriberId;
}
public function getRawTestMode(): string
{
return $this->rawTestMode;
}
public function isTestMode(): bool
{
return $this->rawTestMode === '1';
}
public function isSignedBy(Signature $signature): bool
{
return Signature::equals($signature->forNotification($this), $this->signature);
}
/**
* @param array<string, mixed> $fields
*/
private static function required(array $fields, string $name, int $maxLength): string
{
$value = self::optional($fields, $name, $maxLength);
if ($value === '') {
throw new InvalidArgumentException("Не передано поле {$name}.");
}
return $value;
}
/**
* @param array<string, mixed> $fields
*/
private static function optional(array $fields, string $name, int $maxLength = 255): string
{
if (!array_key_exists($name, $fields)) {
return '';
}
$value = $fields[$name];
if (!is_string($value)) {
throw new InvalidArgumentException("Поле {$name} должно быть строкой.");
}
if (strlen($value) > $maxLength) {
throw new InvalidArgumentException("Поле {$name} длиннее допустимого.");
}
return $value;
}
}