Wallet
The wallet domain is the balance and redemption layer for consumer points.
Current Data Model
walletsstores the fast-read balance snapshot.wallet_ledgerstores the append-only audit trail.reward_eventslinks transactions to wallet changes.redemption_codesstores temporary codes for in-store redemption.
Credit Path
apps/api/src/services/wallet-service.ts performs wallet credit in one transaction:
- Upsert the wallet row for
(tenantId, userId). - Append a credit entry to
wallet_ledger. - Update the wallet snapshot balance.
- Insert the linked
reward_eventsrow.
Redemption Path
Consumer redemption happens in two steps:
- preview: calculates the discount and new balance
- confirm: locks the wallet row, writes a debit ledger entry, and issues a redemption code
The POS operator later validates the redemption code through /api/v1/pos/redemptions/*.
Invariants
- ledger rows are append-only
- points are always stored as positive integers
- the snapshot balance is a read optimization, not the source of truth
- redemption uses
FOR UPDATEto avoid concurrent overspend
Written byDhruv Doshi