<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/system-design-drill-day-9-idempotency-keys-for-payments-and-retries-eywkkueyl" -->

---
title: System Design Drill Day 9: Idempotency Keys for Payments...
description: Preventing duplicate charges during checkout retries requires claiming an idempotency key before any side effect runs, not after. The design covers a...
canonical: https://daily.dev/posts/system-design-drill-day-9-idempotency-keys-for-payments-and-retries-eywkkueyl
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: System Design Drill Day 9: Idempotency Keys for Payments and Retries | daily.dev
og:description: Preventing duplicate charges during checkout retries requires claiming an idempotency key before any side effect runs, not after. The design covers a...
og:url: https://daily.dev/posts/system-design-drill-day-9-idempotency-keys-for-payments-and-retries-eywkkueyl
og:image: https://api.daily.dev/og/posts/eyWKKuEyl.png
og:image:alt: System Design Drill Day 9: Idempotency Keys for Payments and Retries
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# System Design Drill Day 9: Idempotency Keys for Payments and Retries

**[C\# Corner](https://daily.dev/sources/csharpcorner)** · 7 min read · 5 upvotes · 1 comments

## Summary

Preventing duplicate charges during checkout retries requires claiming an idempotency key before any side effect runs, not after. The design covers a marketplace checkout on 3 Laravel servers with shared Redis and a payment webhook provider. A server claims the key atomically with Redis 'SET ... NX' or a unique DB index, marking it 'processing' so a racing duplicate gets a 409. Once the charge, stock decrement and order write complete in one transaction, the key is marked 'completed' and its response stored for replay. Each entry point (client, webhook, queue job) deduplicates on its own id: the webhook on the provider's event id, the queue job on a flag like 'confirmation_email_sent'. Keys should be kept 24 hours to 7 days, while Stripe keeps its own idempotency keys for at least 24 hours.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://www.c-sharpcorner.com/article/system-design-drill-day-9-idempotency-keys-for-payments-and-retries>

## Questions this post answers

### Where should I claim an idempotency key in a payment checkout flow to prevent double charges?

Claim the key atomically before any side effect runs, using Redis 'SET ... NX' or a database insert under a unique index, marking it 'processing'. A second request that finds the key still processing gets a 409 response. Only after the charge, stock decrement, and order write complete in one transaction should the key be marked 'completed' with its response stored for replay on later retries.

_Developers hardening checkout flows against double charges compare idempotency patterns like this on daily.dev._

### How long does Stripe retain idempotency keys for retried payment requests?

Stripe keeps idempotency keys for at least 24 hours. Because that window is limited, a retry arriving much later cannot rely solely on the provider's own idempotency key, so your own system should store keys separately, typically for 24 hours to 7 days, covering both the client retry window and webhook redelivery window.

_Anyone designing retry-safe payment integrations can track provider quirks like this on daily.dev._

### Should a payment webhook handler deduplicate using the provider's event id or my own checkout key?

Use both, for different purposes. Deduplicate on the provider's event id, stored in a processed_webhooks table with a unique index, since the provider generates and retries using that id. Use your own checkout or payment id to link the webhook back to the correct order, especially if the webhook arrives before the order row has finished saving.

_Engineers wiring up payment webhooks can compare dedup strategies like this on daily.dev._

## Community discussion

Top comments from developers on daily.dev.

**@williamsj04** · 0 upvotes

> Claiming the key before any side effect runs is the right order. One edge case that may be worth covering is a worker that crashes after marking a key as processing and never completes it. A time limit on the processing state, plus a status check with the payment provider before a retry, could stop that key from blocking a legitimate retry indefinitely.

## Similar posts on daily.dev

- [How to Stop Charging Customers Twice: A Practical Idempotency Guide](https://daily.dev/posts/how-to-stop-charging-customers-twice-a-practical-idempotency-guide-m6qetcboi) · The T-Shaped Dev · 0 upvotes · 1 comments
- [System Design Drill Day 3: Queues and Async Jobs for Checkout](https://daily.dev/posts/system-design-drill-day-3-queues-and-async-jobs-for-checkout-f7kvhxu3z) · C\# Corner · 0 upvotes · 0 comments
- [Making Laravel MongoDB Operations Idempotent: Safe Retries for Financial Transactions](https://daily.dev/posts/making-laravel-mongodb-operations-idempotent-safe-retries-for-financial-transactions-ajgnfhm42) · Laravel News · 3 upvotes · 1 comments
- [Idempotency: What, Why and How](https://daily.dev/posts/idempotency-what-why-and-how-qrypjmflf) · W endell Adriel · 54 upvotes · 3 comments
- [Idempotency Is Easy Until the Second Request Is Different](https://daily.dev/posts/idempotency-is-easy-until-the-second-request-is-different-skbwedvu3) · Hacker News · 0 upvotes · 0 comments

---

Tags: [#career](https://daily.dev/tags/career), [#distributed-systems](https://daily.dev/tags/distributed-systems), [#redis](https://daily.dev/tags/redis), [#payment-processing](https://daily.dev/tags/payment-processing)

[View this post on daily.dev](https://daily.dev/posts/system-design-drill-day-9-idempotency-keys-for-payments-and-retries-eywkkueyl)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"System Design Drill Day 9: Idempotency Keys for Payments and Retries","url":"https://daily.dev/posts/system-design-drill-day-9-idempotency-keys-for-payments-and-retries-eywkkueyl","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/system-design-drill-day-9-idempotency-keys-for-payments-and-retries-eywkkueyl"},"datePublished":"2026-10-27T05:10:29.696Z","dateModified":"2026-10-27T05:11:02.366Z","description":"Preventing duplicate charges during checkout retries requires claiming an idempotency key before any side effect runs, not after. The design covers a...","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/7a74b09560b2c1bb94baaea53224e2f3?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/7a74b09560b2c1bb94baaea53224e2f3?_a=AQAEuop","isAccessibleForFree":true,"articleSection":"C# Corner","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"C# Corner","logo":"https://media.daily.dev/image/upload/t_logo,f_auto/v1/logos/ada2d584df6241748fc4e71878dc70a3","url":"https://daily.dev/sources/csharpcorner"},"commentCount":1,"discussionUrl":"https://daily.dev/posts/system-design-drill-day-9-idempotency-keys-for-payments-and-retries-eywkkueyl","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":5},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":1}],"keywords":"career,distributed-systems,redis,payment-processing","timeRequired":"PT7M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"C# Corner","item":"https://daily.dev/sources/csharpcorner"},{"@type":"ListItem","position":3,"name":"System Design Drill Day 9: Idempotency Keys for Payments and Retries"}]}
{"@context":"https://schema.org","@type":"WebPage","@id":"https://daily.dev/posts/system-design-drill-day-9-idempotency-keys-for-payments-and-retries-eywkkueyl","comment":[{"@type":"Comment","text":"Claiming the key before any side effect runs is the right order. One edge case that may be worth covering is a worker that crashes after marking a key as processing and never completes it. A time limit on the processing state, plus a status check with the payment provider before a retry, could stop that key from blocking a legitimate retry indefinitely.","datePublished":"2026-10-30T10:26:28.217Z","url":"https://daily.dev/posts/eyWKKuEyl#c-1VAUbIA8Z","author":{"@type":"Person","name":"Jessica Williams","url":"https://daily.dev/williamsj04","image":"https://media.daily.dev/image/upload/s--3AQpOAnJ--/f_auto/v1751281557/avatars/avatar_JY6mOy6NkEoKH16Qh0jBl?_a=BAMClqZW0"}}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/system-design-drill-day-9-idempotency-keys-for-payments-and-retries-eywkkueyl#faq","mainEntity":[{"@type":"Question","name":"Where should I claim an idempotency key in a payment checkout flow to prevent double charges?","acceptedAnswer":{"@type":"Answer","text":"Claim the key atomically before any side effect runs, using Redis 'SET ... NX' or a database insert under a unique index, marking it 'processing'. A second request that finds the key still processing gets a 409 response. Only after the charge, stock decrement, and order write complete in one transaction should the key be marked 'completed' with its response stored for replay on later retries. Developers hardening checkout flows against double charges compare idempotency patterns like this on daily.dev."}},{"@type":"Question","name":"How long does Stripe retain idempotency keys for retried payment requests?","acceptedAnswer":{"@type":"Answer","text":"Stripe keeps idempotency keys for at least 24 hours. Because that window is limited, a retry arriving much later cannot rely solely on the provider's own idempotency key, so your own system should store keys separately, typically for 24 hours to 7 days, covering both the client retry window and webhook redelivery window. Anyone designing retry-safe payment integrations can track provider quirks like this on daily.dev."}},{"@type":"Question","name":"Should a payment webhook handler deduplicate using the provider's event id or my own checkout key?","acceptedAnswer":{"@type":"Answer","text":"Use both, for different purposes. Deduplicate on the provider's event id, stored in a processed_webhooks table with a unique index, since the provider generates and retries using that id. Use your own checkout or payment id to link the webhook back to the correct order, especially if the webhook arrives before the order row has finished saving. Engineers wiring up payment webhooks can compare dedup strategies like this on daily.dev."}}]}
```

