Wallet Service
Wallet behavior is implemented across:
apps/api/src/services/wallet-service.tsapps/api/src/routes/consumer.ts
Responsibilities
- create wallet records lazily per tenant+user
- append immutable ledger credits/debits
- maintain fast-read snapshot balance
- support redemption preview and confirmation
- record reward event linkage for earned points
Credit Flow
creditWallet(...) runs in one DB transaction:
- upsert wallet (
tenant_id,user_id) - insert
wallet_ledgercredit entry - increment
wallets.points_balance - insert
reward_events
This path is used by transaction ingestion.
Redemption Flow
Preview
POST /api/v1/consumer/wallets/:walletId/redeem/preview
- verifies wallet ownership
- verifies requested points are less than or equal to current balance
- returns projected discount and resulting balance
Confirm
POST /api/v1/consumer/wallets/:walletId/redeem/confirm
Within DB transaction:
- lock wallet row with
FOR UPDATE - validate balance
- append debit ledger entry
- decrement snapshot balance
Then API returns:
- 6-char redemption code
- 15-minute expiry timestamp
Ledger Model
wallet_ledger is append-only and constrained by schema checks.
- positive points only
entry_typecontrols debit/credit direction- optional references to source transaction and admin actor
Data Integrity Characteristics
- one wallet per tenant+user (
wallets_tenant_user_unique) - race-safe redemption via row lock
- snapshot and ledger mutations happen in the same transaction
Admin Wallet Controls
Admin routes can:
- freeze/unfreeze wallet (audit logged)
- adjust wallet points (credit or debit) with reason
All privileged actions are written to admin_audit_logs.
Current Boundaries
- redemption code verification workflow is intentionally simple in current phase
- reconciliation jobs are not yet a dedicated worker module
- idempotency-key replay snapshots are not fully surfaced to consumers yet
Related Docs
Written byDhruv Doshi