Balance Service

Global wallet model

Why @downcity/services stores balance by user instead of creating one wallet per downcity.

@downcity/services keeps balance by user, not by downcity.

By default, credits are USD-linked:

  • 1 credit = 1 USD
  • 1 credit = 1,000,000 microcredits
  • user-facing GET /v1/balance/me returns balance in credits, plus microcredits, display, and conversion
  • admin and internal accounting fields still use microcredits

That means:

  • user A spends inside downcity X
  • user A spends inside downcity Y
  • both consume the same shared balance

Why this fits Downcity better

The typical Downcity shape is:

  • one Federation serving multiple products
  • the same user moving across those products
  • one shared AI account layer behind them

If balance is split per product, both product UX and operations become fragmented.

Does downcity still matter

Yes, but it belongs in ledger metadata rather than in the wallet primary key.

await balance.sub(ctx.user!.user_id, 10, {
  note: "agent chat",
  meta: {
    city_id: ctx.downcity?.city_id,
    model_id: ctx.variant?.id,
  },
});

That keeps the wallet global while still preserving where the spend came from.