feat: moneta 2.0, MONETA.Assistant payment module for PHPShop 6.7+

This commit is contained in:
2026-10-27 11:45:22 +03:00
parent 78b7405637
commit f8c12ce44a
82 changed files with 7387 additions and 58 deletions
+151
View File
@@ -0,0 +1,151 @@
<?php
declare(strict_types=1);
namespace MonetaPhpShop\form;
use DateTimeImmutable;
use MonetaPhpShop\order\Order;
use MonetaPhpShop\PhoneNumber;
use MonetaPhpShop\protocol\AssistantProtocol;
use MonetaPhpShop\protocol\TransactionId;
use MonetaPhpShop\Settings;
/**
* Ïëàò¸æíàÿ ôîðìà MONETA.Assistant.
*
* Êàæäûé ïîêàç ôîðìû — íîâûé `MNT_TRANSACTION_ID` (`<uid>_<ñóììà>_<YmdHis>UTC`): ïîâòîðíóþ îïëàòó
* óæå îïëà÷åííîãî çàêàçà îñòàíàâëèâàåò Check URL.
*/
final class PaymentForm
{
/** Ïðåôèêñ ID ïîêóïàòåëÿ PHPShop â `MNT_SUBSCRIBER_ID`. */
public const SUBSCRIBER_USER_PREFIX = 'USER_';
/** Òåêñò êíîïêè ïîñëå íàæàòèÿ; áåç êàâû÷åê è ðàçìåòêè — ïîäñòàâëÿåòñÿ â onsubmit. */
private const REDIRECTING = 'Ïåðåõîäèì ê îïëàòå...';
/** @var string */
private $action;
/** @var array<string, string> */
private $fields;
/**
* @param array<string, string> $fields
*/
private function __construct(string $action, array $fields)
{
$this->action = $action;
$this->fields = $fields;
}
/**
* @param string $currency `MNT_CURRENCY_CODE` ({@see AssistantProtocol::normalizeCurrency()})
* @param ReturnUrls $returnUrls àäðåñà âîçâðàòà ïîêóïàòåëÿ
* @param string $description `MNT_DESCRIPTION` â êîäèðîâêå âèòðèíû
* @param string $cms `MNT_CMS`
* @param string $locale `moneta.locale` — ÿçûê ïëàò¸æíîé ôîðìû ({@see AssistantProtocol::normalizeLocale()})
*/
public static function create(
Settings $settings,
Order $order,
string $currency,
DateTimeImmutable $issuedAt,
ReturnUrls $returnUrls,
string $description,
string $cms,
string $locale
): self {
$amount = $order->getAmount();
$transactionId = TransactionId::build($order->getUid(), $amount, $issuedAt);
$subscriberId = self::subscriberId($order);
$testMode = $settings->isTestMode();
$fields = [
'MNT_ID' => $settings->getAccountId(),
'MNT_TRANSACTION_ID' => $transactionId,
'MNT_CURRENCY_CODE' => $currency,
'MNT_AMOUNT' => $amount->toDecimal(),
'MNT_TEST_MODE' => AssistantProtocol::testModeFlag($testMode),
'MNT_SUBSCRIBER_ID' => $subscriberId,
'MNT_SIGNATURE' => $settings->signature()->forPaymentForm(
$settings->getAccountId(),
$transactionId,
$amount,
$currency,
$subscriberId,
$testMode,
),
'MNT_DESCRIPTION' => $description,
'MNT_SUCCESS_URL' => $returnUrls->getSuccess(),
'MNT_FAIL_URL' => $returnUrls->getFail(),
'MNT_INPROGRESS_URL' => $returnUrls->getInProgress(),
'MNT_RETURN_URL' => $returnUrls->getReturn(),
'MNT_CMS' => $cms,
'moneta.locale' => $locale,
];
return new self($settings->getPaymentServer()->url(), $fields);
}
/**
* `MNT_SUBSCRIBER_ID`: email -> òåëåôîí (`+öèôðû`) -> ID ïîêóïàòåëÿ PHPShop; ïóñòî — äàííûõ íåò
*/
public static function subscriberId(Order $order): string
{
if ($order->getEmail() !== '') {
return $order->getEmail();
}
$phone = PhoneNumber::normalize($order->getPhone());
if ($phone !== null) {
return $phone;
}
return ($order->getUserId() > 0) ? self::SUBSCRIBER_USER_PREFIX . $order->getUserId() : '';
}
public function getAction(): string
{
return $this->action;
}
/**
* @return array<string, string>
*/
public function getFields(): array
{
return $this->fields;
}
/**
* HTML ôîðìû ñ îäíîé êíîïêîé; ñòðîêè â êîäèðîâêå âèòðèíû (cp1251).
*
* `accept-charset="UTF-8"`: áðàóçåð ïåðåêîäèðóåò çíà÷åíèÿ èç cp1251 ñòðàíèöû â UTF-8,
* êîòîðóþ æä¸ò Ìîíåòà. Êíîïêà ïîñëå íàæàòèÿ áëîêèðóåòñÿ (îò äâîéíîé îòïðàâêè), ïîêà îòêðûâàåòñÿ ñòðàíèöà Ìîíåòû.
*/
/**
* @param string $buttonTitle òåêñò êíîïêè, ýêðàíèðóåòñÿ
* @param string $buttonSuffixHtml ãîòîâûé HTML ïîñëå òåêñòà (çíàê âàëþòû âèòðèíû), íå ýêðàíèðóåòñÿ
*/
public function toHtml(string $buttonTitle, string $buttonSuffixHtml = ''): string
{
$html = '<form class="moneta-payment-form" method="post" action="' . self::escape($this->action) . '"'
. ' accept-charset="UTF-8"'
. ' onsubmit="var b=this.querySelector(\'button\');if(b){b.disabled=true;'
. 'b.textContent=\'' . self::REDIRECTING . '\';}">';
foreach ($this->fields as $name => $value) {
$html .= '<input type="hidden" name="' . self::escape($name) . '" value="' . self::escape($value) . '">';
}
$html .= '<button type="submit" class="btn btn-primary moneta-payment-button">'
. self::escape($buttonTitle) . $buttonSuffixHtml . '</button>';
return $html . '</form>';
}
private static function escape(string $value): string
{
return htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'Windows-1251');
}
}