The Token System
Tokens are an internal currency: users buy bundles, plugins debit for usage, and every movement is an auditable transaction.
Concepts & data model
Four entities: UserTokenBalance (the current balance), TokenTransaction (an immutable ledger row), TokenBundle (an admin-configured purchasable package) and TokenBundlePurchase (a purchase moving through payment → completion).
TokenService API
service.get_balance(user_id) -> int
service.credit_tokens(user_id, amount, transaction_type, …)
service.debit_tokens(user_id, amount, transaction_type, …)
service.refund_tokens(user_id, amount, …)
service.get_transactions(user_id, limit=50, offset=0)Tokens are credited on subscription activation and on bundle purchase completion; they are debited by plugins that meter usage (e.g. a chat bot charging per word).
Atomic debit pattern
Create the usage record and the debit in the same transaction so a failure rolls back both — never debit then create:
with db.session.begin_nested():
record = MyUsage(user_id=user_id, cost=cost)
db.session.add(record)
service.debit_tokens(user_id, cost, TxType.USAGE,
reference_id=record.id)
# both committed, or neitherdebit_tokens raises and the API returns 402 insufficient_tokens — surface a “buy tokens” path to the user.Reading cost from config
Never hardcode prices. Read the per-action cost from the plugin's config (which the admin can edit), defaulting to DEFAULT_CONFIG so a fresh install still works.