Даже подлинное уведомление об оплате не должно приводить к повторному выполнению заказа. Вебхукам криптоплатежей нужны два отдельных механизма защиты: сначала проверить отправителя события, затем сделать повторную обработку безопасной для бизнес-операций. Подпись решает первую задачу. Идемпотентность — вторую.
Это руководство для разработчиков, которые подключают к Yolfi разовые покупки цифровых товаров, лицензий или пополнение баланса. Оно объясняет собственный формат вебхуков Yolfi и рекомендуемую архитектуру приложения, а не описывает готовый сервис выдачи покупок. Для подписок доступ зависит еще и от жизненного цикла подписки: нельзя применять правило разового заказа к каждому продлению.
Почему даже подлинный вебхук может привести к повторной операции
Получатель может успешно сохранить событие, но его HTTP-ответ потеряется. Тогда отправитель повторит доставку. Кроме того, фоновый обработчик может аварийно завершиться после выдачи доступа, но до записи об успешном выполнении. Ни одна из этих ситуаций не означает, что клиент заплатил еще раз.
В справочнике событий вебхуков Yolfi описаны доставка как минимум один раз и независимые повторные попытки для нескольких конечных точек. Проектируйте систему с учетом повторного поступления того же события, в том числе пока первая доставка еще обрабатывается. Не полагайтесь на определенное расписание повторов и не считайте, что уведомления всегда приходят в порядке создания.
Разделяйте три вида идентификаторов:
| Идентификатор | Что он определяет | Рекомендуемый контроль |
|---|---|---|
Подписанный id события |
Одно событие провайдера, включая повторные доставки | Уникальная запись события в пределах провайдера, организации и среды |
| Ссылка на счет или платеж | Одну попытку оплаты | Сохраненная связь с соответствующим коммерческим заказом |
| Идентификатор коммерческого заказа | То, что должен получить клиент | Уникальная операция выполнения этого заказа |
Одного устранения повторов событий недостаточно: у двух успешных попыток оплаты одного заказа могут быть разные идентификаторы событий. Защита на уровне заказа не позволит выдать вторую лицензию или повторно пополнить баланс, пока дополнительное поступление разбирают отдельно.
Проверяйте исходный запрос, прежде чем доверять его полям
Следуйте актуальной инструкции Yolfi по проверке подписи. Запросы в собственном формате используют X-Yolfi-Signature: это HMAC-SHA256 от исходного тела запроса, закодированный в Base64 и рассчитанный с секретом подписи данной конечной точки. API-ключ организации — не секрет вебхука.
Сохраните исходное тело до того, как промежуточный обработчик JSON изменит его. Рассчитайте подпись и сравните ее функцией с постоянным временем выполнения; отклоняйте отсутствующие или некорректные подписи, не выполняя бизнес-операций. Храните секреты подписи в хранилище секретов или защищенной конфигурации окружения, а не в исходных файлах или журналах службы поддержки.
Разбирайте и проверяйте содержимое только после проверки подписи. Для устранения повторов используйте id из подписанного тела. X-Yolfi-Event-ID — вспомогательная копия, а не отдельно подписанное значение; если используете этот заголовок, требуйте его совпадения с идентификатором в теле. Привяжите принимающую конечную точку к ожидаемой организации и проверяйте orgId, если он присутствует. Метаданные, переданные пользователем, не должны определять права доступа к данным организации.
Корректная подпись не делает старую доставку новой. Идентификаторы обработанных событий все равно нужно сохранять. Описанная в документации подпись Yolfi не проверяет свежесть по временной метке, поэтому нельзя утверждать, что одна лишь проверка HMAC предотвращает повторное воспроизведение запросов.
Сначала сохраните, затем подтвердите прием и обрабатывайте асинхронно
Используйте надежное хранилище входящих событий: таблицу приложения, которая сохраняет прошедшее проверку событие и состояние его обработки. Вставляйте событие с ограничением уникальности в базе данных, а не через отдельную проверку «уже существует?» с последующей незащищенной вставкой. Одновременные запросы могут оба пройти такую проверку.
Быстро возвращайте 200 OK после надежного сохранения, в том числе когда подлинный дубликат уже записан. Если сохранение не удалось, не подтверждайте прием события. Быстрый ответ с последующей задачей в оперативной памяти не обеспечивает надежности: при перезапуске может исчезнуть единственная копия, которую сохранило приложение.
Затем фоновый обработчик может загрузить сохраненное событие, проверить его связь с заказом и выполнить переход бизнес-состояния. Ошибки обработки должны оставаться видимыми: храните число повторов, следующее действие и ответственного за оповещения. Сохраняйте только необходимые данные события в хранилище с ограниченным доступом: тело события может содержать сведения о клиенте.
Выполняйте заказ только после подтверждения и проверки связи с ним
В руководстве Yolfi по вебхукам событие payment.confirmed указано как основной сигнал для выполнения заказа. invoice.created означает создание счета, а не оплату клиентом. Перенаправление браузера или снимок экрана не заменяет проверенное подтверждение на стороне сервера.
Сопоставьте событие с заказом, сохраненным на сервере. В собственном формате отдельные события API Checkout могут содержать checkoutSessionId и merchantOrderId без paylinkId. Не отклоняйте корректную отдельную покупку только из-за отсутствия идентификатора Paylink. В то же время один лишь идентификатор многоразовой ссылки Paylink не определяет заказ покупателя однозначно.
Перед выдачей покупки проверьте ожидаемую организацию, среду, попытку оплаты и контекст суммы заказа. Описанное в документации поле amount — итоговая сумма к получению продавцом в платежном токене; она не обязательно равна исходной сумме счета в местной валюте. Не сравнивайте эти числа так, будто у них одинаковые единицы измерения. Сохраняйте исходный заказ, одобренный сервером, и условия расчета его цены.
Для покупок с динамическими параметрами используйте процесс API Checkout, чтобы сохранить связь заказа с сессией. Для фиксированных товаров платежные ссылки дают многоразовую точку входа в оплату; решение о выдаче покупки все равно принимает ваше приложение.
Защищайте результат операции, а не только таблицу событий
Если выполнение заказа целиком происходит в вашей базе данных, записывайте результат события и изменение состояния заказа в одной транзакции. Обеспечьте уникальность бизнес-операции, например однократную выдачу права доступа по разовому заказу. Повторное событие должно возвращать существующий результат, а не выдавать новое право доступа.
Если выполнение заказа требует обращения к другому сервису, используйте надежную очередь исходящих действий: сохраняйте планируемое действие вместе с изменением состояния заказа, а затем поручайте его доставку фоновому обработчику. Если получатель поддерживает ключ идемпотентности, задайте стабильный ключ на основе заказа. Сама по себе очередь исходящих действий не предотвращает повторную удаленную операцию, если обработчик аварийно завершится после ее успешного выполнения.
Если получатель не поддерживает идемпотентность или проверку результата, оставляйте неопределенный исход для сверки вместо слепого повтора необратимой операции. HTTP-вебхук не может гарантировать выполнение «ровно один раз» во всех базах данных, очередях и внешних сервисах.
Не позволяйте устаревшим событиям возвращать выполненный заказ в предыдущее состояние. Событие invoice.overdue относится к одной попытке оплаты и не должно отменять выполненный заказ. Для регулярного доступа провайдер отделяет события счетов от subscription.overdue и subscription.cancelled; реализуйте документированные правила подписок отдельно.
Проверяйте сбои до приема реальных заказов
Используйте эти приемочные сценарии для получателя и фонового обработчика:
- Доставьте одно подписанное событие дважды, в том числе одновременно: одна запись события и одна бизнес-операция.
- Прервите обработчик до и после изменения состояния заказа: восстановление не должно выдавать доступ дважды.
- Доставьте две подтвержденные попытки оплаты одного заказа: выполните заказ один раз и отметьте дополнительное поступление.
- Отправьте некорректную подпись, несовпадающую организацию и неизвестный заказ: покупка не выдается.
- Доставьте старое событие счета после подтверждения: выполненный заказ не возвращается в предыдущее состояние.
- Отправьте событие тестового режима получателю рабочей среды: никакой реальной лицензии, отгрузки или зачисления на счет.
Тестовые события в панели управления Yolfi используют livemode: false и идентификатор с префиксом evt_test_. Они проверяют доставку и обработку содержимого, но не создают реальных платежных записей. Явно разделяйте тестовые и реальные действия: успешное тестовое уведомление не доказывает, что покупка оплачена.
Отслеживайте прошедшие проверку события, ожидающие обработки, сбои фоновых обработчиков и подтвержденные платежи без результата выполнения заказа. Включите эти исключения в контрольный список сверки платежей. Если второе поступление — действительно дополнительный платеж, а не повторная доставка, следуйте порядку проверки возвратов, а не удаляйте подтверждающие данные.
Часто задаваемые вопросы
Предотвращает ли проверка подписи повторные зачисления?
Нет. Она подтверждает подлинность и целостность. Подлинное событие может прийти несколько раз, а ранее корректный запрос можно воспроизвести повторно. Сохраняйте идентификаторы событий и обеспечивайте бизнес-ограничение на уровне заказа.
Нужно ли возвращать успешный ответ до сохранения события?
Нет. Подтверждайте прием после надежного сохранения, а более долгую работу выполняйте асинхронно. Иначе сбой после отправки ответа может привести к незаметной потере события.
Достаточно ли хеша транзакции для устранения повторов?
Для обработки вебхуков — нет. Используйте документированный подписанный идентификатор события для доставок и собственный ключ заказа для выполнения покупки. Переводы и заказы имеют разные идентификаторы; одна транзакция может содержать несколько переводов.
Можно ли использовать тот же обработчик для данных адаптеров?
Не проверив их контракт — нельзя. Эта статья описывает собственный формат с адаптером NONE. Адаптеры совместимости с провайдерами могут передавать данные иной структуры; прежде чем повторно использовать разбор и правила сопоставления собственного формата, проверьте документированные поля адаптера.
Итог: подтвердите, сохраните и выполните один раз
Надежная автоматизация криптоплатежей отделяет проверенную доставку от бизнес-результата заказа. Проверьте подпись исходного тела, сохраните событие, сопоставьте заказ и обеспечьте безопасное выполнение при повторах. Начните с платежных ссылок Yolfi для фиксированных товаров или документированной сессии, созданной сервером, для динамических заказов; ответственность за однократную выдачу каждой покупки оставьте приложению.
