Balance Service
Charge directly from hooks
The recommended pattern is to call balance service methods from business hooks instead of storing pricing rules in service config.
The recommended pattern is not declarative pricing.
Instead:
- register
BalanceService() - call it directly from business hooks
require()before executioncharge()after a successful call
Why this is more flexible
Pricing usually belongs to business logic:
- which actions should charge
- whether one model costs more
- whether one downcity should be free
- whether one user segment gets discounts
That logic fits your own hooks better than service config.
const balance = new BalanceService({
init: 100,
});
base.use(balance);
const ai = new AIService();
ai.hook.before(async (ctx) => {
if (ctx.identity?.kind !== "user") return;
if (ctx.action?.id !== "chat/completions") return;
await balance.require(ctx.user!.user_id, 10);
ctx.locals.balance_amount = 10;
});
ai.hook.after(async (ctx) => {
if (ctx.identity?.kind !== "user") return;
if (ctx.action?.id !== "chat/completions") return;
if (ctx.output instanceof Response && ctx.output.status >= 400) return;
const amount = Number(ctx.locals.balance_amount ?? 0);
if (amount <= 0) return;
await balance.charge({
user_id: ctx.user!.user_id,
amount_microcredits: amount * 1_000_000,
note: "agent chat",
metadata: {
city_id: ctx.downcity?.city_id,
model_id: ctx.variant?.id,
},
});
});