Allowed R6 · Within policy
{
"slug": "idempotent-topup-api",
"title": "Idempotent Top-Up API",
"difficulty": "medium",
"tags": [
"coding",
"oop-design",
"technical",
"backend"
],
"is_free": true,
"locked": false,
"frequency": 0.75,
"last_reported_at": "2026-06-14T22:54:45.272524Z",
"url": "https://www.mokabla.com/en/c/orange/idempotent-topup-api",
"agent": {
"page": "https://www.mokabla.com/agent/en/c/orange/idempotent-topup-api",
"question": "https://www.mokabla.com/agent/_call/get_question?company=orange&question=idempotent-topup-api"
},
"company": {
"slug": "orange",
"name_en": "Orange Egypt / Orange Business"
},
"body_md": "## Problem\nWe expose a Spring Boot endpoint `POST /topups` that adds credit to a subscriber's balance. Mobile clients retry on timeouts, so the same top-up sometimes arrives two or three times and the customer gets charged once but credited twice. Design the endpoint so that retries are safe, then implement the core service method in memory: `topUp(idempotencyKey, msisdn, amount)` returns the resulting balance and must produce exactly one credit per key, even under concurrent calls.\n\nIn the Java version I expect to hear about `@Transactional`, a unique constraint on the idempotency key, and what happens when two threads hit the method at once.\n\n## Examples\n**Example 1**\n`topUp(\"k1\", \"0100000001\", 50)` → balance 50; `topUp(\"k1\", \"0100000001\", 50)` again → still 50 (replayed response, no second credit); `topUp(\"k2\", \"0100000001\", 20)` → 70.\n\n**Example 2**\n`topUp(\"k9\", \"0100000002\", 30)` succeeds; `topUp(\"k9\", \"0100000002\", 99)`, same key, different payload → reject with a conflict error rather than silently returning the old result.\n\n## Constraints\n- Keys are client-generated UUIDs, valid for 24 hours\n- Two concurrent requests with the same key must not both credit\n- Balance is an integer number of piasters, never negative\n\n## What they look for\nStore-then-act ordering (insert the key first, credit second, inside one transaction), a DB unique constraint as the real guard, payload hash comparison for the conflict case, and knowing that `synchronized` does not help across two pods.",
"follow_ups_md": "- Where would you cache the replayed response and for how long?\n- How does this interact with the payment gateway's own retries?\n- Which HTTP status for a replay, and which for a conflict?",
"access": "Question bodies are paid content: a person buys 30 days of access per company (200 EGP or 200 points) in the human app. Agents get titles, tags and difficulty; bodies only for free questions."
}