# Capture to settlement: the expected flow

Source: platform team runbook, section "Money flow". Applies to card payments with automatic or manual capture.

## Sequence for one payment

| Step | Event / action | Topic | Producer | Consumer | Expected timing |
|---|---|---|---|---|---|
| 1 | Payment authorized | `payments.payment.authorized` | orchestrator | webhook-dispatcher, risk | at authorization |
| 2 | Payment captured | `payments.payment.captured` | orchestrator | ledger, webhook-dispatcher | at capture (seconds after 1 for automatic capture) |
| 3 | Ledger entry posted (kind `capture`) | `ledger.entry.posted` | ledger | settlement | within 5 seconds of 2 |
| 4 | Payment included in an open settlement | (database only: `settlement_items`) | settlement | none | within 1 minute of 3 |
| 5 | Settlement paid, payment settled | `settlement.payment.settled` | settlement | webhook-dispatcher | T+2 business days, 06:00 UTC batch |
| 6 | `payment.settled` webhook delivered | (HTTP to merchant) | webhook-dispatcher | merchant | within minutes of 5 |

## Consumer behavior that matters

- The ledger validates every message against the schema in the event catalogue before processing. A message that fails validation is sent to `ledger.dlq` with the error in the headers. There is no automatic retry and no alert below 5,000 DLQ messages per hour.
- Settlement only knows about a payment through `ledger.entry.posted`. It never reads `payments.*` topics.
- The webhook dispatcher delivers what it consumes. It cannot deliver an event that was never produced.

## Where to look

- Kafka UI: search by key (the payment id) across `payments.*`, `ledger.*`, `settlement.*`. Dead letter topics are keyed by source offset, search them by payload.
- Logs: `ledger-service`, `settlement-service`, `webhook-dispatcher`, filter on `payment_id`.
- Database: `ledger_entries` and `settlement_items` by `payment_id`.
