Blog

Payments that run once

2 min read

A client that sent a payment and got no answer cannot tell whether it went through. Idempotency keys let it ask again safely; idempotent() makes them one line.

A phone sends a payment, the network drops, and no answer comes back. Did the payment happen? The client cannot know. If it sends again and the first one went through, the customer pays twice. If it does not and the first one failed, they do not pay at all.

Stripe's answer, now an IETF draft, is the idempotency key: the client makes up a key for the request and sends the same key when it retries. The server runs the request once, and answers a retry with the first answer.

One wrapper

route POST /payments { json: { to: str, amount: dec } } = idempotent(() => {    let id = pay(to, amount)?    respond(201, { id })})
curl localhost:8080/payments -H "Idempotency-Key: $(uuidgen)" --json '{"to": "bob", "amount": "12.50"}'

idempotent(f) keeps the first answer for each key: the status, the headers and the body. The same request again with the same key gets that answer back, marked Idempotent-Replayed: true, and f does not run.

The edge cases, handled

Getting this right by hand takes a table, a lock and careful thought about every failure. It is the kind of thing a web language should do once, correctly.

Read more: Idempotent requests in the docs.

The Quillo newsletterNew posts, components and plugins, about once a week. No tracking.

Thread
6Useful? Like it, or add to its thread below.

Thread 0 posts

On Payments that run once: questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.