8.3 KiB
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:
- looks the item up in
static/storefronts/sf{StorefrontType}.json; - rejects a stale price (
409) — this stops a stale or tampered client buying at a price the catalog no longer offers; - debits the buyer atomically (
400on insufficient balance); - grants the drop — an avatar item into the
inventorytable (own-once), a consumable into theconsumabletable (each buy stacks a new instance); currency/xp drops aren't granted yet; - returns a gift box and pushes a
StorefrontBalanceUpdateover the socket.
Two things are easy to get wrong:
Balancein the response is the change applied (the negated price), not the resulting total. The client reads its new total fromGET /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.
buyItemgrants avatar-item and consumable drops; currency/xp drops aren't granted.- Consumables are granted and listed but never spent by gameplay, so
Countonly grows. - Several routes (room keys, wishlist, equipment, room consumables/currencies, game rewards) are empty-list stubs pending their own stores.