ThisOrThat — the game marketplace
ToT is the buyer-facing marketplace where Flip and Pull plays happen. Buyers pick cards, choose a game, and a provably-fair RNG decides their final price — but they always get a card. The operator-side is admin-only; everything below the buyer surface (listings, plays, settlement) is automated.
Public surfaces
| Page | For | What's there |
|---|---|---|
/ | Anyone | Landing page — sells the games to first-time visitors. Live Flip + Pull simulation panels. |
/browse | Buyers | The marketplace grid. Faceted filter rail, search combobox, card detail slide-overs, Lots/Singles toggle. |
/listing/[id] | Buyers | Individual listing detail. Flip game UI for solo singles; Pull-only banner for sub-$15. |
/flip-cart | Buyers | Multi-card cart for combined flip plays |
/pull | Buyers | Pull stack assembly + commit page |
/play/[id] | Buyers | Result page after a play — outcome reveal + provably-fair verification |
/simulation | Anyone | Auto-playing demo of Flip and Pull. Tabbed view, real RNG, fake cards. |
/embed/simulation | Iframe | Chromeless version of /simulation for cross-site embedding |
Operator surfaces (admin-only)
| Page | What's there |
|---|---|
/collection | Staged cards from SlabTrack — CollectionCard rows. Operator can publish singles or lots manually (rarely needed since auto-publish landed). |
/seller | Sales dashboard — all orders, ship-label printing, refund trigger |
/admin | Admin home — system health, listings, plays, orders |
/admin/content | Listings + ad attribution + simulation seeding |
/admin/simulation | Demo Theatre — run buyer + operator scenarios in real-time |
The data model
| Table | Purpose |
|---|---|
User | Buyers + the platform admin. Auth via JWT shared with SlabTrack. |
CollectionCard | Cards staged from SlabTrack via the ingest webhook. Has bundle metadata (transferCode, bundleId, lotKind). |
Card | Pure card record. Joined to CollectionCard via slabtrackId. |
Listing | An active marketplace listing. Has type FLIP/PICK_ONE/GAME_DAY, status ACTIVE/COMPLETED, theme metadata, lotKind. |
FlipConfig | Per-listing pricing — cardValue, spread, winPrice, losePrice. |
Pull | A buyer's pull commit — selectedListingIds, pullPrice, seedHash, solanaTx, status. |
Play | Generic play record (Flip or Pull) with outcome, payment intent, settlement info. |
Order | Final purchase record — buyer, payment intent, order total, ship address, tracking. |
Refund | Refund records, partial supported. |
processedWebhookEvent | Stripe webhook dedupe — idempotency at the event level. |
The auto-publish flow
When SlabTrack's Strategist commits cards to ToT, the webhook fires POST /api/webhook/ingest with grouped card payloads. The webhook:
- Caches each card via
prisma.card.upsert({ where: { slabtrackId } })— Card rows are the canonical satellite-side card record - Stages a CollectionCard row per card with the transfer code + bundle metadata
- For each group in the payload, builds a Listing directly (recent change — used to require manual publish)
- Each Listing gets its FlipConfig with cardValue + spread + winPrice + losePrice
- Theme metadata (theme_key, theme_label, theme_sports, theme_teams) flows through so city lots render with team chip pills
- CollectionCard rows flip to
listed: truewith theirlistingId - Idempotent — group skipped if any of its cards already on a Listing
Stripe model
Direct charges on platform account. No Connect. All buyer money flows into the operator's single Stripe account; payouts handled by Stripe's normal payout schedule.
- Pull commits use Payment Intent + manual capture. The amount is captured only after the play settles.
- Flip plays do the same — auth at commit, capture at outcome, with the right amount based on win/lose.
- Cart purchases (multi-card flip) — same flow, scaled by combined cart total.
- Refunds: admin-only, partial supported, idempotency-keyed by order id.
- Webhook dedupe via
processedWebhookEventtable — same event from Stripe twice → only processed once.
Shipping model
One global ship-from address (operator's address) configured in env vars (SHIP_FROM_*). Shippo SDK calls go through src/app/api/shipping/route.ts. Operator triggers label printing from /seller; tracking number stored on Order; buyer-shipped email fires.
Code references
- Routes:
thisorthat/src/app/api/ - Game logic:
thisorthat/src/lib/games/ - Stripe singleton:
thisorthat/src/lib/stripe.ts(centralized, single API version) - Solana:
thisorthat/src/lib/blockchain/solana.ts - Schema:
thisorthat/prisma/schema.prisma