08 · Refunds

Limit refunds to the captured amount

Allow partial refunds up to the captured amount, which may be less than what was authorized.

Python · SQLitemedium

What the hardened grader checks

Solutions

Reference

reference

Keeps a per-payment refund budget and updates it atomically.

Naive: accepted (4/4)Hardened: accepted (8/8)

Alternative

alt_valid

Reconstructs state by replaying an append-only JSON event journal; reference updates per-payment budget counters.

Naive: accepted (4/4)Hardened: accepted (8/8)

wrong_checks_single_refund

Checks each refund against capture, ignoring earlier refunds.

Naive: rejected (3/4)Hardened: rejected (4/8)

wrong_counts_rejected

Includes rejected attempts in the remaining refund budget.

Naive: rejected (3/4)Hardened: rejected (3/8)

wrong_uses_authorized

Uses the authorized amount as the refund ceiling.

Naive: rejected (3/4)Hardened: rejected (5/8)

Held-out solutions

Tests × solutions

Each row groups a test’s parameterized cases; any failing case marks the row as Fail. * marks a wrong solution’s declared must_fail test.

Hardened · 8 recorded cases · 8 test rows
TestStarterReferenceAlternativechecks single refundcounts rejecteduses authorized
Cumulative partialsFailPassPassFail *FailPass
Capture less than authorizationFailPassPassPassFailFail *
Valid after rejectionFailPassPassPassFail *Pass
Invalid and uncapturedPassPassPassPassPassPass
Unknown payment id raises value errorFailPassPassPassPassPass
Capture once and within authorizationFailPassPassFailPassFail
Concurrent refunds respect budgetFailPassPassFailFailPass
Refund sequence matches greedy oracleFailPassPassFailFailFail
Naive grader
Naive · 4 recorded cases · 4 test rows
TestStarterReferenceAlternativechecks single refundcounts rejecteduses authorized
Single full refundPassPassPassPassPassPass
Over refund rejectedFailPassPassPassPassPass
Ticket worked exampleFailPassPassFailFailFail
Refund before capture rejectedPassPassPassPassPassPass

The ticket

Ticket: refund only captured funds

refunds.service.RefundService(db_path) uses SQLite and exposes create_payment(authorized_minor)->id, capture(id,amount_minor), refund(id,amount_minor)->refund_id, refunded_total(id)->int. Exceptions: RefundExceedsCaptured, PaymentNotCaptured, InvalidAmount (all derive from ValueError).

All money is integer minor units in one currency (CAD, 2 dp), never bool. Authorization, capture and refund amounts must be positive ints, otherwise InvalidAmount. An unknown payment id raises ValueError from capture, refund and refunded_total (after the amount has been validated). Each payment may be captured once, at most its authorization; invalid/repeated capture -> InvalidAmount. Refund before capture -> PaymentNotCaptured. Partial refunds are allowed. The cumulative successful refund amount may not exceed the captured amount, which may be less than authorized. Exceeding remaining captured funds -> RefundExceedsCaptured. Rejected attempts must never count toward refunded_total or reduce the future budget. Validate positive amount before capture status.

Refund ids are distinct positive ints. Capture and successful refunds survive a new service instance on the same file. Operations are atomic; no rejected operation may alter successful totals. Worked example: create_payment(10000), capture(id, 6000). refund(id, 4000) succeeds and refunded_total(id) == 4000. refund(id, 2500) then raises RefundExceedsCaptured, because only 2000 of the captured 6000 remains, and refunded_total(id) is still 4000.

No settlement network or real customer is involved. Preserve the interface and run python -m pytest tests -q from starter.

Source