Practice · Payments · Card 1
What's the right idempotency key for this job?
A background job creates a payment intent. The job can retry. The wrong key turns one charge into two.
The code
A Sidekiq job that creates a Stripe PaymentIntent. The job retries on transient errors.
class ChargeOrderJob < ApplicationJob
retry_on Stripe::APIConnectionError, wait: :exponentially_longer
def perform(order_id)
order = Order.find(order_id)
intent = Stripe::PaymentIntent.create(
{ amount: order.total_cents, currency: "usd" },
{ idempotency_key: KEY_HERE }
)
order.create_payment!(intent_id: intent.id)
end
end The question
Which value of KEY_HERE prevents the charge from happening twice when the job retries?
Take a moment. Pick the best answer. Wrong picks reveal why they're wrong, which is half the point.
✅ Answer breakdown
✗ A. SecureRandom.uuid
Every retry generates a new UUID, so every retry sends a different key to Stripe. Stripe treats them as separate operations and creates a separate charge each time. This is the exact bug idempotency keys exist to prevent.
✓ B. "charge-order-#{order.id}"
The key is tied to the order. Every retry of the job sees the same order, generates the same key. Stripe returns the same intent on the retry without creating a new one. One charge per order, no matter how many retries.
✗ C. Time.current.to_i.to_s
Each retry runs at a slightly different time, so each gets a different key. Same bug as A.
✗ D. "charge-#{Thread.current.object_id}"
Each retry runs on a different thread (or the same thread but at a different time, where the object_id may have changed). Not stable across retries.
💡 The principle
An idempotency key has to be the same across retries of the same operation, and different across different operations. Two safe shapes: tie it to a stable resource ID, or pass it as a job argument so the retry inherits the original value.
Bonus defense: check your own database for an existing successful Payment before calling the API at all. The idempotency key is the second line of defense; "we already did this" is the first.
📚 Theory
For the full walkthrough, read Payments · Idempotency Keys in Payment APIs.