Files
route-commerce/ENVIRONMENT.md
T
Nora 42f5745c97 docs: refresh stale docs to match current implementation
Documentation pass — reviewed codebase in full, identified stale references,
and aligned docs with actual code state.

Changes:
- CLAUDE.md, PRODUCTION_SETUP.md, LAUNCH_CHECKLIST.md, src/auth.config.ts:
  middleware path src/middleware.ts → src/proxy.ts (Next.js 16+ convention)
- CLAUDE.md, ENVIRONMENT.md, README.md:
  dev server port localhost:3000 → localhost:4000 (per package.json dev
  script 'next dev --webpack -H 0.0.0.0 -p 4000')
- PRODUCTION_DEPLOYMENT_CHECKLIST.md:
  rewrote migration list — actual db/migrations/ contains 10 files
  (0000–0091), not the Supabase-era 001–092 series. Updated platform
  version 1.6 → 2.0 and last-updated stamp.
- MEMORY.md:
  refreshed 'Last updated' to 2026-06-25 and added a current-state header
  flagging the 'Auth.js v5 wiring' section as historical (current auth
  is Neon Auth/Better Auth). Also flags the 'GitHub origin' notes as
  historical (only Gitea origin exists).
- LAUNCH_CHECKLIST.md:
  added 'Last updated: 2026-06-25' header and refresh note in footer.

Verification:
- npx tsc --noEmit: pre-existing Stripe API version + test mock errors
  only (verified by stashing changes and re-running — same error set).
- No new errors introduced.
2026-06-25 20:13:19 -06:00

