Reference
referenceKeeps a per-payment refund budget and updates it atomically.
08 · Refunds
Allow partial refunds up to the captured amount, which may be less than what was authorized.
referenceKeeps a per-payment refund budget and updates it atomically.
alt_validReconstructs state by replaying an append-only JSON event journal; reference updates per-payment budget counters.
wrong_checks_single_refundChecks each refund against capture, ignoring earlier refunds.
wrong_counts_rejectedIncludes rejected attempts in the remaining refund budget.
wrong_uses_authorizedUses the authorized amount as the refund ceiling.
refund_check_then_writeExpect: rejectObserved: rejectedChecks refund budget and writes in separate transactions, violating atomic cumulative refund limits.
alt_conditional_updateExpect: acceptObserved: acceptedAtomically reserves refund budget with a conditional UPDATE instead of an explicit read lock.
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.
| Test | Starter | Reference | Alternative | checks single refund | counts rejected | uses authorized |
|---|---|---|---|---|---|---|
| Cumulative partials | Fail | Pass | Pass | Fail * | Fail | Pass |
| Capture less than authorization | Fail | Pass | Pass | Pass | Fail | Fail * |
| Valid after rejection | Fail | Pass | Pass | Pass | Fail * | Pass |
| Invalid and uncaptured | Pass | Pass | Pass | Pass | Pass | Pass |
| Unknown payment id raises value error | Fail | Pass | Pass | Pass | Pass | Pass |
| Capture once and within authorization | Fail | Pass | Pass | Fail | Pass | Fail |
| Concurrent refunds respect budget | Fail | Pass | Pass | Fail | Fail | Pass |
| Refund sequence matches greedy oracle | Fail | Pass | Pass | Fail | Fail | Fail |
| Test | Starter | Reference | Alternative | checks single refund | counts rejected | uses authorized |
|---|---|---|---|---|---|---|
| Single full refund | Pass | Pass | Pass | Pass | Pass | Pass |
| Over refund rejected | Fail | Pass | Pass | Pass | Pass | Pass |
| Ticket worked example | Fail | Pass | Pass | Fail | Fail | Fail |
| Refund before capture rejected | Pass | Pass | Pass | Pass | Pass | Pass |
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.