Upstash Redis · Stripe Checkout · 5 min read

Upstash + Stripe

Tiny deploy. Sorted set for the leaderboard.

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

Upstash Redis

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

NPM PACKAGES

  • @upstash/redis

API KEYS — name and exactly where to create them

  • UPSTASH_REDIS_REST_URL: Upstash Console → database → REST API → UPSTASH_REDIS_REST_URL
  • UPSTASH_REDIS_REST_TOKEN: Same page → UPSTASH_REDIS_REST_TOKEN

SETUP STEPS

  1. https://console.upstash.com → Create Redis database
  2. Copy REST URL + token
  3. Use a ZSET score = total_bid_cents for ranking

Stripe

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

NPM PACKAGES

  • stripe

API KEYS — name and exactly where to create them

  • STRIPE_SECRET_KEY: https://dashboard.stripe.com/apikeys → Secret key (sk_test_… locally, sk_live_… in prod). Server only.
  • NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY: Same page → Publishable key (pk_test_…). Only if you use Stripe.js; Checkout redirect can skip it.
  • STRIPE_WEBHOOK_SECRET: https://dashboard.stripe.com/webhooks → Add endpoint → Signing secret (whsec_…). Locally: stripe listen --forward-to localhost:3000/api/webhooks/stripe

SETUP STEPS

  1. https://dashboard.stripe.com → sign up, stay in Test mode
  2. Developers → API keys → STRIPE_SECRET_KEY
  3. Create Checkout Session: mode=payment, line_items price_data.unit_amount = bid cents, metadata.pending_id
  4. Success URL: {SITE}/success?checkout_id={pending_id}
  5. Webhook endpoint: {SITE}/api/webhooks/stripe event checkout.session.completed
  6. Local: stripe listen --forward-to localhost:3000/api/webhooks/stripe → STRIPE_WEBHOOK_SECRET
  7. Test card: 4242 4242 4242 4242

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: Upstash Redis
  • Payments: Stripe Checkout

ADAPTER RULES

Store listing hashes + a Redis ZSET score = total_bid_cents for ranking.

Bids as a list per listing. All writes in Route Handlers.

Stripe Checkout + webhook. Fine for a small board; document the scale limit.

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

  • NEXT_PUBLIC_SITE_URL
  • UPSTASH_REDIS_REST_URL
  • UPSTASH_REDIS_REST_TOKEN
  • STRIPE_SECRET_KEY
  • STRIPE_WEBHOOK_SECRET

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.