Skip to main content

Milestone 5.2d — Settlement webhooks and reconciliation

Status: Shipped on main (env-gated)
Tracking: ehx-api#14
Parent: ehx-web#4 checkout epic

What shipped

PieceDescription
Idempotent confirmcheckout_tx_claims — one EVM tx hash can activate at most one session
Audit logcheckout_settlement_events — confirm, verify, processor, reconcile
Background reconcilePolls confirming sessions with a tx hash every ~45s (RPC re-verify)
Processor webhookPOST /api/v1/checkout/webhooks/settlement (HMAC-signed)
Outbound eventcheckout.session.confirmed when a session confirms
Operator CSVGET /api/v1/admin/billing/settlement-events.csv

Configuration

# Background RPC reconciliation (default on)
EHX_CHECKOUT_RECONCILE_ENABLED=1
EHX_CHECKOUT_RECONCILE_INTERVAL_SEC=45

# Inbound processor/indexer events (required to accept POSTs)
EHX_CHECKOUT_PROCESSOR_WEBHOOK_SECRET=…
# Or reuse outbound secret:
# EHX_CHECKOUT_WEBHOOK_SECRET=…

Disable reconciliation in CI or dev: EHX_CHECKOUT_RECONCILE_ENABLED=0.

Inbound processor webhook

POST /api/v1/checkout/webhooks/settlement

Headers:

  • X-Ehx-Signature: sha256=<hmac> (or X-Ehx-Processor-Signature)

Body (JSON):

{
"type": "payment.confirmed",
"session_id": "<checkout-session-uuid>",
"transaction_hash": "0x…"
}

Supported type values:

  • payment.confirmed, checkout.session.confirm, payment.settled → idempotent confirm
  • payment.failed, payment.expired → audit log only

Idempotency rules

  • Same session + same tx: safe to retry (returns already_confirmed or owned).
  • Different session + same tx: 409 tx_hash_already_claimed.
  • RPC verify success calls the same confirm path as the processor webhook.

Operator exports

EndpointPurpose
GET /admin/billing/checkout-export.csvSession finance rows
GET /admin/billing/settlement-events.csvSettlement audit trail

Bearer or X-Ehx-Admin-Token required.