Urgent.News

600+ sources. One page. See who else covered it.

Editions

Culture

The Retry That Charged a Customer Twice, and What We Learned About Idempotency

We found out about it from a support ticket, not a dashboard. A customer had been billed $340 twice for the same order, four seconds apart, and wanted to know if we'd started double-charging people. We hadn't — or rather, we hadn't meant to. What we'd actually built was a payment flow that assumed retries were free, and that assumption cost us a very uncomfortable afternoon of pulling logs and a…

A customer discovered they had been billed $340 twice for the same order, four seconds apart. The support ticket revealed a system issue rather than a deliberate double-charging. The checkout service had attempted to retry the payment when the initial call timed out. This assumed that retries were free, which proved costly in this case. The team realized they had built a payment flow that treated retries as error-free re-attempts, rather than understanding they could result in double charges.

The root cause was treating retries as a network-layer concern, rather than an operation-design principle. They had been using "retry it and see" as a correctness strategy for multiple services, including webhook handlers, background jobs, and checkout clients. While this worked for idempotent operations, it failed when applied to charge operations, which are naturally non-idempotent.

The solution was implementing idempotency keys. Every write operation that could be retried was assigned a client-generated UUID as a unique identifier. Before executing a charge, the system checked a dedupe table, returning the stored result if the UUID was seen before. If not, the charge was executed and stored, with a TTL (time-to-live) of 24 hours to cover retry windows. This approach was well-documented for payment processors but required extensive codebase changes to retrofit idempotency keys.

The team extended the use of idempotency keys to other endpoints: refund issuance, subscription upgrades, and a bulk-invite endpoint. These operations also needed idempotency support to avoid duplicate refunds or upgrades, which could lead to financial losses. After implementing this fix, duplicate-charge and duplicate-refund tickets dropped from recurring monthly occurrences to zero.

The broader lesson was about the necessity of pairing retry logic with idempotency keys. Adding one without the other creates a bug waiting to be triggered by a timeout. They emphasized that idempotency keys are a default requirement for any new mutating endpoint, not an afterthought. This experience taught them that operational lessons often reveal deeper design principles that need to be baked into the system from the beginning.

Written by urgent.news from Dev.to's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.

Read the original at dev.to →

More in Culture

Spain's annual flamingo ringing begins

Spain's annual flamingo ringing begins

Hundreds of volunteers gathered at Spain’s Fuente de Piedra lake on Saturday for the annual flamingo-ringing initiative. Since the lake became a nature reserve in 1984, more than 200,000 flamingo…

A woman built her parents a 725-square-foot cottage in her backyard so they could be closer to their grandchildren

A "granny pod" ad inspired Caitlin Swann to build an ADU in her backyard for her parents when they retired. Now they're steps from their grandkids.

  • Interior designer Caitlin Swann built 725-square-foot cottage for parents Patti and Gary Christie.
  • One-bedroom, one-bathroom cottage located in backyard, steps away from Swann's home.
  • Christies live in cottage daily, grandchildren visit frequently.

More from Sunday 9 August →