Files
recflare/apps/econ
Devin Zuczek 7ab171b646 v4 avatars
2026-07-23 02:19:17 -04:00
..
2026-07-21 01:52:37 -04:00
2026-07-23 02:19:17 -04:00
2026-07-16 15:30:22 -04:00
2026-06-11 02:10:01 -04:00
2026-07-20 17:59:37 -04:00
2026-07-20 17:59:37 -04:00
2026-06-11 02:10:01 -04:00
2026-06-30 18:52:37 -04:00

econ

Economy Worker served on the econ subdomain (econ.recflare.net). Hosts the avatar/economy endpoints the game client calls on the econ service (distinct from the main api worker, which also serves many of them — the client may call either host).

Balances, inventory, consumables, saved outfits, avatars and gift boxes are D1-backed; storefront catalogs are static assets (static/storefronts/sf{N}.json) served via the ASSETS binding. Several routes are still empty-list stubs.

Routes

= auth-gated (validates the Bearer JWT from the auth worker; empty-body 401 when missing/invalid).

Method Path Auth Description
GET /api/avatar/v1/defaultunlocked Default-unlocked avatar items (static)
GET /api/avatar/v1/defaultbaseavataritems Default base avatar items (stub [])
GET /api/avatar/v4/items Owned items + the default catalog
GET /econ/customAvatarItems/v1/owned Owned custom avatar items (stub)
GET /api/objectives/v1/myprogress Objectives progress (static)
GET/POST /api/objectives/v1/cleargroup Clear an objectives group (no-op [])
GET /api/avatar/v2 The player's own avatar
POST /api/avatar/v2/set Save the player's avatar
GET /api/checklist/v1/current NUX checklist (stub [])
GET /api/itemWishlists/v1/wishlist/me Item wishlist (stub [])
GET /api/avatar/v3/saved Saved outfits
POST /api/avatar/v3/saved/set Save an outfit into a slot
GET /api/avatar/v2/gifts Pending (unopened) gift boxes
POST /api/avatar/v2/gifts/consume Open a gift box → success envelope
GET /api/avatar/v2/:id Another player's avatar (render subset)
GET /api/equipment/v2/getUnlocked Unlocked equipment (stub [])
GET /api/roomconsumables/v1/roomConsumable/room/:id Room consumables (stub [])
GET /api/roomconsumables/v1/roomConsumable/room/:id/me Caller's room consumables (stub [])
GET /api/roomcurrencies/v1/currencies Room currencies (stub [])
GET /api/roomcurrencies/v1/getAllBalances Room balances (stub [])
POST /api/settings/v2/set Persist settings (accept-and-ack)
GET /api/consumables/v2/getUnlocked Unlocked consumables
POST /api/consumables/v1/consume Consume an owned consumable
GET /api/storefronts/v4/balance/:currencyType Currency balance
GET /api/storefronts/v3/giftdropstore/:id Gift-drop storefront catalog
POST /api/storefronts/v2/buyItem Buy a storefront item
GET /api/storefronts/v1/adcarouselitems Ad-carousel items (static)
GET /api/challenge/v2/getCurrent Current weekly challenge (static)
POST /api/challenge/v2/updateProgress Report challenge progress (stub)
GET /api/gamerewards/v1/pending Pending game rewards (stub [])
POST /api/gamerewards/v1/request Request a game reward (stub [])
GET /api/roomkeys/v1/mine The player's room keys (stub [])
GET /api/roomkeys/v1/room Room keys for a room (stub [])
POST /api/CampusCard/v1/UpdateAndGetSubscription Subscription lookup (both null)
GET /openapi.json Generated OpenAPI 3.1 spec (see below)

The app runs with strict: false, so trailing-slash variants match (the client posts /gifts/consume/ with a trailing slash).

API documentation

GET /openapi.json serves a spec generated from describeRoute blocks alongside each handler, with the schemas in src/openapi.ts. Descriptive, not enforced — same rationale as the auth/accounts/match workers. A test asserts every route appears in the spec, so adding one without documenting it fails.

Purchases (buyItem)

The core flow. The client posts the storefront/item ids, the currency, and the RequestedPrice it rendered; the handler:

  1. looks the item up in static/storefronts/sf{StorefrontType}.json;
  2. rejects a stale price (409) — this stops a stale or tampered client buying at a price the catalog no longer offers;
  3. debits the buyer atomically (400 on insufficient balance);
  4. grants the drop — an avatar item into the inventory table (own-once), a consumable into the consumable table (each buy stacks a new instance); currency/xp drops aren't granted yet;
  5. returns a gift box and pushes a StorefrontBalanceUpdate over the socket.

Two things are easy to get wrong:

  • Balance in the response is the change applied (the negated price), not the resulting total. The client reads its new total from GET /balance/:type.
  • Ownership is persisted at purchase, not when the box is opened. Opening a box (/gifts/consume) just deletes it — the item was already granted. So the grant never waits on the cosmetic "open it" moment.

A Gift block routes the item (and box) to another player, but the caller always pays. A self-buy or anonymous gift is attributed to the "Coach" system account (id 1).

Consume envelopes

Both consume routes (/gifts/consume, /consumables/consume) always answer HTTP 200 with { error: "", success: true, value: null } — even for a missing or already-gone target. A captured real consume returns this envelope, not an empty body: the client parses it to finish the action, so a bare 200 reads as a failure and the item never finishes unlocking. Deletes are scoped to the caller, so an unauthenticated or mismatched call is a harmless no-op (opening another player's box is a 403).

Bindings

Binding Type Notes
DB D1 Shared recflare database — balances, inventory, etc.
JWT_SECRET Secrets Store Shared HS256 signing key (see the auth README)
ASSETS static assets Serves sf{N}.json storefront catalogs
RECFLARE_NOTIFICATIONS_HUB Durable Object Cross-worker RPC to the notify worker's hub
STARTING_TOKENS var Optional; new-player token grant (default in balance-db)

Add a storefront by dropping a new sfN.json in static/storefronts — no code change.

Known gaps

  • Gifting to another player grants the item and box but does not notify the recipient.
  • buyItem grants avatar-item and consumable drops; currency/xp drops aren't granted.
  • Consumables are granted and listed but never spent by gameplay, so Count only grows.
  • Several routes (room keys, wishlist, equipment, room consumables/currencies, game rewards) are empty-list stubs pending their own stores.