1. Webhook сначала сохраняем
Не делай тяжёлую обработку прямо в webhook request. Сначала сохрани payload, ответь 200, дальше обработай worker’ом.
POST /webhooks/payment
1. verify signature
2. save raw payload
3. return 200 OK
4. enqueue processing job2. Idempotency key
Один и тот же webhook может прийти несколько раз. Используй внешний event_id или собственный idempotency key.
if Event.objects.filter(external_id=event_id).exists():
return "already processed"
Event.objects.create(external_id=event_id, payload=payload)3. Retry policy
- Сетевые ошибки повторять.
- HTTP 429 повторять позже.
- HTTP 400 не повторять без исправления payload.
- После нескольких ошибок отправлять alert.
4. Очередь и dead letter
Если обработка несколько раз падает, событие не должно исчезать. Переводи его в failed/dead-letter состояние и показывай в админке кнопку ручного повторения.
status: pending -> processing -> processed
status: processing -> failed
manual action: retry failed event5. Подписи и безопасность
- Проверяй signature/HMAC, если сервис её даёт.
- Не доверяй сумме платежа с frontend.
- Храни raw payload для разбора спорных случаев.
- Не логируй токены и персональные данные без нужды.
6. Версии API
Не ломай старых клиентов без переходного периода. Для публичного API лучше сразу держать версию в URL или header.
/api/v1/orders
/api/v1/webhooks/payment
Accept: application/vnd.pyaidev.v1+jsonChecklist перед запуском
- Есть таблица входящих событий.
- Повтор webhook не создаёт дубль заявки.
- Ошибки обработки видны в админке или логах.
- Есть ручной retry для failed events.
- Есть test endpoint или sandbox mode.