Webhook — delivery and ordering guarantees
Webhook emission
Section titled “Webhook emission”Confirmed against backend code (merchants/consumers.py):
- Synchronous within the request that changes the state. The
payment_merchantsignal fires insidePayment.update(), and the handler runs in the same thread as the call. - Single attempt —
session.send(request)with notimeout=or retry loop. - If the merchant returns
HTTP > 204or is down, the webhook is lost (logged as an error on the backend side; no retry).
Delivery order
Section titled “Delivery order”Deduplication pattern
Section titled “Deduplication pattern”async function handleWebhook(body) { const existing = await db.orders.findByB4bitId(body.identifier);
if (existing && new Date(body.edited_at) <= new Date(existing.last_update)) { // webhook viejo, ya tenemos info más reciente. logger.debug('Webhook obsoleto, ignorando', { identifier: body.identifier }); return 200; }
await db.orders.upsertStatus({ b4bit_id: body.identifier, status: body.status, last_update: body.edited_at, raw: body, });
return 200;}Retries
Section titled “Retries”How to recover missed states
Section titled “How to recover missed states”- Nightly reconciliation: a job that runs every early morning and calls
GET /orders/?start=YESTERDAY&end=TODAY&page=1&items_per_page=100, iterates every page, and cross-checks against your database. For every payment whoseedited_atadvanced, update. - Point-in-time query: if you detect an “orphan” order (exists in your checkout but never received a webhook), fetch
GET /orders/info/{identifier}for the authoritative state. - Endpoint high availability: reverse proxy with at least 2 workers, healthcheck, downtime alerts.
- Async response: immediate
HTTP 200+ internal queue (Redis, SQS, Celery) to process heavy logic without blocking the ACK.
Idempotency
Section titled “Idempotency”Your handler must tolerate the same (identifier, status) arriving more than once:
- Use
identifier+edited_atas the unique dedupe key. - Do not run side effects (sending emails, updating inventory) more than once per state.
- Persist the fact that “I already processed this state” before running the side effect.