Idempotency in Payments — Why You Get Charged Twice
Underflow with gg · 2026-06-23 · 16м 5с · 43 просмотров · YouTube ↗
Топики: durable-execution
🎧 Аудио
📝 Summary
model=deepseek-v4-flash · prompt=summary-v7 · 4 766→1 939 tokens · 2026-07-20 15:00:06
🎯 Главная суть
Идемпотентность в платежах — это гарантия, что повторный запрос с тем же идентификатором приводит к тому же результату, что и первый, а не создаёт новый платёж. Повторы неизбежны из-за ненадёжности сети, поэтому их не запрещают, а делают безопасными — единственное стабильное имя (idempotency key) используется во всех повторных попытках, и только на этапе списания денег идемпотентность строго обязательна.
Проблема двойного списания в платежах
Платежи — критическая операция: клиент платит, но ответ может потеряться из-за обрыва сети. Если клиент повторяет запрос, думая, что первый не дошёл, а на самом деле платёж уже обработан, он получит второй платёж — двойное списание. Пример: заказ мороженого по рации — из-за помех вы не слышите подтверждение и говорите снова, но продавец уже начал готовить, и второй заказ создаёт ещё одну порцию. Та же логика работает в API платежей.
Идемпотентность: повтор безвреден, а не запрещён
Бороться с повторами бесполезно — сеть всегда будет терять сообщения. Правильное решение: сделать так, чтобы повторный запрос не создавал нового платежа, а возвращал тот же результат, что и исходный. Идемпотентность = повторы безвредны, повторы не запрещены. Ключ — использовать один и тот же уникальный идентификатор (idempotency key) во всех повторных попытках.
Кто генерирует идемпотентный ID? — клиент или сервер
Распространённый вопрос на собеседованиях: кто должен создавать idempotency key? Ответ: сторона, которая выполняет повторные попытки (retry). Обычно это сервер, общающийся с платёжным шлюзом, а не мобильное приложение клиента. Причины:
- Мобильное устройство может потерять сохранённый ключ или сгенерировать коллизию (при 100 млн устройств вероятность пересечения высока).
- Сервер должен получить от платёжного шлюза уникальный идентификатор намерения (intent) и сохранить его в своём кеше или БД, прежде чем начинать платёж. Этот идентификатор и становится idempotency key. Мобильное приложение получает его от сервера и использует для последующих оплат.
Резервирование — sloppy safe, оплата — критический шаг
Весь процесс делится на два этапа: резервирование (reserve) и непосредственное списание (pay). Резервирование — это выделение idempotency key (например, «конус номер 7»). Если при резервировании ответ потерялся, его можно безопасно повторить — платёж ещё не произошёл. Лишняя резервация просто исчезнет по TTL (time-to-live). Это «мусор, но не баг». Списание — момент, когда деньги покидают счёт. Здесь повтор уже может привести к двойной оплате, поэтому на этом шаге идемпотентность обязательна: шлюз проверяет, был ли уже обработан данный idempotency key, и если да — возвращает сохранённый результат, а не создаёт новый платёж.
Что делать, если ответ после оплаты не получен
Даже при успешной оплате ответ шлюза может потеряться. Возможны два механизма:
- Уведомления (webhook) — шлюз сам отправляет серверу/клиенту сообщение об успешном платеже («кричит, когда мороженое готово»). Клиент получает уведомление даже без повторного запроса.
- Проверка статуса (reconciliation) — клиент (или сервер) позже запрашивает статус конкретного платежа по тому же idempotency key («позвонить и спросить, что с заказом номер 7»). Оба подхода работают, так как у платежа есть одно стабильное имя для поиска.
Итоговый принцип: одно имя на все повторные попытки
Ключевой секрет идемпотентности — при каждом повторе используется тот же самый идентификатор (тот же «конус номер 7»). Если клиент непреднамеренно меняет сумму (150 → 200 рублей) с тем же ID, платёж не пройдёт, потому что у одного намерения может быть только одна сумма. Для нового платежа требуется новый intent и новый idempotency key. Это защищает от случайных ошибок и гарантирует, что повтор (с той же суммой) не приведёт к двойному списанию.
📜 Transcript
en · 2 579 слов · 33 сегментов · clean
Показать текст транскрипта
Hello everyone. Today's topic is item potency. We are going to go deeper into item potency questions. I'm sure that you know already what is item potency, how it works, but still while in interview, interviewer would ask some of the questions related to item potency, which are very tricky. And I'm sure that after today's video, you would be able to answer all those questions and frame your answers pretty well. So stay tuned. You're watching Underflow with Chichi. Now here is the thing. Whenever you're searching idempotency on YouTube or anywhere on the internet, most of the articles or videos you will get idempotency in payments or idempotency in API of payments. Why is that? Because payment is very critical operation when it comes to charge twice. Idempotency means a similar kind of request should lend on the similar kind of results always. So we are going to understand this from a uh ice cream example now for example you're ordering ice cream on a crackly walkie talkie suppose you have a walkie talkie and the ice cream parlor person also has a walkie talkie and you order that hey one chocolate cone please on the walkie talkie now suppose it's a crackly walkie talkie your your your order never reached to the person whom you're ordering and you never heard back from the person what would you do will you say it again of course you will say it again but what if that person already heard it and he started working on your ice cream and then he received another request from you so it's you and you're not intend to have another request but because you never heard back from a person you have to make the request twice that makes that your requests are not idempotent whenever you are requesting for i for an ice cream you are saying hey i want a chocolate cone and if what if you heard you heard back that okay i received your payment that okay i received your your order and your ice cream will be ready you will be safe but if you haven't heard back and you have to make the order twice that means you may get charged for two ice creams where you are not supposed to now what are the challenges what are the solutions now why can't we just prevent the retries why can't we just say that you should not say it again like you just said it once and assumes that your request will always reach that does not happen in all the systems so availability is one of the factor in that suppose the other side is not available to receive your request in that terms your request will never reach and someone has to retry now the network will always drop messages every time and the client that hears nothing will resend that's correct behavior not a bug so We don't fight the repeat. We make sure that repeat lands on the same result instead of creating a second one. Itempotents equal to repeats are harmless, not forbidden. Now, a question comes, and this is very interesting. If you ask me, I come from a frontend background, and I know that this question comes a lot in interview. Who makes the itempotent IDs? Is it client or server? You would laugh and say that it's pretty much clear server would make it, but no. if client is making that id there is no harm because the site who is doing the retries should make the ids that is the only catch here now for example your server is talking to payment gateway when your server is talking to payment gateway your payment gateway is giving you the results that okay we have received your request and your payment is done and here is the here is the response in this two things can happen one you will get the response second you would not get the response back because of the network unavailability if you won't get the response whom you want to do the retry should it be your mobile application or should it be the server who is actually talking to the payment gateway in my view the retry should happen from the server who is talking to the payment gateway we will understand in this way so here this is your mobile application this mobile application is talking to one of the server and this server is talking to one of the payment gateway now understand this when i'm saying that server should do the retry why i'm saying that and why not mobile because for example your mobile creates the id xyz your mobile has to save it and suppose this payment gateway sends no reply there is no reply from here this will also give you timeout here the thing is you have to again send the same xyz on the server and then server again go to the payment gateway one thing keeping this idempotent ids on mobile is not safe first second when you will have 100 million mobiles and there are chances that someday one or two ids will collide and you don't want to do that so for that what you have to do is you have to create these ids on server how to create the item potent ids in a distributed system is another another discussion point which we will cover in some of our next videos how to create unique ids on the distributed system that is something which we will cover next but for now understand my server should generate the unique item potent id this will send to payment gateway payment gateway will get it back by the way this is also a wrong move we will understand why ideally you should reserve your space at the payment gateway payment gateway should give you id back saying that hey this is a xyz intent and all your payment should only happen on this xyz so server only needs to save it somewhere in the db not generated by itself because payment gateway is my source of truth here and payment gateway is handling servers like these many servers like these so payment gateway should start creating the intent and should give the item potent id back to server server should save it somewhere and from there it should give back results to the mobile application this is one part now do all the steps need to be this careful first you reserve a cone like i said reserve a item potent id um and then later you pay for it two different steps so reserving can be sloppy suppose again from for example payment gateway started one intent with xyz this xyz is your idempotent id your server actually saves it in one of the cache and then gives it back to the mobile site for example if this doesn't happen this never reached to mobile what will happen nothing will happen basically mobile will say hey i want to pay server will say okay let me save a space for you let me save reserve a space for you with which you can pay and server will ask payment gateway but can you give me another id or intent payment gateway will say yeah you this time you can use abc server will go and save this abc in your cache now your question is what would happen to xyz so after a ttl xyz we can delete it there is no harm basically there and then you can actually start doing the payment on this abc next time so i hope you understood what i am trying to explain here reserving can be sloppy reserve twice by accident no harm nobody paid the leftover reservation just melts away on its own or ttl cleans it up it's a little litter not a bug okay but paying cannot paying is the moment money leaves your pocket so if you are paying twice it's real it is real harm you are losing the money this is the only step where repeat hurts okay so where you would ask that where exactly we are talking about the double charge double charge but where does it actually happens now when you say okay charge me for cone 7 and you never got the response back and you again said it charge me for cone 7 because you thought that other person never heard you but for for real that person actually heard you and you started making the cone for you now second time when you said the person thought okay it's a second request from the same person and started again the preparing the cone for you now you have to pay for two cones twice the danger is one precise moment you paid but the done never reached to you okay so you repeat if the truck can't tell your repeat from a brand new payment you pay twice everything before paying was harmless this single step is where the stable name payment id becomes the mandatory the truck checks seven number already paid here is your code instead of charging again now what if you never hear back at all this is also very interesting and this is i want to show you the different version of it so for example this is your mobile app your mobile app says that i want to make payment of 150 rupees to your server your server goes to the payment gateway and ask hey this person whose name is gg wants to pay 150 rupees can you please create one intent and this payment gateway would create an intent saying okay xyz is his intent this xyz is saved in your cache and you give it back xyz to mobile user This is phase 1. I am calling it phase 1. Now, in phase 2, what happens? A mobile application would say, with this intent ID, can I make a payment of 150 to server? Server says, yeah, sure, why not? And then there is a gateway. Gateway says, payment is done. And server saves this payment operation in the cache or DB, wherever you want to save. But while giving this response back this network interrupted and it never reached to the user in this case there are two ways to do it one when server is saving the payment status in the database there should be one service which we call it notification service should also tell this to the mobile client that your payment is done now whatever payment you would make will be on the new intent id or either it is done or there is an error any of it you have to save back to your notification services or once user come back to application after closing the app and come back you can make the call to server saying hey what happened to my previous payments are those are succeeded not succeeded are there any errors and server can check the db and can tell you okay yeah your last payment was done so don't worry your wallet is updated now so this is something which we call uh reconciliation when you come back in case of you never heard back from the server side And this is what we are trying to explain it here. The ice cream version. The walkie talkie dies completely. You left standing there unsure if your cone is coming or not. The truck shouts when it is ready. This is like a notification. Even with no reply, the trucks call out. Cone number 7 is done. You find out eventually a wear book or a notification. You call up and ask, what happened to order 7? You check the status of what specific order yourself. Both still use the name. the shout and the question both works because the order has one stable name seven to look it up by i hope this is you are understanding it from what i'm trying to tell you from a notification or a webbook point now the whole flow mobile says i want to pay server makes order seven save seven for you pay for seven charge server retries if needed if not fine and then shout reply either through the response web or through the notification now what we answer today is why not just one stop retry you can't you make the repeat safe instead who names the order who creates the id decide who retries always understand this problem statement client can also create the id when i say client that means mobile application can also make the id the only thing is then you have to rely on the mobile site to retry every time even with the payment gateways you cannot just say that i will only go and check with the server i will not go to the payment gateway this time you have to go to payment gateway to do the retries now all these steps need that much care no so resolving is sloppily safe what you can do actually you can save the space or save the item content id with the caching system if somehow it never reaches back to the client it's okay client can start a new request saying that okay i want to create another id where is the double charge i explained you when the double charge actually happens when you are using the same id and you are making the same call again same name different order this is something suppose you are you are saying that okay this is order xyz but first you said that i want to pay i want to pay 150 rupees but next time you are saying this is still order xyz and i want to pay 200 rupees this time the payment will not happen because your your identity is still the same your identity haven't changed when i say identity it can be your token your name combination of all so when you start an intent that intent also saves your identity against the payment so you cannot have a two different number of payments like 150 200 or 2000 rupees on the same intent if you are starting one intent you have to go with the one payment for another payment you have to start another intent never hear back truck shouts is equal to buy book if you never heard back it's okay if the payment happens doesn't happen doesn't matter it will come back to you through the notification or you refresh your page once you come back in the application now idempotency means repeated request lands on the same results not the second one it's only mandatory on the step that moves money and it works because the caller reuses one stable name across the retries i think same name every repeat that's the whole secret i hope you understand the idempotency and the questions follow-up questions behind it how to answer those most important question who creates the id what happens if you never heard back and how does the notification or webbook works so we are going to get a new topic on every week so stay tuned you are watching underflow with chichi
⚙️ Pipeline jobs
| Stage | Status | Att. | Updated | Error |
|---|---|---|---|---|
| download | done | 2/3 | 2026-07-20 14:59:07 | |
| transcribe | done | 1/3 | 2026-07-20 14:59:46 | |
| summarize | done | 1/3 | 2026-07-20 15:00:06 | |
| embed | done | 1/3 | 2026-07-20 15:00:07 |
📄 Описание YouTube
Показать
Tap "Pay" once, get charged twice. It's one of the most common failure modes in payment systems — and one of the most revealing interview topics. Anyone can define idempotency. The follow-ups are where senior engineers separate themselves. In this video we go past the textbook definition and answer the questions interviewers actually ask about idempotency in payment APIs: → Why can't we just stop the duplicate request from being sent? → Should the client or the server generate the payment ID? → Do all steps need to be idempotent — or only some of them? → Where exactly does the double-charge happen? → Same idempotency key, different request body — now what? → What if the confirmation is lost and you never hear back? We use one running picture the whole way through — ordering ice cream over a crackly walkie-talkie — so the mechanics stay intuitive, then ground every answer in a real two-phase payment flow: reserve, then pay, with the stable paymentID the client reuses on retry. If you've described idempotency in an interview but stumbled when they started probing the failure cases — this is for you.