Networks fail. A customer taps "Pay", your shop charges the card and saves the order, and then the phone loses its connection before your answer arrives. The app can't tell whether the payment went through, so it sends the same POST /orders/8/pay again, and the card is charged twice.
The fix is an idempotency key. The app makes up a random key once per payment and sends it in the header Idempotency-Key, with the first try and with every retry. Your shop remembers its answer for each key: when a key comes back, it sends the saved answer again instead of charging again. A request you can repeat with the same effect as sending it once is called idempotent.
A dictionary makes a good memory. in tells you whether a key is already there, and None is never in it unless you put it there:
SEEN = {}
SEEN["abc"] = {"ticket": 1}
print("abc" in SEEN) # True
print("xyz" in SEEN) # False
print(None in SEEN) # False
Your code must answer like this. GET /check/payments is added by the checker: it lists the payments the provider made.
POST /orders/8/pay (Idempotency-Key: 5f1d7c9e-…) → 201 {"amount": 80, "order_id": 8, "payment_id": "pay_1"}
POST /orders/8/pay (the same key) → 201 {"amount": 80, "order_id": 8, "payment_id": "pay_1"}
GET /check/payments → 200 [{"amount": 80, "id": "pay_1", "order_id": 8, "status": "succeeded"}]
POST /orders/10/pay (no key) → 201 {"amount": 21, "order_id": 10, "payment_id": "pay_2"}
POST /orders/11/pay (no key) → 201 {"amount": 18, "order_id": 11, "payment_id": "pay_3"}
POST /orders/9/pay (Idempotency-Key: c2a94e51-…) → 402 {"error": "Payment declined", "reason": "card_declined"}
GET /check/payments → 200 [{… "id": "pay_1" …}, {… "id": "pay_2" …}, {… "id": "pay_3" …}]
Your task: make POST /orders/<id>/pay idempotent. Keep a dictionary ANSWERS and read the Idempotency-Key header. If the key is already in ANSWERS, answer its saved answer with 201 before the provider is called. After a new payment, save the answer under its key (only when the client sent one), then return it with 201.
Assignment
Follow the Coding Tutorial to remember each payment's answer under its idempotency key and send it again for a retry.
Hintrequest.headers.get("Idempotency-Key") gives None when the header is missing. Check the key before calling the provider, and save the answer only when key is not None: otherwise every request without a key would share the key None.
Free plan: 30 tutor messages. PRO is unlimited.
