Billing
Customers do not pay invoices. They pay amounts.
One transfer against four invoices with a reference naming two of them. Half an invoice now and the rest next month. A round number that does not match anything.
Every one of those is ordinary, and every one is where an invoice ledger hands the work back to a person with a spreadsheet.
Splitting
It proposes, explains, and waits.
Three rules in order. Any invoice named in the payment reference — matched longest-first, so a full number beats a prefix of itself. Then, only if nothing matched yet, an invoice whose balance is exactly the amount, and only when there is precisely one. Then oldest first.
Each proposed line comes back with why it was proposed. Nothing is applied until somebody accepts, and what is left over stays as credit rather than being pushed onto an invoice that did not earn it.
- INV-2026-0184£12,400.00exactnamed in the payment reference
- INV-2026-0179£8,150.00exactthe only open invoice for that exact amount
- INV-2026-0166£6,000.00oldest firstoldest still open
- INV-2026-0171£2,450.00oldest firstnext oldest — partially filled
- Applying many at once is one act —
Duplicate rows are folded, and any invoice belonging to a different customer than the payment is refused rather than quietly allocated.
- Overpaying an invoice is refused —
Checked against a freshly recomputed balance, not a cached one.
- Un-allocating reverses properly —
And restores the invoice to the state it was actually in — sent if it had been sent, rather than blindly back to finalised.
The guarantee
The database refuses money that does not exist.
Allocating more of a payment than the payment is worth is not prevented by careful code. It is refused by the database, which re-sums every allocation on write and rejects the transaction if the total exceeds the payment. The same guard exists for credit notes against their own total.
Which means a concurrency bug in a web handler — two clicks, two workers, a retry — cannot produce an over-allocated payment. The invariant does not depend on the code path being right.
- A receipt number is minted on capture, not on attempt —
A declined card leaves no gap in the sequence.
- Gateway payments cannot be recorded twice —
Enforced on the provider's own reference — and offline payments, which have no such reference, are exempt rather than blocked.
- A missed notification is caught overnight —
A nightly pass replays the provider's own record of the last day through exactly the same path. It cannot double-count, because the same guard applies.
- The incoming notification identifies its own workspace —
By which stored secret validates the signature — the provider does not know who you are, so the signature answers it.
›Which gateways?
Two, with payment links, incoming notifications and the overnight reconciliation behind both.
›Can a customer pay part of an invoice?
Yes — it is the normal case rather than a special one. The invoice moves to partly paid and the balance is maintained by the database.
›Is there a payment schedule or instalment plan?
No. Partial payments are supported as they arrive; there is no agreed schedule object, and no automatic interest or late fee. Dunning escalates and can place a hold — it never adds money to the balance.
›Is there a standalone payments register?
Payments are read on the invoice they belong to. There is no separate ledger screen today.