Зафиксируйте заказ до перехода к оплате
Учебный пример — бот продаёт доступ к набору материалов. До создания платежа сервер сохраняет владельца заказа, состав покупки, сумму, валюту и применённые условия. Браузер или бот передаёт выбор пользователя, но итоговую цену определяет сервер. Это модельный сценарий, а не описание платёжного инцидента клиента.
Свяжите платёж поставщика с конкретным внутренним заказом. Не ищите заказ только по email, приблизительной сумме или последнему открытому экрану: у пользователя могут быть несколько попыток и вкладок. Если условия изменились, создавайте новую согласованную попытку по правилам своей системы, сохраняя историю предыдущей.
| Данные | Как используются |
|---|---|
| Владелец и номер заказа | Определяют, кому и что выдавать после подтверждения. |
| Сумма, валюта, условия | Сверяются с подтверждённым платежом, а не со значениями из браузера. |
| Идентификатор платежа поставщика | Связывает внешнее событие с сохранённой попыткой. |
| Статус и результат выдачи | Позволяют безопасно обработать повтор и восстановиться после сбоя. |
Проверьте подлинность по протоколу вашего поставщика
У разных платёжных сервисов различаются способы проверки уведомлений. ЮKassa описывает проверку текущего состояния объекта и источника уведомления. Stripe предусматривает проверку подписи webhook, для которой важно сохранить исходное тело запроса. Нельзя механически переносить заголовки и алгоритм одного сервиса в интеграцию другого.
Используйте официальный серверный SDK или точную текущую документацию провайдера. После подтверждения источника проверьте, что событие относится к вашему магазину и известной попытке оплаты. Название поля success или paid в пришедшем JSON само по себе не является доказательством.
- Проверьте предусмотренную поставщиком подлинность до выдачи товара или доступа.
- При проверке подписи не подменяйте исходные байты тела повторно собранным JSON.
- Не считайте произвольный X-Forwarded-For надёжным источником IP; доверие к прокси настраивается отдельно.
- Разделите тестовые и рабочие ключи, магазины и события.
Подробнее в первоисточнике: ЮKassa: проверка подлинности входящих уведомлений; Stripe: подпись, повторы и порядок webhook-событий.
Сверьте смысл платежа перед выдачей
Подлинное событие тоже нужно сопоставить с ожиданиями. Сверьте сумму, валюту, получателя, внутренний заказ и окончательный статус, который у этого провайдера означает завершённую оплату. Создание счёта, авторизация суммы и окончательное списание могут быть разными этапами.
В учебном боте платёж за пробный набор не должен открывать годовой доступ только из-за совпадения пользователя. Если данные не сходятся или запрос статуса завершился ошибкой, сохраните состояние «уточняется» и выполните сверку. Не превращайте временную неопределённость в оплаченную покупку или в предложение немедленно заплатить ещё раз.
- Найдите сохранённую попытку по проверенному идентификатору платежа.
- Получите подтверждённое состояние по протоколу поставщика.
- Сверьте его с сохранёнными параметрами заказа.
- Примените допустимый переход статуса и выдайте ровно предусмотренный результат.
- Сохраните причину расхождения для разбора, если проверка не прошла.
Повторное событие не должно повторять покупку
Stripe прямо предупреждает о повторных событиях и отсутствии гарантированного порядка доставки. Для любого выбранного поставщика проверьте его конкретные правила. В приложении нужна идемпотентность: повтор уже выполненной операции узнаётся и не создаёт новое последствие.
У учебной подписки это означает, что повтор webhook не прибавляет второй месяц. Сохранение платежа и выдача доступа должны быть согласованы: сбой между ними не должен оставлять покупателя без покупки или выдавать её дважды. Если выполнение вынесено в очередь, задача также должна узнавать ранее завершённый результат.
Не полагайтесь только на память процесса: после перезапуска она исчезнет. Устойчивый идентификатор операции и ограничения в базе помогают сохранить результат. Возвраты и отмены обрабатываются отдельными допустимыми переходами; позднее старое событие не должно случайно отменить более новое подтверждённое состояние.
Подробнее в первоисточнике: Stripe: подпись, повторы и порядок webhook-событий.
Проверьте неудобные сценарии в тестовом режиме
Используйте тестовые средства своего платёжного сервиса. Заранее определите ожидаемый результат и число записей покупки, чтобы проверка не заканчивалась на коде ответа webhook. Не выполняйте реальные списания только ради повторения этих сценариев.
| Сценарий | Ожидаемый результат |
|---|---|
| Открытие страницы успеха без оплаты | Доступ не выдан; показывается подтверждённое сервером состояние. |
| Уведомление с неверной проверкой подлинности | Нет изменения заказа и выдачи покупки. |
| Подлинное событие с несовпадающей суммой или заказом | Расхождение сохранено, неверная покупка не выдана. |
| Две одинаковые доставки, в том числе одновременно | Один платёж и один результат выдачи. |
| Перезапуск после сохранения платежа | Обработка безопасно продолжается без потери или двойной выдачи. |
| Уведомление задержалось, запрос статуса временно недоступен | Понятное ожидание и последующая сверка вместо ложного успеха. |
| События пришли в другом порядке | Состояние соответствует допустимым переходам и актуальной сверке. |
Сохраните доставку после подключения защиты
Платёжный webhook можно фильтровать на подключённом HTTP-домене, но он остаётся машинной интеграцией. Браузерная проверка или общий жёсткий лимит могут задержать настоящие уведомления. Настройте точный путь, метод, допустимый формат и совместимые правила, затем повторите тестовую доставку через новый маршрут.
RoboGuard помогает управлять HTTP-запросами, которые проходят через него. Подпись, сверка платежа, идемпотентность и выдача покупки выполняются вашим backend. Не открывайте весь API ради одного webhook и не считайте оплату подтверждённой только потому, что запрос пропустила защита.
Вопросы и ответы
Можно ли выдавать покупку по return_url?
Нет. Адрес возврата помогает показать результат пользователю. Право на покупку определяется подтверждённым серверным состоянием связанного платежа.
WAF проверит, действительно ли заказ оплачен?
Нет. WAF фильтрует поддерживаемые признаки HTTP-запросов. Подлинность уведомления, сумма, заказ и результат оплаты проверяются вашей платёжной интеграцией.