Webhook нельзя обрабатывать как обычную форму
Callback от платежной системы должен проходить проверку подлинности, суммы и состояния заказа.
Webhook приходит не от пользователя, а от внешней системы. Поэтому обработчик должен быть маленьким, предсказуемым и защищенным от повторных запросов. Он не должен выполнять тяжелые операции синхронно, если их можно вынести в очередь.
Минимальная проверка включает подлинность события, соответствие суммы и валюты, наличие заказа, допустимость перехода статуса и защиту от дублей. Только после этого можно менять бизнес-статус.
- Проверить подпись или доверенный механизм провайдера.
- Сверить сумму, валюту и order id.
- Проверить, не финальный ли уже статус.
- Записать raw event в журнал.
- Вернуть провайдеру быстрый технический ответ.
Статусы должны быть конечным автоматом
Без явных правил переходов заказ может стать оплаченным, потом ожидающим, а затем ошибочным из-за позднего события.
Платежные статусы нужно описывать как конечный автомат. Есть начальные, промежуточные и финальные состояния. Финальные статусы нельзя случайно перезаписывать более старыми событиями.
Например, если заказ уже имеет статус paid, поздний pending callback не должен откатывать заказ назад. Если пришел refund, это отдельное бизнес-событие, а не просто failed.
- Created и Pending - не финальные состояния.
- Paid, Failed, Cancelled - финальные для оплаты.
- Refunded/Partially refunded - отдельная ветка после успешной оплаты.
- Любой запрещенный переход должен логироваться.
Retries и очереди защищают от временных сбоев
CRM, ERP или фискальный сервис могут быть недоступны, но платежное событие не должно потеряться.
Если после оплаты нужно отправить данные в CRM, ERP или сервис фискализации, лучше делать это через очередь. Платежный webhook сохраняется быстро, а внутренние интеграции получают событие с повторными попытками.
Это снижает риск ситуации, когда деньги списаны, но заказ не обновился из-за временного сбоя сторонней системы. В журнале должно быть видно, какие доставки были успешны, какие ждут retry и какие требуют ручного разбора.
- Сохранять событие до обработки.
- Использовать exponential backoff для повторов.
- Иметь dead-letter статус для ручного разбора.
- Не повторять списание при повторе внутренней доставки.
FAQ
Webhook должен сразу менять заказ?
Он может менять заказ только после проверки подлинности, суммы, валюты, order id и допустимости перехода статуса.
Что делать, если CRM недоступна после успешной оплаты?
Сохранить платежное событие и доставлять его в CRM через очередь с повторными попытками. Оплата не должна теряться из-за временного сбоя CRM.
Можно ли принимать один webhook несколько раз?
Да, обработчик должен быть идемпотентным. Повтор события не должен создавать повторные заказы, чеки или статусы.