212 lines
8.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Environment Setup
This guide documents every environment variable the platform uses, what each one does, where to get it, and any gotchas.
Copy `.env.example``.env.local` and fill in the values.
---
## Public Variables
These are safe to commit and can be used in client bundles. Prefix with `NEXT_PUBLIC_`.
### `NEXT_PUBLIC_BASE_URL`
The base URL of your deployment. Used for OAuth redirects and webhook URLs.
- **Local:** `http://localhost:4000` (dev script binds to port 4000; override via `npm run dev -- -p <port>`)
- **Production:** `https://yourdomain.com`
### `NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY`
Stripe publishable key for Stripe.js in the client.
- **Where to get:** Stripe Dashboard → Developers → API Keys → Publishable key
- **Format:** `pk_live_...` or `pk_test_...`
### `NEXT_PUBLIC_SINGLE_BRAND`
Set to a brand slug to hide other brand routes in single-brand mode.
- **Example:** `tuxedo`
- **Default:** empty (multi-brand mode)
---
## Server-Only Variables
**Never prefix with `NEXT_PUBLIC_`**. These must only be accessed in Server Components, Server Actions, or API Routes.
### Database
#### `DATABASE_URL`
Full Postgres connection string. The app uses this exclusively (no Supabase, no JS client) for every server-side query — server actions and API routes import a shared `pg` `Pool` from `src/lib/db.ts` and call `SECURITY DEFINER` PL/pgSQL functions.
- **Where to get:** Neon Dashboard → Project → Connection Details → Connection string (pooled)
- **Example:** `postgresql://user:pass@ep-xxx.us-east-2.aws.neon.tech/routecommerce?sslmode=require`
- **Format:** `postgres://user:pass@host:port/dbname?sslmode=require`
#### `DATABASE_ADMIN_URL` (optional)
Same connection string but with elevated perms (typically the direct, non-pooled Neon connection). Only used by `scripts/migrate.js` for DDL — fall back to `DATABASE_URL` if unset.
### Stripe
#### `STRIPE_SECRET_KEY`
Stripe secret key for backend API calls (creating sessions, webhooks, etc.).
- **Where to get:** Stripe Dashboard → Developers → API Keys → Secret key
- **Format:** `sk_live_...` or `sk_test_...`
#### `STRIPE_WEBHOOK_SECRET`
Signing secret for Stripe webhook payloads.
- **Where to get:** Stripe Dashboard → Developers → Webhooks → select endpoint → Signing secret
- **Format:** `whsec_...`
#### Stripe Price IDs
Create these as recurring prices in your Stripe Dashboard first. Each maps to a plan tier or add-on.
| Variable | What it is | Stripe Dashboard |
|---|---|---|
| `STRIPE_PRICE_STARTER` | $49/mo Starter plan | Create a Product → Price at $49/mo |
| `STRIPE_PRICE_FARM` | $149/mo Farm plan | Create a Product → Price at $149/mo |
| `STRIPE_PRICE_ENTERPRISE` | $399/mo Enterprise plan | Create a Product → Price at $399/mo |
| `STRIPE_PRICE_HARVEST_REACH` | $79/mo Harvest Reach add-on | Create a Product → Price at $79/mo recurring |
| `STRIPE_PRICE_WHOLESALE_PORTAL` | $99/mo Wholesale Portal add-on | Create a Product → Price at $99/mo recurring |
| `STRIPE_PRICE_WATER_LOG` | $39/mo Water Log add-on | Create a Product → Price at $39/mo recurring |
| `STRIPE_PRICE_AI_TOOLS` | $59/mo AI Intelligence add-on | Create a Product → Price at $59/mo recurring |
| `STRIPE_PRICE_SQUARE_SYNC` | $39/mo Square Sync add-on | Create a Product → Price at $39/mo recurring |
| `STRIPE_PRICE_SMS_CAMPAIGNS` | $29/mo SMS Campaigns add-on | Create a Product → Price at $29/mo recurring |
**Note:** Annual pricing requires separate annual prices (e.g., $441/yr for Starter = $49 × 12 × 0.75). Use different price IDs and pass `annual=true` to checkout actions.
### Email (Resend)
#### `RESEND_API_KEY`
API key for sending transactional email via Resend.
- **Where to get:** [Resend.com](https://resend.com) → API Keys → Create API key
- **Format:** `re_...`
#### `RESEND_WEBHOOK_SECRET`
Signing secret for Resend webhook payloads.
- **Where to get:** Resend Dashboard → Webhooks → select endpoint → Signing secret
- **Format:** `whsec_...`
### AI
The platform supports five AI providers. Each brand can override per-provider keys in the admin AI Provider panel (`/admin/settings/ai`). Env vars here are fallbacks for when no per-brand key is configured.
#### `OPENAI_API_KEY`
OpenAI API key for AI features (AI Intelligence Pack add-on).
- **Where to get:** [OpenAI Platform](https://platform.openai.com) → API Keys → Create secret key
- **Format:** `sk-...`
#### `OPENAI_ORG_ID`
OpenAI organization ID (optional — for workspace-level usage tracking).
- **Where to get:** OpenAI Platform → Settings → Organization ID
- **Format:** `org-...`
#### `MINIMAX_API_KEY`
MiniMax (MiniMax) API key — OpenAI-compatible. **Pre-launch default provider.**
- **Where to get:** [MiniMax Platform](https://platform.minimax.io) → API Keys
- **Format:** `ey...` (JWT-style)
- **Used by:** `getAIClient()` falls back to this when no per-brand key is set. `ai-import.ts` Import Center also prefers this over `OPENAI_API_KEY` when both are set.
#### `MINIMAX_BASE_URL` (optional)
Override the MiniMax API base URL.
- **Default:** `https://api.minimax.io/v1` (global)
- **China:** `https://api.minimaxi.com/v1` (mainland endpoint, lower latency inside China)
#### Other providers
The admin panel also accepts per-brand keys for **Anthropic** (`sk-ant-...`), **Google Gemini** (`AIza...`), **xAI** (`xai-...`), and **custom** OpenAI-compatible endpoints. These can also be set globally via `ANTHROPIC_API_KEY`, `GOOGLE_API_KEY`, `XAI_API_KEY`.
### Square
#### `SQUARE_APP_SECRET`
Square app secret for Square API authentication.
- **Where to get:** Square Developer Dashboard → Your App → Credentials → Application Secret
- **Format:** `sq0...`
#### `SQUARE_ENVIRONMENT`
Controls whether to use Square sandbox or production.
- **Values:** `sandbox` (default for dev) or `production`
- **⚠️ Note:** Production credentials and sandbox credentials are separate. Make sure this matches which credentials you're using.
---
## Webhook Setup
### Stripe
**Dashboard:** Stripe Dashboard → Developers → Webhooks → Add endpoint
- **URL:** `https://yourdomain.com/api/stripe/webhook`
- **Events to listen for:**
- `checkout.session.completed`
- `customer.subscription.updated`
- `customer.subscription.deleted`
- `invoice.payment_succeeded`
- `invoice.payment_failed`
### Resend
**Dashboard:** Resend → Webhooks → Add webhook
- **URL:** `https://yourdomain.com/api/resend/webhook`
- **Events:** `email_delivered`, `email_bounced`, `email_complained`
---
## Local vs Production
| Variable | Local (`.env.local`) | Production (hosting dashboard) |
|---|---|---|
| `NEXT_PUBLIC_BASE_URL` | `http://localhost:4000` (or whatever port the dev server is on) | `https://yourdomain.com` |
| `STRIPE_SECRET_KEY` | `sk_test_...` | `sk_live_...` |
| `STRIPE_PUBLISHABLE_KEY` | `pk_test_...` | `pk_live_...` |
| `SQUARE_ENVIRONMENT` | `sandbox` | `production` |
---
## Gotchas
- **Wrong Stripe key type:** Using `sk_live_` in development or `sk_test_` in production will silently fail. Always match key type to environment.
- **Stripe price IDs drift:** If you create new prices in Stripe Dashboard but forget to update `.env.local`, billing will fail. Keep them in sync.
- **SQUARE_ENVIRONMENT mismatch:** Production Square credentials won't work with `sandbox`. Match it to your app secret type.
- **OPENAI_API_KEY for AI features:** AI features won't work without this. The AI Intelligence Pack add-on requires a valid OpenAI key.
---
## Authentication
### Production (HTTPS Required)
Neon Auth session cookies use the `__Secure-` prefix, which requires HTTPS. In production, the auth flow works as follows:
1. User submits credentials to `/api/auth/sign-in`
2. Neon Auth server sets session cookie with `secure: true`
3. Browser stores the cookie and sends it with subsequent requests
4. Middleware validates the session cookie
### Local Development (HTTP)
For local development over HTTP (e.g., `http://localhost:4000`), the platform provides a dev_session bypass:
1. **Via Login Page:** Visit `/login` - you'll see "Dev Mode — Quick Access" buttons for Platform Admin, Brand Admin, and Store Employee roles.
2. **Via Browser Console:** `document.cookie = 'dev_session=platform_admin; path=/; max-age=86400'`
3. **Via curl:** `curl -b "dev_session=platform_admin" http://localhost:4000/admin`
The dev_session cookie is automatically recognized by:
- The middleware (`src/proxy.ts`)
- The `getAdminUser()` function (`src/lib/admin-permissions.ts`)
**Note:** The dev_session bypass only works when `NODE_ENV !== "production"`. In production, only Neon Auth session cookies are accepted.
### HTTPS for Local Development
If you prefer to test with real auth over HTTPS locally, you can use a tool like [mkcert](https://github.com/FiloSottile/mkcert):
```bash
# Install mkcert
brew install mkcert
# Create local CA and install it
mkcert -install
# Generate certificate for localhost
mkcert localhost 127.0.0.1 ::1
# Update next.config.ts to use HTTPS
```
For most development work, the dev_session bypass is sufficient.