The required guarantees
Skinloop delivers webhooks at least once, and status polling can observe the same completed checkout concurrently. Never call an external fulfillment service directly from either handler. Use two independent safeguards:- Store one durable fulfillment job for each checkout in your database.
- Send the same stable idempotency key to the fulfillment destination on every attempt.
Store a durable job
Adapt this PostgreSQL schema to your order system:Use one enqueue operation
Both the signed webhook handler and status poller must call this operation. It records authoritative completion and creates the job in one local transaction:Claim work with a lease
A worker claims one job in a short database transaction, then commits before making the external request:lease_token prevents a stale
worker from acknowledging a job after another worker has reclaimed it.
Deliver outside the transaction
Send the stored key to a destination that guarantees repeated requests with that key return the original delivery result:Recover every crash point
For an ambiguous result without destination idempotency, mark the job for
reconciliation:
queued.
Production checklist
- Verify Skinloop signature and timestamp before recording a webhook.
- Deduplicate webhook storage by event
id. - Re-read or validate authoritative completed status before enqueueing.
- Validate merchant order ID, checkout ID, amount, and currency.
- Enforce unique checkout, order, and fulfillment keys in the database.
- Commit the job before making any external request.
- Use bounded timeouts and leases.
- Require the matching lease token for success and failure updates.
- Reuse the same fulfillment key forever for that checkout.
- Alert on
needs_reconciliationand expired processing leases. - Record the destination reference before marking the merchant order fulfilled.
