account_balance.billing_mode.
Postpaid is off until smsg.balance.postpaid.enabled is true, so migrating adds two columns and
changes nothing until you say so.
What changes
A postpaid account is not debited at all. Itsbalance stays at zero, no block is reserved, and
what it owes is derived from cdr_submit when the bill is prepared.
What the gateway enforces instead is credit_limit — how much unbilled usage may accrue before the
account is refused.
Hitting the limit is the same refusal as running out of prepaid credit — SMPP 0x402, HTTP
402, reason insufficientCredit. Deliberately: both mean “you may not send this now”, and two
status codes would make every client on the platform learn a second one.
Exact bill, approximate enforcement
These are two different jobs, and they deliberately do not share an implementation.The limit check
Reads an in-memory figure refreshed on the same 30-second poll that applies recharges. It is
allowed to be stale.
The invoice
A pure function of
cdr_submit over a half-open window. Never reads that counter, never reads
account_balance, never reads the ledger.cdr_submit row. Nothing about
the enforcement path can corrupt an invoice.
The staleness is biased safely
A poll can only ever tighten headroom, never raise it — because the query cannot see messages sent while it was running. Headroom rises only when the debt genuinely shrinks: a period closed, a refund landed, or an operator changed the limit. That bias is the point. Staleness refuses too early rather than too late.How far past the limit an account can get
The last row is the one to watch if you ever run more than one gateway against a database. It is
stated because it is the one that scales, not because it is a current problem.
Choosing between the two
Prepaid is the safer default for a new account. Postpaid earns its risk with customers whose volume
makes constant top-ups a nuisance.
Related
Prepaid credit
Blocks, reservations and the money identity.
Statements
Closing a period and issuing the bill.