Files
PHPShop/moneta/class/host/OperationRepository.php

134 lines
4.8 KiB
PHP

<?php
declare(strict_types=1);
namespace MonetaPhpShop\host;
use MonetaPhpShop\callback\OperationJournal;
use MonetaPhpShop\ModuleInfo;
use MonetaPhpShop\order\Order;
use MonetaPhpShop\protocol\CallbackNotification;
use MonetaPhpShop\protocol\ResultCode;
use MonetaPhpShop\protocol\TransactionId;
use RuntimeException;
/**
* Æóðíàë îïåðàöèé — òàáëèöà `[base] moneta_operations` èç `inc/config.ini` ìîäóëÿ.
*
* Ñòðîêà íà êàæäîå ïîäïèñàííîå óâåäîìëåíèå; ó ñòðîêè çà÷ò¸ííîé îïëàòû çàïîëíåí
* `paid_order_id` ñ UNIQUE-êëþ÷îì — âòîðîé çà÷¸ò òîãî æå çàêàçà íåâîçìîæåí äàæå áåç áëîêèðîâêè.
* Ïîäïèñü è êîä ïðîâåðêè öåëîñòíîñòè íå õðàíÿòñÿ.
*/
final class OperationRepository implements OperationJournal
{
private const COMMAND_PAY = 'PAY';
/** @var Database */
private $database;
public function __construct(Database $database)
{
$this->database = $database;
}
public function findPaidOperation(int $orderId): ?string
{
$value = $this->database->fetchValue(
'SELECT operation_id FROM `' . self::table() . '` WHERE paid_order_id = ? LIMIT 1',
[$orderId],
);
return $value === null ? null : (string) $value;
}
public function recordPaid(CallbackNotification $notification, Order $order, string $event): bool
{
try {
$this->insert($notification, $order, $event, ResultCode::paid(), $order->getId());
} catch (RuntimeException $exception) {
if ($exception->getCode() === Database::ER_DUP_ENTRY) {
return false;
}
throw $exception;
}
return true;
}
public function note(
CallbackNotification $notification,
?Order $order,
string $event,
?ResultCode $resultCode
): void {
$this->insert($notification, $order, $event, $resultCode, null);
}
/**
* Ïîñëåäíèå çàïèñè æóðíàëà äëÿ àäìèíêè, íîâûå ñâåðõó.
*
* @param string|null $orderUid òîëüêî çàïèñè ýòîãî çàêàçà ({@see TransactionId::orderUidFromInput()}):
* ñ ýòèì `order_uid` èëè — åñëè çàêàç íå ðàñïîçíàí — ñ `MNT_TRANSACTION_ID` âèäà `<íîìåð>_…`
*
* @return array<int, array<string, mixed>>
*/
public function latest(int $limit, ?string $orderUid = null): array
{
$where = '';
$params = [];
if ($orderUid !== null) {
// Â íîìåðå çàêàçà íåò `_` è `%` (TransactionId::ORDER_UID) — ýêðàíèðîâàòü â LIKE
// íóæíî òîëüêî ñàì ðàçäåëèòåëü `_`.
$where = ' WHERE order_uid = ? OR transaction_id LIKE ?';
$params = [$orderUid, $orderUid . '\\' . TransactionId::SEPARATOR . '%'];
}
$params[] = $limit;
return $this->database->fetchAll(
'SELECT id, created_at, command, transaction_id, operation_id, order_id, order_uid, amount,'
. ' test_mode, result_code, event FROM `' . self::table() . '`' . $where . ' ORDER BY id DESC LIMIT ?',
$params,
);
}
public static function table(): string
{
$table = $GLOBALS['SysValue']['base'][ModuleInfo::PATH]['moneta_operations'] ?? null;
// ßâíûé êëàññ, à íå \w: áåç /u \w ñëåäóåò ëîêàëè, à PHPShop ñòàâèò ñâîþ (setlocale â
// class/base.class.php) — â cp1251 \w ïðîïóñòèë áû êèðèëëèöó â èìÿ òàáëèöû, êîòîðîå èä¸ò â SQL.
if (!is_string($table) || preg_match('/\A[0-9A-Za-z_]+\z/', $table) !== 1) {
throw new RuntimeException('Ìîäóëü íå óñòàíîâëåí: íåò òàáëèöû îïåðàöèé â êîíôèãóðàöèè.');
}
return $table;
}
private function insert(
CallbackNotification $notification,
?Order $order,
string $event,
?ResultCode $resultCode,
?int $paidOrderId
): void {
$amount = $notification->getAmount();
$this->database->execute(
'INSERT INTO `' . self::table() . '` (created_at, command, transaction_id, operation_id, order_id,'
. ' order_uid, amount, test_mode, result_code, event, paid_order_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
[
time(),
$notification->isCheck() ? $notification->getCommand() : self::COMMAND_PAY,
$notification->getTransactionId(),
$notification->getOperationId(),
($order !== null) ? $order->getId() : 0,
($order !== null) ? $order->getUid() : '',
($amount !== null) ? $amount->toDecimal() : '',
$notification->getRawTestMode(),
($resultCode !== null) ? $resultCode->getValue() : null,
$event,
$paidOrderId,
],
);
}
}