На странице
Платёжная страница
Перенаправляйте клиента на страницу оплаты или встраивайте её в защищённый iframe, а заказ исполняйте после подписанного вебхука.
Используйте payment_url, который возвращает POST /v1/invoices, как основной URL оплаты. Один и тот же URL работает в двух режимах: открытие на всю страницу (redirect) и встраивание в iframe (embed).
Оба режима работают на всех каналах проекта, кроме «Telegram-бот»: там тот же payment_url открывает бота Paymos, платёжной страницы нет — переходить и встраивать нечего.
Переход на страницу оплаты
Это самый простой способ интеграции. Создайте инвойс на сервере и отправьте клиента на payment_url:
<a href="https://checkout.paymos.io/invoice/inv_xxx">Оплатить инвойс</a>
Клиент оплачивает на странице, размещённой на стороне Paymos. Заказ на вашем сервере выполняется только после того, как вы получили и проверили подписанное событие вебхука.
Встраивание в iframe
Этот режим использует тот же payment_url, но с параметром ?embed=true. Подойдёт, когда инвойсы уже создаются на вашем сервере, а от Paymos нужен только интерфейс оплаты внутри вашей страницы:
<iframe
src="https://checkout.paymos.io/invoice/inv_xxx?embed=true"
title="Paymos checkout"
allow="clipboard-write"
sandbox="allow-scripts allow-same-origin allow-forms"
style="display:block;width:100%;max-width:460px;height:min(90vh,900px);border:0;"
></iframe>
События iframe
Страница оплаты внутри iframe отправляет события через postMessage на родительскую страницу:
| Событие | Значение |
|---|---|
paymos:succeeded |
Оплата перешла в статус paid или paid_over. |
paymos:failed |
Оплата завершилась неуспехом — конечный статус underpaid, expired или cancelled. |
paymos:close |
Клиент нажал «закрыть» или «назад» внутри встроенной страницы оплаты. |
Родительская страница обязана проверять и origin отправителя, и то, что сообщение пришло именно из вашего iframe:
const iframe = document.querySelector('#paymos-checkout');
window.addEventListener('message', (event) => {
if (event.origin !== 'https://checkout.paymos.io') return;
if (event.source !== iframe.contentWindow) return;
if (!event.data || typeof event.data !== 'object') return;
switch (event.data.type) {
case 'paymos:succeeded':
console.log('Payment UI succeeded:', event.data.invoice_id);
break;
case 'paymos:failed':
console.warn('Payment UI failed:', event.data.reason);
break;
case 'paymos:close':
iframe.remove();
break;
}
});
Эти события — подсказка для интерфейса, а не подтверждение оплаты. По ним можно показать индикатор загрузки, выполнить переход на страницу успеха и отправить событие в аналитику, но никогда не выдавайте товар, не пополняйте баланс и не помечайте заказ оплаченным на основании postMessage.
Сообщение postMessage легко подделать: любая страница в том же origin может отправить фальшивый paymos:succeeded. Оплата считается подтверждённой только подписанным вебхуком на стороне сервера — это единственный источник правды для выдачи товара.
Модель безопасности
Рекомендуемый набор sandbox для iframe намеренно сделан минимальным:
- —
allow-scriptsнужен для работы страницы оплаты: запросов к API, таймеров, кнопок копирования и обновления статуса в реальном времени. - —
allow-same-originпозволяет странице оплаты работать в собственномorigin, а не в обезличенном (opaque) origin песочницы. - —
allow-formsразрешает обычную работу с формами внутри страницы оплаты. - —
allow-top-navigationнамеренно не указан, чтобы iframe не мог перенаправить страницу мерчанта. - —
allow-popupsне указан, потому что странице оплаты не нужно открывать новые окна браузера.
Когда вместо этого нужен Low-Code SDK
Используйте Low-Code SDK, когда хотите, чтобы Paymos сам создавал инвойсы по настройкам на стороне браузера, рисовал кнопку оплаты, управлял жизненным циклом модального окна, проверял сообщения из iframe и за вас отправлял события paymos:* через CustomEvent.
Встраивайте iframe вручную, когда инвойсы уже создаёт ваш бэкенд, а вам нужно лишь разместить интерфейс оплаты внутри своей страницы.
Одно условие: Low-Code SDK открывается только в проекте со способом подключения Low-Code — в остальных POST /public/v1/sdk отвечает 403 embed_not_enabled. У ручного iframe такого ограничения нет: он принимает payment_url любого проекта, у которого есть платёжная страница.