119 lines
3.7 KiB
PHP
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;
|
|
}
|
|
}
|