Every payment gateway I've ever worked on had the same hidden bug.
A provider API times out. The code says "failure". So you retry. But the original request actually succeeded – the provider just took too long to respond. Now you've double‑charged the customer.
I built Azums , an open‑source payment gateway in Rust, specifically to stop this pattern.
_ _The fix: make ambiguity explicit.
Instead of pending → success/fail , I designed a state machine with five states:
pending (request sent, waiting)
succeeded (confirmed success)
failed (confirmed failure)
retryable (temporary error, safe to retry)
unknown (timeout or ambiguous response – needs investigation)
When a timeout happens, the system doesn't guess. It marks the transaction as unknown and stops. No blind retries.…