Idempotent Payment Requests
Using idempotency keys on payment API calls to ensure that retried requests due to network failures or timeouts produce the same result without creating duplicate charges.
Description
Idempotency in payment processing ensures that performing the same operation multiple times produces the same outcome as performing it once. This is critical because network failures, client timeouts, and server errors can leave the caller uncertain whether a request succeeded. Without idempotency, retrying a failed payment creation could result in the customer being charged twice. Stripe supports idempotency by accepting an Idempotency-Key header on all POST requests; if Stripe receives a request with a key it has seen before (within 24 hours), it returns the cached response from the original request.
The idempotency key should be generated deterministically based on the operation's intent, not randomly. For example, use a combination of user ID, order ID, and amount (e.g., `pay_user123_order456_2999`) so that the same logical payment always maps to the same key. Random UUIDs as idempotency keys protect against accidental double-clicks but not against application-level retry bugs where a new UUID is generated per attempt. Store the idempotency key alongside your order record before making the Stripe call, so you can always retry with the same key.
Beyond Stripe's built-in idempotency, your own system should enforce at-most-once semantics at the application layer. Use database constraints (unique indexes on order_id + payment_intent_id) and state machine patterns (order status must be 'pending' to initiate payment) to prevent duplicate payment attempts regardless of the payment provider's idempotency support. This defense-in-depth approach protects against double charges even in distributed systems with multiple server instances.
Prompt Snippet
Generate idempotency keys deterministically from the business operation context (e.g., `payment_${orderId}_${amount}_${currency}`) rather than random UUIDs, and pass them via the idempotencyKey option in stripe.paymentIntents.create(). Persist the idempotency key in your orders table before calling Stripe so retries reuse the same key. Implement application-level idempotency by adding a UNIQUE constraint on (order_id, status) and using a compare-and-swap pattern: UPDATE orders SET status = 'processing' WHERE id = ? AND status = 'pending' returning the affected row count to gate the Stripe call.Tags
Related Terms
Double-Charge Prevention
Implementing safeguards at the application, API, and database layers to prevent customers from being charged twice for the same transaction due to retries, race conditions, or user double-clicks.
Payment Intent Flow
The server-driven flow using Stripe's PaymentIntent API to create, confirm, and track a payment through its complete lifecycle from creation to settlement.
Stripe Integration Architecture
The overall design of how your application communicates with Stripe's APIs for payment processing, customer management, and subscription handling.
Payment Retry Logic
Configuring automatic reattempts of failed subscription payments with strategic timing, escalating notifications, and eventual fallback actions when all retries are exhausted.