Build the payment path for equipment order 47. Three pressure sleeves, one inspection seal, and delivery total 17,200 minor units under the selected currency rules. The browser proposes product IDs and quantities; the server validates catalog and stock, prices the order, and persists the accepted snapshot. A hosted checkout session is bound to that snapshot and an idempotent attempt key. Returning to the application shows the server order state, even when the provider event has not arrived. The webhook handler validates raw-body signature and provider scope, checks amount and currency, records event IDs, and writes one paid transition with one fulfillment intent. A reconciliation job uses the same transition if a webhook is delayed or lost. A partial refund of 4,700 units remains pending until provider confirmation, after which the ledger shows 12,500 net captured units without deleting the original capture.
Project: checkout reconciliation
Build contract
- Tamper with the browser total and verify the provider still receives the accepted server amount.
- Refresh and retry hosted checkout without uncontrolled payable attempts; forge the return URL and confirm it cannot mark paid.
- Deliver duplicate, reordered, wrong-amount, and missing webhook events; ensure one effective fulfillment.
- Request partial and concurrent refunds, inject a failed refund, and reconcile ledger entries with provider state.
Implementation checkpoint
function remainingCapturedMinor(capturedMinor, completedRefundMinor) {
return capturedMinor - completedRefundMinor;
}
console.log(remainingCapturedMinor(17200, 4700));
// Output: 12500Cost and boundaries
Pricing is O(n) for n order lines plus catalog and policy lookups. A unique payment attempt and event ID add small indexed records; fulfillment through an outbox adds a worker step but survives crashes between capture and shipment. Reconciliation uses provider reads and should back off for old or resolved attempts. A payment ledger grows with each movement, so balance reads can scan O(k) movements or use a transactional summary that is regularly reconciled. Track pending age, duplicate events, mismatched amounts, over-refund rejections, and one-order-to-one-shipment behavior.
Failure drill
Send a browser request claiming a 200-unit total for order 47; the server must reject or ignore it. Open the success route without paying and confirm status remains pending. Deliver one signed capture event twice, then an older authorization event: the order remains captured and only one fulfillment key is emitted. Crash after the provider confirms payment but before the local write, then let reconciliation recover it. Start two 12,500-unit refunds concurrently; only an amount within the remaining 17,200 may be reserved. If a refund later fails, restore the correctly reserved balance without erasing the capture.
Acceptance checks
- Order and provider amount match a persisted, currency-aware snapshot.
- Browser return cannot authorize fulfillment.
- Duplicate or reordered provider events cannot duplicate shipment.
- Refund reservations and confirmed ledger movements never exceed capture.
Common Mistakes
- Using the browser total or return query as payment truth.
- Acknowledging a webhook before durable state is recorded.
- Treating a refund request as a settled refund.
Related lessons
Server-Priced Order Snapshots and Money Units; Hosted Checkout Sessions and Browser Returns; Payment Webhook Reconciliation and Idempotent Fulfillment; Refunds, Reversals, and Payment Ledger State.
