graymarshall.dev portfolio
Back to index
Live/2026

Invest Check.

Marketplace for verified, reconciled trader histories — built under PitLane Systems for Adam Edwards.

Invest Check
FIG. · invest-check
Year 2026Status LiveStackNext.js 16TypeScriptDrizzleNeonBetter AuthStripeResendRechartsTailwind v4Visit
I.
Summary
Overview

A verified-trading marketplace where individual traders publish reconciled brokerage and prop-firm histories, and followers buy access. Two purchase paths per trader: a permanent point-in-time snapshot or a recurring monthly subscription that grants live trade access plus a daily digest after market close. Bundle subscriptions cover multiple traders at a discount. Built ground-up under PitLane Systems for Adam Edwards.

II.
Margin
Notes
  • §Ground-up build on Next.js 16 App Router with React Server Components, Drizzle ORM over Neon serverless Postgres, Better Auth for identity, Stripe for billing, and Resend for transactional email. `vercel.ts` config gates Production builds to `main` only — every other branch is `deploymentEnabled: false` so dev pushes don't burn build minutes on a paid project.
  • §Two purchase paths per trader: permanent one-time snapshots (frozen point-in-time reports) and recurring monthly subscriptions (live trade access plus a daily digest). Bundle subs cover multiple traders under one Stripe subscription at a discount. Cancelling ends live access at period-end with no retained visibility; snapshots stay viewable forever.
  • §Better Auth handles identity end-to-end — email/password with OTP, Google OAuth, and the `@better-auth/stripe` plugin auto-creating Stripe Customers on signup and reconciling subscription state from webhooks. Per-trader Stripe Prices are minted lazily and *replaced* (never edited) on price changes; existing subscribers stay on their original Price until Stripe renews them.
  • §Single polymorphic `trades` table covers brokerage, option, future, and prop-firm rows with per-kind field projections, plus IANA-zone-aware bucketing so an 11:30 PM ET trade lands in the admin's intended calendar day, not the next UTC one. Stats compute helpers filter to the kinds they handle and skip rows missing required fields, so callers pass mixed sets without per-kind branching.
  • §Live analytics — equity curves, win rate, drawdown, average return, and a follow-simulation projecting follower returns at varying position sizes — all computed at request time from Drizzle queries against the trades table. No precomputed analytics tables; rendered live on both the public landing page and per-trader cards.
  • §Access-with-reason model: every view of a trader's full data resolves to an `AccessReason` discriminant (`free_preview | subscription | snapshot | none`), letting the UI surface *why* a user has access — "Active subscription," "Snapshot from Apr 12," "Included in current free preview." A single-flag free-preview profile (app-enforced "exactly one at a time") replaces a legacy `rank === 1` hardcode.
  • §Daily digest via Vercel Cron — `/api/cron/digest` runs at 22:00 UTC (DST-safe, always at least an hour after the US market close), authenticated by `CRON_SECRET`, rendering Resend emails to active subscribers. The admin "Run now" action reuses the same `lib/digest.ts` worker so scheduled and manual digests share one code path.