Supabase (Postgres + RLS) · Dodo Payments · 5 min read

Supabase + Dodo

SQL source of truth, Dodo for global cards.

Copy-paste this into Cursor, Claude, or Codex to scaffold a pay-to-rank board like ihatereading.lol.

In this stack

The prompt

Official docs, keys, and adapter rules the agent should follow.

Follow official docs only. Use the URLs, npm packages, and key locations in PLATFORM DETAILS. Do not invent SDK method names — look them up from those links.

Next.js + shadcn + Tailwind

OFFICIAL LINKS (open and follow these — do not invent APIs)

NPM PACKAGES

  • next
  • react
  • tailwindcss
  • class-variance-authority
  • lucide-react

API KEYS — name and exactly where to create them

SETUP STEPS

npx create-next-app@latest . --ts --tailwind --app --src-dir
npx shadcn@latest init
npx shadcn@latest add button input card badge

Supabase

OFFICIAL LINKS (open and follow these — do not invent APIs)

NPM PACKAGES

  • @supabase/supabase-js
  • @supabase/ssr

API KEYS — name and exactly where to create them

  • NEXT_PUBLIC_SUPABASE_URL: Supabase Dashboard → Project Settings → API → Project URL
  • NEXT_PUBLIC_SUPABASE_ANON_KEY: Same page → Project API keys → anon / public
  • SUPABASE_SERVICE_ROLE_KEY: Same page → service_role (secret). Server only. Bypasses RLS.

SETUP STEPS

  1. https://supabase.com/dashboard → New project
  2. Project Settings → API → copy URL, anon key, service_role
  3. SQL Editor: create listings, bids, pending_checkouts + indexes
  4. RLS: public SELECT on live listings; writes only with service_role in Route Handlers

Dodo Payments

OFFICIAL LINKS (open and follow these — do not invent APIs)

NPM PACKAGES

  • dodopayments
  • @dodopayments/nextjs

API KEYS — name and exactly where to create them

SETUP STEPS

  1. https://app.dodopayments.com/ → create merchant account
  2. Developer → API Keys → DODO_PAYMENTS_API_KEY
  3. Products → one-time Pay What You Want, minimum $1 → DODO_PAYMENTS_PRODUCT_ID
  4. checkoutSessions.create with product_cart[].amount in CENTS
  5. Developer → Webhooks → URL {SITE}/api/webhooks/dodo → DODO_PAYMENTS_WEBHOOK_KEY
  6. DODO_PAYMENTS_ENVIRONMENT=test_mode until you go live (then live_mode)
  7. Return URL {SITE}/success?checkout_id={pending_id}
  8. Also poll GET /api/status — webhooks can arrive after the browser returns

You are scaffolding a pay-to-rank bidding board (same product as ihatereading.lol).

PRODUCT

A public leaderboard. Anyone pastes a URL (or listing), types a bid of $1+, pays, and that listing rises by CUMULATIVE money. Highest total bid is #1. No user accounts. Anonymous is fine.

USER JOURNEY

  1. Land on / — hero + bid form + live board
  2. Paste URL → preview title/image/description (Open Graph)
  3. Set bid (number stepper, min $1 / 100 cents). Default the input to the current highest board bid.
  4. Pay → redirect to provider checkout
  5. Return /success?checkout_id=... → poll status until live → show rank + confetti
  6. Board shows rank, thumbnail, title, total $, bid count. Click a row → /listing/[id] to add another bid (stack).

DOMAIN (use these names, do not invent a different model)

  • listings: id, url, type, title, description, thumbnail_url, favicon_url, author_name, submitted_by_name, total_bid_cents, bid_count, bid_cents_last_24h, status (pending|live|rejected), created_at, last_bid_at
  • bids: id, listing_id, amount_cents, bidder_name, provider, checkout_id, payment_id, customer_email, created_at
  • pending_checkouts: id, url, preview fields, bid_amount_cents, existing_listing_id (null = new URL), provider_session_id, created_at
  • Optional: presence + stats counters for "N online" / views

RULES (must implement)

  • Min bid $1 (400 if amount_cents < 100)
  • Normalize URLs (https, strip tracking params). Same URL cannot create a second listing — return 409 with existing_listing_id; user must stack a bid instead
  • Stacking: paying against existing_listing_id adds amount to total_bid_cents and bid_count
  • Rank all-time: ORDER BY total_bid_cents DESC, last_bid_at DESC
  • Rank trending: ORDER BY bid_cents_last_24h DESC
  • Payments are idempotent: never insert the same payment_id twice
  • Flow: create pending_checkout → create provider checkout with metadata { pending_id, bid_amount_cents } → webhook OR success-page poll fulfills → create/update listing + insert bid
  • Server-only writes for money (never let the browser increment totals)
  • No auth required
  • Admin page optional: password gate to reject a listing

PAGES

  • / landing (form + board)
  • /listing/[id] detail + stack bid + bid history
  • /success payment confirmation
  • /stats simple counters
  • shadcn/ui + Tailwind. Light theme. Mobile-first: stack board cards on small screens.

APIS

  • POST /api/preview { url }
  • POST /api/checkout { url, preview, bid_amount_cents, existing_listing_id? }
  • GET /api/status?checkout_id=
  • POST /api/webhooks/[provider]
  • GET /api/board?sort=all_time|trending

SHIP

  • .env.example with every key AND a comment above each key: what it is + the exact dashboard URL to create it
  • README: numbered setup using the PLATFORM DETAILS links below
  • Seed + clear scripts
  • TypeScript strict. Do not add extra features (auth, AI ranking, comments).

STACK (do not substitute)

  • Next.js 14+ App Router, TypeScript, Tailwind CSS, shadcn/ui, React Query or SWR
  • Database: Supabase (Postgres + RLS)
  • Payments: Dodo Payments

ADAPTER RULES

Supabase tables + service-role writes only.

Dodo PWYW checkout (amount in cents), webhook + status poll fallback.

Use a Postgres transaction when applying a successful payment.

ENV KEYS for .env.example (comment each with the dashboard URL from PLATFORM DETAILS)

  • NEXT_PUBLIC_SITE_URL
  • NEXT_PUBLIC_SUPABASE_URL
  • NEXT_PUBLIC_SUPABASE_ANON_KEY
  • SUPABASE_SERVICE_ROLE_KEY
  • DODO_PAYMENTS_API_KEY
  • DODO_PAYMENTS_PRODUCT_ID
  • DODO_PAYMENTS_WEBHOOK_KEY
  • DODO_PAYMENTS_ENVIRONMENT=test_mode

Copy every official link from PLATFORM DETAILS into README.md under Setup.

Build the complete runnable app now. After files exist, print: install, env, migrate/seed, webhook listen, npm run dev.