Documentation

Everything you need to ship

Getting started

Clone the repo, install dependencies, and copy the environment template:

pnpm install
cp .env.example apps/api/.dev.vars
cp .env.example apps/web/.env   # keep only the VITE_* lines

Provision a Neon database, set DATABASE_URL, then push the schema and start everything:

pnpm db:generate && pnpm db:migrate
pnpm dev

The web app runs on localhost:3000, the API on localhost:8787.

Payments & subscriptions

RayStarter monetizes three ways, all through Stripe:

  • Products — one-time purchases (courses, templates). Fulfilled by the webhook at /api/webhooks/stripe.
  • Subscriptions — recurring plans you create and price in the admin dashboard; the matching Stripe products and prices are provisioned automatically. Renewals and credit grants are handled by the payments module via the Stripe webhook.
  • Credit packs — prepaid AI credits that never expire.

For local testing: stripe listen --forward-to localhost:8787/api/webhooks/stripe

AI credits

Every AI generation is metered against per-model rates defined in packages/core/src/plans.ts. The system keeps an append-only ledger (credit_transaction) plus a cached balance on the user row — changed atomically in a single SQL statement, so the two can never drift.

Subscriptions grant monthly credits on every paid invoice, keyed by invoice ID so webhook retries can't double-grant.

Admin dashboard

Set a user's role to admin in the database (or via the Better Auth admin API) to unlock /admin:

UPDATE "user" SET role = 'admin' WHERE email = 'you@example.com';

From there you can manage products, write blog posts, cases and docs, review orders and AI usage, and ban or comp users.

Deploying

Both apps deploy to Cloudflare Workers:

pnpm --filter @raystarter/api deploy
pnpm --filter @raystarter/web deploy

Set production secrets with wrangler secret put in apps/api, create the R2 bucket (raystarter-media), and point Stripe's webhook endpoints at your deployed API.