feat: paygw_moneta 1.0.0, MONETA.Assistant payment gateway for Moodle

This commit is contained in:
2026-09-25 16:42:38 +03:00
parent 70464af858
commit 14b386a550
65 changed files with 5377 additions and 59 deletions
+99
View File
@@ -0,0 +1,99 @@
<?php
declare(strict_types=1);
namespace paygw_moneta\local\order;
use InvalidArgumentException;
use paygw_moneta\local\receipt\Receipt;
use paygw_moneta\local\receipt\Vat;
/**
* Снимок настроек счёта и данных чека на момент создания заказа.
*
* Колбэки сверяют уведомление с тем, что реально было запрошено (сумма, режим,
* данные чека, `MNT_SUBSCRIBER_ID`), а не с текущей ценой курса и профилем. Номер счёта в снимке —
* дополнительная защита: он обязан совпадать и с текущей настройкой, потому что
* ключ подписи всегда берётся из неё; заказы, созданные до смены счёта,
* оплатить нельзя. Секрет (код проверки целостности) в снимок не попадает.
*
* @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 OrderSnapshot
{
public function __construct(
public readonly string $accountNumber,
public readonly bool $testMode,
public readonly bool $fiscalization,
public readonly Vat $vat,
public readonly Receipt $receipt,
public readonly string $description,
public readonly string $subscriberId,
) {
if ($accountNumber === '') {
throw new InvalidArgumentException('В снимке заказа нет номера счёта.');
}
}
/**
* @return array<string, mixed>
*/
public function toArray(): array
{
return [
'accountnumber' => $this->accountNumber,
'testmode' => $this->testMode,
'fiscalization' => $this->fiscalization,
'vat' => $this->vat->value,
'receipt' => $this->receipt->toArray(),
'description' => $this->description,
'subscriberid' => $this->subscriberId,
];
}
public function toJson(): string
{
return json_encode($this->toArray(), JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE);
}
/**
* @param array<string, mixed> $data
*/
public static function fromArray(array $data): self
{
return new self(
accountNumber: (string) ($data['accountnumber'] ?? ''),
testMode: (bool) ($data['testmode'] ?? false),
fiscalization: (bool) ($data['fiscalization'] ?? false),
vat: Vat::from((string) ($data['vat'] ?? '')),
receipt: Receipt::fromArray(is_array($data['receipt'] ?? null) ? $data['receipt'] : []),
description: (string) ($data['description'] ?? ''),
subscriberId: (string) ($data['subscriberid'] ?? ''),
);
}
public static function fromJson(string $json): self
{
$data = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
if (!is_array($data)) {
throw new InvalidArgumentException('Снимок заказа повреждён.');
}
return self::fromArray($data);
}
/**
* Совпадение реквизитов и чека; описание не сравнивается — оно не влияет
* на оплату и не должно плодить заказы.
*/
public function equals(self $other): bool
{
$mine = $this->toArray();
$theirs = $other->toArray();
unset($mine['description'], $theirs['description']);
return $mine === $theirs;
}
}