Currency, storefronts, purchasing (#12)

And a few other minor things, but primarily, the balance table exists and also consumable/inventory table.
This commit is contained in:
devin
2026-07-15 14:10:49 -04:00
committed by GitHub
parent 8314e54439
commit c33919bc67
29 changed files with 2204 additions and 524 deletions
+13 -5
View File
@@ -1,5 +1,7 @@
import { Hono } from 'hono'
import { consumeGift } from '@repo/domain'
import { authedId, unauthorized } from '../http'
import {
createInvention,
@@ -89,13 +91,16 @@ export const avatarRoutes = new Hono<App>({ strict: false })
})
.post('/api/avatar/v2/gifts/consume', async (c) => {
const id = await authedId(c)
if (id === null) return unauthorized(c)
const body = await c.req.parseBody().catch(() => ({}) as Record<string, unknown>)
const giftId = typeof body.Id === 'string' ? Number.parseInt(body.Id, 10) || 0 : 0
if (giftId === 0) return c.json({ success: false, error: 'Invalid gift ID' }, 400)
// No DB → gift can never be found.
return c.json({ success: false, error: 'Gift not found' }, 404)
// Opening a box just deletes it — the item was granted into the player's inventory
// when they bought it (see the `econ` worker's buyItem), so there's nothing to grant.
// Answers the `{ error, success, value }` envelope a captured real consume returns
// (not an empty body — the client parses it to finish opening the box). A missing/zero
// id, no token, or a box that's already gone (or isn't theirs) is a scoped no-op, not
// an error. Mirrors the econ worker's consume route (the client may call either host).
if (id !== null && giftId !== 0) await consumeGift(c.env.DB, id, giftId)
return c.json({ error: '', success: true, value: null })
})
// Custom avatar item gates — real Rec Room client endpoints with no backing
@@ -108,6 +113,9 @@ export const avatarRoutes = new Hono<App>({ strict: false })
.get('/api/customAvatarItems/v1/isCreationEnabled', (c) => c.json(true))
.get('/api/customAvatarItems/v1/isRenderingEnabled', (c) => c.json(true))
// The featured custom-avatar-item feed. No curated items yet → an empty list.
.get('/api/customAvatarItems/v1/featured', (c) => c.json([]))
// Custom avatar items created by a given account. No storage yet → an empty
// paginated result (matches the econ `customAvatarItems/v1/owned` shape).
.get('/api/customAvatarItems/v2/fromCreator/:accountId{[0-9]+}', (c) =>