IOSOR База знаний

Дубликат webhook не должен писать второй debit

Fail path: retries и replays остаются идемпотентными для prepaid money и inbox — один event ID, одна строка debit, одна строка inbox.

At-least-once доставка будет ретраить. Дубликат webhook, который пишет второй debit или вторую строку inbox — инцидент money/ops, а не «безобидный ACK». Эта страница — fail path: retries и replays идемпотентны для prepaid money и inbox. Это не эссе про API send-idempotency и не playbook inbound SMS retry.

Связанные: Гейт подписи и окна replay, Контракт webhook до первой отправки, debit и delivery status в одном ledger.

IOSOR — white-label prepaid. USD 20 оплачивает smoke дубликата на одном consumer; soft review около USD 1 000/мес делает «retry = новый charge» долгом recon.

Идемпотентность — fail path, не слоган

Happy path прост: одно signed событие, один accept, один debit. Fail path сжигает доверие — timeout, 5xx, provider replay, ручной re-push. Idempotency key из Контракт webhook до первой отправки сохраняют до side effects: ledger, inbox, CRM.

Что считается дубликатом

Сигнал Дубликат, когда Безопасный исход
Event ID Тот же ID уже accepted in-window ACK; нет второго debit
Message ID То же сообщение уже в ledger Переиспользовать строку
Inbox key Тот же MO/MT уже в файле Нет второй строки inbox
Вне окна replay Stale retry после gate reject Reject; нет money/status write
Unknown type Нет в списке контракта Drop;

Money не должна двигаться дважды

Второй debit за тот же event ID — баг, даже если product «всё ещё показывает delivered». Finance фильтрует по event/message ID и видит одну prepaid-строку за UTC-окно. Частичные side effects после ACK — сначала CRM, потом ledger — создают double truth. Если processing упал после persist, ретрайте worker по тому же ключу. Soft volume language blocked, пока smoke не покажет одну строку ledger.

Inbox тоже не должен удваиваться

Идемпотентность — не только money. Replay inbound/delivery, который открывает второй inbox thread, учит support гоняться за призраками и может запустить autoreply loops. Храните inbox key с тем же event ID, что и debit. Product и finance делят слова reject/duplicate — не hero upstream codes: Общий язык статусов для product и finance.

Чеклист покупателя: duplicate-safe webhooks

  1. Форма idempotency key согласована в контракте и сохранена до side effects?
  2. Дубликат in-window event ID → ACK без второго debit?
  3. Тот же message ID никогда не открывает вторую prepaid-строку ledger?
  4. Inbox insert с тем же ключом — нет второго thread на replay?
  5. Signature fail и window reject считаются отдельно от true duplicates?

6.

Начните с IOSOR

Принудительно повторите один подписанный webhook внутри окна на коридоре, который уже списан. Выгрузите event id рядом с id в ledger и докажите одну строку debit и одну строку inbox. Если появится второе списание — остановите этот consumer и верните лишнюю строку, не «усредняйте» её следующим трафиком. Это деньги replay, не проверка E.164 и не форма SMS-доставки.

Итог IOSOR

Повтор — не новая отправка. Один event id пишет один debit.

Делайте: держите подпись и окно replay, затем докажите один debit после POST внутри окна. Не делайте: списывать каждый POST или считать сетевой retry вторым счётом.

Был ли материал полезен?

Связанные гайды