Loading...
Loading...
In payment infrastructure, idempotency isn't a feature — it's the foundation everything else stands on. Here's why we made it mandatory for every endpoint.
Short Field Note — TxCore
---
We made idempotency keys mandatory on every endpoint in TxCore — not just the payment-critical ones. This felt excessive at first. Why require an idempotency key on a read-only balance query?
Because the boundary between "read-only" and "mutating" shifts over time. A balance query today might trigger a fee calculation tomorrow. A status check might update a last-seen timestamp. When that shift happens, if idempotency isn't already there, you have a window where retries can cause side effects.
Three things I learned:
1. Idempotency keys are cheap; duplicate transactions are expensive.
Generating a UUID client-side costs nothing. Handling a duplicate transaction — customer support, refunds, reputation damage — costs real money. We optimized for the rare case (duplicate) by making the common case (normal request) slightly heavier. This is the right tradeoff for financial systems.
2. The saga pattern is correct but exhausting.
Compensating transactions are harder to write than the primary flow. Every compensation has to handle the same edge cases — partial failures, timeouts, conflicting updates — but in reverse. We spent 40% of our engineering time on compensation logic. It was worth it. The system has never lost a transaction, even during two major outages.
3. Event sourcing is a superpower for forensics, not for day-to-day reads.
Developers love event sourcing in theory. In practice, "replay the event stream to get the current balance" is too slow for API responses. We maintain read models — materialized views that are updated asynchronously — and use the event store only for audit queries and rebuilds. The event store is the source of truth; the read model is the fast path. Both are necessary.
---
TxCore is a private infrastructure project. Architecture details shared with permission.