Neural Mastery
← Practice
Retries Refund Twicemedium

Retries Refund Twice

medium Specify, Verify, Debug 20–25 min

Standard library only · runs in your browser

The refund endpoint passes every test the agent wrote. In production, the mobile app retries any request that takes longer than two seconds, and on a slow day finance finds orders refunded twice. Every individual call was handled correctly.

Task

Every refund request carries an idempotency_key, generated once by the client and reused on every retry of that same request.

Fix handle(store, request) so that:

  • A retry gets the original response. Same key, same order_id and amount → return exactly the response the first call got, and change nothing. That includes error responses.
  • A reused key with a different body is an error. Return {"status": "error", "code": "idempotency_key_reused"}, and don't remember it.
  • New requests are checked in this order: unknown order → unknown_order; amount ≤ 0 → invalid_amount; already-refunded plus this amount over the order total → exceeds_order_total. Otherwise refund, and return {"status": "ok", "refund_id": "rf-<n>"}, where n counts up from 1001.

process_requests(orders, requests) (provided) replays a list of requests, retries included, and returns every response plus the total refunded per order.

Function Signature

handle(store: dict, request: dict) -> dict   (process_requests is provided)

Examples

Example 1: A request and its retry
Input: orders = {'o-1': {'total': 80}, 'o-2': {'total': 30}} requests = [{'idempotency_key': 'k1', 'order_id': 'o-1', 'amount': 30}, {'idempotency_key': 'k1', 'order_id': 'o-1', 'amount': 30}]
Output: {'responses': [{'status': 'ok', 'refund_id': 'rf-1001'}, {'status': 'ok', 'refund_id': 'rf-1001'}], 'refunded': {'o-1': 30}}
Explanation: Both calls should get refund rf-1001. The order is refunded 30, not 60.
Example 2: The slow day finance found
Input: orders = {'o-1': {'total': 80}, 'o-2': {'total': 30}} requests = [{'idempotency_key': 'k1', 'order_id': 'o-1', 'amount': 30}, {'idempotency_key': 'k1', 'order_id': 'o-1', 'amount': 30}, {'idempotency_key': 'k2', 'order_id': 'o-1', 'amount': 60}, {'idempotency_key': 'k3', 'order_id': 'o-1', 'amount': 50}, {'idempotency_key': 'k2', 'order_id': 'o-1', 'amount': 60}]
Output: {'responses': [{'status': 'ok', 'refund_id': 'rf-1001'}, {'status': 'ok', 'refund_id': 'rf-1001'}, {'status': 'error', 'code': 'exceeds_order_total'}, {'status': 'ok', 'refund_id': 'rf-1002'}, {'status': 'error', 'code': 'exceeds_order_total'}], 'refunded': {'o-1': 80}}
Explanation: k2 asks for too much and fails. Its retry must fail the same way, even though k3 changed the balance in between.
Example 3: A key reused for a different amount
Input: orders = {'o-1': {'total': 80}, 'o-2': {'total': 30}} requests = [{'idempotency_key': 'k1', 'order_id': 'o-1', 'amount': 30}, {'idempotency_key': 'k1', 'order_id': 'o-1', 'amount': 40}]
Output: {'responses': [{'status': 'ok', 'refund_id': 'rf-1001'}, {'status': 'error', 'code': 'idempotency_key_reused'}], 'refunded': {'o-1': 30}}

Constraints

  • `store` holds `orders`, `refunded` (order_id → amount), `seen` (for you to use) and `next_id`.
  • The starter is the agent's version: correct for one call, wrong under retries.
  • Hidden tests replay retries of failed requests and keys reused with new amounts.
Python3Saved ✓
import json
import math
import re
from datetime import date, datetime, timedelta


def handle(store, request):
"""Handle one refund request. Retries (same idempotency_key) must be safe."""
# The agent's version: correct for a single call.
order = store["orders"].get(request["order_id"])
if order is None:
return {"status": "error", "code": "unknown_order"}
if request["amount"] <= 0:
return {"status": "error", "code": "invalid_amount"}
if store["refunded"].get(request["order_id"], 0) + request["amount"] > order["total"]:
return {"status": "error", "code": "exceeds_order_total"}
store["refunded"][request["order_id"]] = store["refunded"].get(request["order_id"], 0) + request["amount"]
store["next_id"] += 1
return {"status": "ok", "refund_id": "rf-" + str(store["next_id"])}


# =============================================================================
# PROVIDED BELOW -- part of Helpdesk. Read it, don't edit it.
# =============================================================================
def process_requests(orders, requests):
"""Replay the requests exactly as the payment API received them,
retries included, and report what each caller got back."""
store = {"orders": orders, "refunded": {}, "seen": {}, "next_id": 1000}
responses = [handle(store, dict(r)) for r in requests]
return {"responses": responses, "refunded": store["refunded"]}

Case 1: A request and its retry
Input: orders = {'o-1': {'total': 80}, 'o-2': {'total': 30}} requests = [{'idempotency_key': 'k1', 'order_id': 'o-1', 'amount': 30}, {'idempotency_key': 'k1', 'order_id': 'o-1', 'amount': 30}]
Expected: {'responses': [{'status': 'ok', 'refund_id': 'rf-1001'}, {'status': 'ok', 'refund_id': 'rf-1001'}], 'refunded': {'o-1': 30}}
Case 2: The slow day finance found
Input: orders = {'o-1': {'total': 80}, 'o-2': {'total': 30}} requests = [{'idempotency_key': 'k1', 'order_id': 'o-1', 'amount': 30}, {'idempotency_key': 'k1', 'order_id': 'o-1', 'amount': 30}, {'idempotency_key': 'k2', 'order_id': 'o-1', 'amount': 60}, {'idempotency_key': 'k3', 'order_id': 'o-1', 'amount': 50}, {'idempotency_key': 'k2', 'order_id': 'o-1', 'amount': 60}]
Expected: {'responses': [{'status': 'ok', 'refund_id': 'rf-1001'}, {'status': 'ok', 'refund_id': 'rf-1001'}, {'status': 'error', 'code': 'exceeds_order_total'}, {'status': 'ok', 'refund_id': 'rf-1002'}, {'status': 'error', 'code': 'exceeds_order_total'}], 'refunded': {'o-1': 80}}
Case 3: A key reused for a different amount
Input: orders = {'o-1': {'total': 80}, 'o-2': {'total': 30}} requests = [{'idempotency_key': 'k1', 'order_id': 'o-1', 'amount': 30}, {'idempotency_key': 'k1', 'order_id': 'o-1', 'amount': 40}]
Expected: {'responses': [{'status': 'ok', 'refund_id': 'rf-1001'}, {'status': 'error', 'code': 'idempotency_key_reused'}], 'refunded': {'o-1': 30}}