Start learning free

LESSON 111 OF 173 · BACKEND: PAYMENTS AND OTHER SERVICES

The Same Request Twice in Java

Practice

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 HashMap makes a good memory. containsKey tells you whether a key is already there, and null is never in it unless you put it there:

Map<String, Object> seen = new HashMap<>();
seen.put("abc", Map.of("ticket", 1));
System.out.println(seen.containsKey("abc"));   // true
System.out.println(seen.containsKey("xyz"));   // false
System.out.println(seen.containsKey(null));    // 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/{orderId}/pay idempotent. Keep a map ANSWERS; payOrder already gets the Idempotency-Key header in its key parameter. 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.


Hint
@RequestHeader(value = "Idempotency-Key", required = false) gives null when the header is missing. Check the key before calling the provider, and save the answer only when key != null: otherwise every request without a key would share the key null. Until the last step, payOrder ends without a return, so the code won't compile.


STEP 1 OF 7
    Ctrl + Enter to check

    Pay Idempotency in Java

    Keep going with Java1,600+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free