Принимаем оплату через ЮKassa: заказ, вебхук и честный статус

В демо-магазине demo-form.dynapi.ru покупатель выбирает кофе, заполняет форму заказа и нажимает «Оплатить». Дальше покупателя ведут три сценария: первый создаёт заказ и уводит на страницу ЮKassa, второй принимает вебхук о платеже, третий показывает статус заказа. Собирает всю цепочку визард: восемь вопросов в окне создания сценария, на выходе — три сценария и шаблон, которые остаётся просмотреть и опубликовать. Разбираем цепочку по шагам: что происходит между «Оплатить» и «Заказ оплачен», как приёмник вебхука проверяет уведомление и почему статус заказа читается из базы, а не из редиректа.
Все платежи в демо — тестовые: магазин подключён к тестовому кабинету ЮKassa, деньги не двигаются.
Что собирает визард
Пресет «Приём оплаты через ЮKassa» живёт в окне создания сценария: Сайт → Сценарии → «Создать сценарий». Визард спрашивает восемь вещей:
- тип контента заказа и поля суммы и статуса в нём (в демо — тип
orderс полямиamountиstatus); - id магазина и секретный ключ из личного кабинета ЮKassa — ключ визард прячет в Секреты и подставляет в Basic-заголовок запросов к API;
- токен для адреса вебхука;
- базовый адрес сайта и путь страницы статуса.


На выходе — три сценария и шаблон статус-страницы. Маршруты визард публикует сам: POST-биндинг сценария оплаты вешает на маршрут сценария (его путь вы называете при создании — в демо это /yookassa/checkout), а страницу статуса и приёмник вебхука ставит на собственные маршруты — GET /payment/return и POST /yookassa-hook/<токен>. Сами сценарии и шаблон визард оставляет черновиками — их смотрят и публикуют вручную; тост в конце так и говорит: «Проверь результат и опубликуй». После публикации магазин принимает платежи.

Оплата: заказ, платёж, редирект
Форму заказа магазин отправляет прямо на POST /yookassa/checkout — сценарий оплаты висит на этом маршруте. Дальше четыре действия:
- GraphQL создаёт запись заказа — в демо со статусом «в обработке» (в данных он записан как
pending). - HTTP POST уходит в
api.yookassa.ru/v3/payments: сумма, описание иreturn_url— адрес статуса, собранный из базового адреса сайта и пути. - GraphQL дописывает в заказ идентификатор платежа, вернувшийся от ЮKassa.
- «Редирект» отправляет покупателя на
confirmation_url— страницу ЮKassa, где вводятся карточные данные.
Сумма — самое капризное поле цепочки. Из формы она приходит без фиксированного формата: «990» или «990.5». ЮKassa же требует строку с двумя знаками после запятой. Пресет прогоняет сумму через фильтр money, который приводит значение к виду «990.00» — и в заказ, и в платёж уходит одно и то же число в одном и том же формате. Без этой нормализации сверка вебхука разваливается на ровном месте: «990» и «990.00» для сравнения строк — разные значения.
Вебхук: сверка перед доверием
Оплату ЮKassa подтверждает вебхуком — POST на адрес приёмника. Адрес содержит длинный токен, но постучаться на него может кто угодно: токен в URL — не доказательство платежа. Поэтому приёмник не верит самому факту уведомления. Он ищет в базе заказ, у которого совпадают и идентификатор платежа из уведомления, и сумма. Нашёл — ставит статус paid («оплачен»). Не нашёл — отвечает 400 mismatch и ничего не трогает.
Проверить защиту можно прямо из командной строки — послать уведомление с выдуманным идентификатором платежа:
curl -X POST https://demo-form.dynapi.ru/yookassa-hook/<токен> \
-d '{"event":"payment.succeeded","object":{"id":"fake-id","status":"succeeded","amount":{"value":"990.00","currency":"RUB"}}}'
Приёмник отработает три узла — поиск заказа, условие, ответ — и вернёт отказ. В журнале запусков прогон выглядит как штатный: сценарий не сломался, отказ — его решение.

Настоящий вебхук проходит ту же сверку, только с совпадающими полями: заказ находится, статус меняется на «оплачен». Обычно вебхук приходит раньше, чем покупатель успевает вернуться с платёжной страницы.
Статус: из базы, а не из редиректа
Расплатившись, покупатель возвращается на /payment/return?order=<id>. Страница статуса — тоже сценарий: один запрос GraphQL читает заказ из базы, шаблон рисует то, что в базе написано. «Заказ оплачен» появляется только после вебхука; пока тот не пришёл, покупатель видит «Заказ в обработке» — даже если страница открыта в ту же секунду после редиректа. Редирект — это чьё-то слово, запись в базе — факт.

Повторить у себя
Понадобятся тестовый магазин ЮKassa (id и секретный ключ — в личном кабинете) и сайт на платформе. Дальше: Сайт → Сценарии → «Создать сценарий» → пресет «Приём оплаты через ЮKassa», восемь вопросов, публикация сценариев и шаблона. Форму на сайте визард не создаёт — добавьте форму, которая отправляет название и сумму POST-запросом на маршрут сценария оплаты (в демо это форма заказа кофе). В личном кабинете ЮKassa пропишите адрес вебхука — его показывает визард.
Полный разбор пресета — с Liquid-шаблонами, фильтром money и настройкой вебхука — в документации: Визард «Приём оплаты через ЮKassa».
All posts