_<сумма>_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 */ private $fields; /** * @param array $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 */ 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 = '
'; foreach ($this->fields as $name => $value) { $html .= ''; } $html .= ''; return $html . '
'; } private static function escape(string $value): string { return htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'Windows-1251'); } }