Skip to main content
Version: 2.x

Protect financial invariants

Prerequisites

Outcome

  • A billing path you can retry: the same idempotency key always replays the same ledger entry.
  • Leases that admit long-running work without charging it twice.
  • Refunds and expiry posted as auditable reversal entries, never silent write-offs.

Bursar enforces financial invariants at the canonical account boundary:

  • Each mutation locks the account before calculating a new balance.
  • An idempotency key maps to one ledger entry per account.
  • The account balance must equal the sum of its ledger entries.
  • Available lot amounts must equal derived bucket totals.
  • A strict-prepaid operation cannot cross its minimum balance.
  • Leases reserve capacity and can settle only once.
  • Refunds and expiry are reversal entries, never projection rewrites.

Idempotency keys

Every monetary mutation accepts an idempotency key. The store indexes (account, idempotency_key) uniquely, so a retry with the same key and the same request replays the original entry — the result is returned with idempotent=True and no second ledger row is created:

first = bursar.credits.add_credits(
user_id, 100, entry_type="purchase", idempotency_key="checkout:42"
)
replay = bursar.credits.add_credits(
user_id, 100, entry_type="purchase", idempotency_key="checkout:42"
)
assert first.entry_id == replay.entry_id and replay.idempotent

Reusing a key with a different request is a conflict, not a silent pass: the store rejects it, because two different payloads cannot share one ledger row. Use provider event ids as keys in webhook handlers so redelivery is a no-op — never a double charge. Keys are scoped per account.

Leases: the safe path for long-running AI work

When the final cost is only known after the work runs, charge through a lease instead of guessing at admission:

  1. reserve prices a worst-case estimate, enforces entitlement, quota, and allowance, and atomically creates the lease — the only admission gate.
  2. Do the work. Call renew before the TTL elapses on long jobs.
  3. settle bills the actual cost and finalizes the lease; release returns an unused hold on failure.
from bursar.credits.service_types import ReserveOptions, SettleOptions

lease = bursar.credits.reserve(
user_id,
estimate_metrics,
ReserveOptions(idempotency_key="job:42:reserve"),
)
try:
actual = run_completion()
result = bursar.credits.settle(
user_id,
lease.lease_id,
actual,
SettleOptions(idempotency_key="job:42:settle"),
)
except Exception:
bursar.credits.release(user_id, lease.lease_id)
raise

The lease captures its minimum_balance and pricing snapshot at admission. Settlement honors that captured policy even if the user changes plans while the work is in flight, so a plan change can never strand approved work — or let it bypass the policy it was admitted under. A lease settles exactly once; a settled or released lease cannot be charged again.

run_billed wraps reserve → work → settle in one call: on a do_work exception the lease is released automatically, and settlement is retried with a bounded attempt count because the outcome of a failed settle may be unknown:

outcome = bursar.credits.run_billed(
user_id,
RunBilledOptions(
estimate=estimate_metrics,
do_work=run_completion,
),
)

A crashed worker leaves the lease to expire: the TTL plus the store's expire_leases reaper reclaims the hold without ever charging it.

Refund bounds

refund_credits(entry_id, amount=...) refunds against the original entry, not the running balance. The store enforces the bounds: never more than the remaining original amount, no duplicate refunds, and only refundable entry kinds. Sources are preserved — the refund entry carries the original entry id — so the audit trail always shows which charge a refund reverses.

charge = bursar.credits.deduct_credits(user_id, 10, idempotency_key="operation:42")
refund = bursar.credits.refund_credits(
charge.entry_id,
amount=5,
idempotency_key="refund:operation:42:partial",
)
print(refund.amount, refund.new_balance)

Invariant checklist

InvariantEnforcement
Balance equals the ledger sumAccount row lock + transactional postings
One entry per (account, idempotency key)Unique index, checked under the row lock
Same key, different requestRejected as a conflict
No balance below minimum in strict-prepaidFloor checked at admission and settlement
Lease settles at most onceLease status machine (settlingsettled)
Plan changes cannot strand leased workMinimum balance captured at reserve
Refund never exceeds the originalRefunded-amount bounds on the source entry
Expiry is an accounting eventexpiry ledger entries, never silent write-offs

Team charges use the same posting path: deduct_team debits the team pool with the member stored as the ledger actor, and member spend caps are enforced in the same transaction. Spend caps, allowance windows, usage quotas, and maximum concurrency are account-plan policy — they never duplicate the monetary balance.