ADR-005 rollout step 2 (slice 1): capture cash-out settlements on the machine's dispense report #49
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/dispense-outcome"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The spirekeeper half of bitspire ADR-005 (
docs/adr/005-cash-out-dispense-outcome.mdin aiolabs/bitspire), pairing with aiolabs/bitspire#123. Fixes the structural root of bitspire#122.The change in one line: a
cash_outsettlement is no longer distributed when the payment lands — it lands asawaiting_dispenseand waits for the machine'sreport_dispense. Payment is the authorization; the report is the capture.What the report does
dispense_confirmedpendingpartial_pendingcash_owedremediates_txidpendingdispense_unreportedAlready-captured settlements are recorded, never moved. A byte-identical resend is acked without a new row. A report that arrives before its payment (hold invoices settle after the dispense; the invoice listener can lag) is stored unlinked and adopted when the settlement is inserted — same transition either way.
cash_inis untouched.Schema (m016, additive): append-only
dispense_reports(lamassu-server'scash_out_actionsshape);dispense_*columns +dispensed_fiat_centsondca_settlements(andbills_json/cassettes_jsonfinally get written — with what came out, not what was provisioned);cash_out_held_since/_reason/_codeondca_machines, mirrored from the machine's state document.Dashboard: three owed-cash buckets render first on the worklist;
partial_pendingrows open the partial-dispense dialog pre-filled from the hardware's own count; machine detail shows a held-cash-out banner with a Resume button →POST /machines/{id}/resume-cash-out, a new machine-wideresume_cash_outop (position 0, no position on the wire) on the existing operator channel. The machine honours it only if stamped after the hold began.Verified: 284/284 (20 new, project style — monkeypatched crud, no DB); ruff clean on new code; no new mypy errors; m016 smoke-executed against SQLite — every column lands. Not yet exercised end to end: that needs #123 on a machine and an lnbits restart here to run m016. Soft-fails like the other RPCs if
register_rpcis missing.Deliberately not in this PR (slice 2): the Nostr operator notification on
cash_owed, the off-machinesettle_cash_owedpath +settle_transactionop, thecash_out_enabledswitch, the error glossary, operator docs. Each carries its own design decision; the capture logic shouldn't wait on them.After merge:
sudo systemctl restart lnbitson bohm runs m016 against the dev DB. Version bump is a release step, not this PR (per the extension release procedure).Refs aiolabs/bitspire#122, aiolabs/bitspire#123.
Three buckets render first on the worklist — cash_owed, partial_pending, dispense_unreported (awaiting_dispense older than the threshold) — the only ones whose meaning is "a customer is owed money". partial_pending rows open the partial-dispense dialog pre-filled from the machine's report: the fraction from dispensed_fiat_cents / fiat_amount and the dispenser's error in the note, so the operator confirms a number the hardware produced rather than typing one. Machine detail shows a held-cash-out banner (code, time, reason) with a Resume button; POST /machines/{id}/resume-cash-out records a resume_cash_out op and publishes the window. The machine clears the hold on receipt and the banner clears on its next state report.