Files
PHPShop/moneta/class/protocol/TransactionId.php
T

119 lines
3.7 KiB
PHP

<?php
declare(strict_types=1);
namespace MonetaPhpShop\protocol;
use DateTimeImmutable;
use DateTimeZone;
use InvalidArgumentException;
use MonetaPhpShop\Money;
/**
* `MNT_TRANSACTION_ID` — ôîðìàò `<íîìåð>_<ñóììà>_<YmdHis>UTC`: íîìåð çàêàçà PHPShop
* (`phpshop_orders.uid`, íàïðèìåð `4-36`), ñóììà ôîðìû â êîïåéêàõ è ìåòêà âûäà÷è ôîðìû â UTC,
* `4-36_1600000_20260930113005UTC`.
*/
final class TransactionId
{
/**
* Íîìåð çàêàçà PHPShop. Øòàòíî `<÷èñëî>-<ïðåôèêñ>` (`PHPShopDone::setNum()`),
* íî õóê `setNum` äðóãîãî ìîäóëÿ ìîæåò åãî ïåðåîïðåäåëèòü, ïîýòîìó äîïóñêàþòñÿ
* ëàòèíèöà, öèôðû è äåôèñ â ïðåäåëàõ `phpshop_orders.uid varchar(64)` — áåç `_`.
*/
public const ORDER_UID = '[0-9A-Za-z](?:[0-9A-Za-z-]{0,62}[0-9A-Za-z])?';
public const SEPARATOR = '_';
/** Ñóììà â êîïåéêàõ áåç âåäóùèõ íóëåé; 16 öèôð âìåùàþò {@see Money::MAX}. */
private const AMOUNT = '0|[1-9][0-9]{0,15}';
/**
* Ïîÿñ ìåòêè — âñåãäà UTC, áóêâàìè.
*/
private const ZONE = 'UTC';
private const PATTERN = '/\A
(?<uid>' . self::ORDER_UID . ') # íîìåð çàêàçà PHPShop
' . self::SEPARATOR . '
(?<amount>' . self::AMOUNT . ') # ñóììà ôîðìû â êîïåéêàõ
' . self::SEPARATOR . '
[0-9]{14} # ìåòêà âðåìåíè YmdHis
' . self::ZONE . ' # ïîÿñ ìåòêè
\z/x';
/** @var string */
private $orderUid;
/** @var Money */
private $amount;
private function __construct(string $orderUid, Money $amount)
{
$this->orderUid = $orderUid;
$this->amount = $amount;
}
/**
* @param Money $amount ñóììà ôîðìû (`MNT_AMOUNT`)
* @param DateTimeImmutable $issuedAt ìîìåíò âûäà÷è ôîðìû, â ëþáîì ïîÿñå — ìåòêà ïèøåòñÿ â UTC
*/
public static function build(string $orderUid, Money $amount, DateTimeImmutable $issuedAt): string
{
if (preg_match('/\A' . self::ORDER_UID . '\z/', $orderUid) !== 1) {
throw new InvalidArgumentException('Íîìåð çàêàçà íå ïîäõîäèò äëÿ MNT_TRANSACTION_ID.');
}
return $orderUid
. self::SEPARATOR . $amount->toMinorUnits()
. self::SEPARATOR . $issuedAt->setTimezone(new DateTimeZone(self::ZONE))->format('YmdHis') . self::ZONE;
}
/**
* Íîìåð çàêàçà è ñóììà ôîðìû èç ïðèøåäøåãî `MNT_TRANSACTION_ID` èëè null, åñëè ñòðîêà íå íàøåãî ôîðìàòà.
*/
public static function parse(string $raw): ?self
{
if (preg_match(self::PATTERN, $raw, $matches) !== 1) {
return null;
}
try {
$amount = Money::fromMinorUnits((int) $matches['amount']);
} catch (InvalidArgumentException $exception) {
return null;
}
return new self($matches['uid'], $amount);
}
/**
* Íîìåð çàêàçà èç òîãî, ÷òî ââ¸ë àäìèíèñòðàòîð (ôèëüòð æóðíàëà îïåðàöèé): ñàì íîìåð (`33-57`)
* èëè öåëèêîì `MNT_TRANSACTION_ID`, ñêîïèðîâàííûé èç êàáèíåòà MONETA.RU. Èíà÷å — null.
*/
public static function orderUidFromInput(string $input): ?string
{
$input = trim($input);
if (preg_match('/\A' . self::ORDER_UID . '\z/', $input) === 1) {
return $input;
}
$transactionId = self::parse($input);
return ($transactionId === null) ? null : $transactionId->getOrderUid();
}
public function getOrderUid(): string
{
return $this->orderUid;
}
/**
* Ñóììà ïëàò¸æíîé ôîðìû, âûïóñòèâøåé ýòîò íîìåð.
*/
public function getAmount(): Money
{
return $this->amount;
}
}