Environment Variables
Copy the local environment example and look up required, public, and server-only values.
Configure your local app with the values generated by ./coremvp env sync, then
add the provider settings for the capabilities you use.
Local environment
Polar billing settings
Follow Polar Billing for the sandbox sequence.
All settings below are server-only; do not add a NEXT_PUBLIC_ prefix.
| Setting | Default / purpose |
|---|---|
DEMO_BILLING_PROVIDER_SELECTOR | Optional test billing selector; disabled by default. |
PAYMENT_PROVIDER | stripe by default; selects the provider for new checkout. |
POLAR_ENVIRONMENT | sandbox by default; selects the Polar environment for access and management. |
POLAR_ORGANIZATION_ID | Organization owning the configured offers. |
POLAR_INDIVIDUAL_PRODUCT_ID, POLAR_TEAM_PRODUCT_ID | Fixed one-time offers for the source lifetime tiers. |
POLAR_STARTER_PRODUCT_ID_MONTHLY, POLAR_STARTER_PRODUCT_ID_YEARLY | Starter monthly/yearly fixed recurring products. |
POLAR_PRO_PRODUCT_ID_MONTHLY, POLAR_PRO_PRODUCT_ID_YEARLY | Pro monthly/yearly fixed recurring products. |
POLAR_BUSINESS_PRODUCT_ID_MONTHLY, POLAR_BUSINESS_PRODUCT_ID_YEARLY | Business monthly/yearly fixed recurring products. |
POLAR_ACCESS_TOKEN | Runtime organization token for checkout, product, order, subscription and customer session operations. |
POLAR_WEBHOOK_SECRET | Signing secret for this endpoint/listener. |
POLAR_TEST_EMAIL | Optional organization-member mailbox used by the local guest sandbox test. |
POLAR_TEST_DISCOUNT_CODE | Optional configured discount for the local hosted checkout proof. |
POLAR_TEST_BILLING_COUNTRY | Local proof address: United States by default, or United Kingdom. |
Enable the demo selector only with Stripe test mode, a matching test secret key,
and Polar sandbox. Live or invalid demo settings block new checkout. Existing
billing relationships retain their provider when PAYMENT_PROVIDER changes.
POLAR_ENVIRONMENT is independent of PAYMENT_PROVIDER; switching to production
excludes sandbox grants and targets without deleting history. The runtime token
needs checkouts:write, products:read, orders:read, subscriptions:read,
and customer_sessions:write; catalog and lifecycle administration uses
separate operator permissions.
The existing Stripe settings remain necessary for Stripe subscriptions, retained customer portal sessions and outstanding Stripe event processing. A Polar-only app can omit them.
Run ./coremvp env sync after local Supabase starts. The CLI writes the local
app URL, Supabase URL, publishable key, secret key, and database connection to
.env.local. Do not copy those values from another checkout or maintain them
by hand.
Local OAuth is optional and has a separate startup prerequisite. To test Google,
follow Configure OAuth Providers
before starting Supabase. env sync derives app connection values; it does not
configure OAuth providers or supply their client credentials.
Inspect redacted env
Check redacted env presence with:
./coremvp env listUse this command for troubleshooting because it reports set or missing without printing secret values.
Key presence does not prove connectivity. Start your app and follow the local Auth checks to check the running application.
Local env example
This block shows shared application settings and the default Stripe provider.
Keep the Supabase and database values written by env sync. For Polar, omit
the Stripe sections and add the Polar settings instead.
For Stripe, add your test keys and replace every prod_replace_me and
price_replace_me for the tiers you sell.
For Stripe, lifetime billing uses the four Individual/Team ID variables; subscriptions use
the nine Starter/Pro/Business ID variables. Configure the tiers you use from
src/config/pricing.config.ts.
# App origin
NEXT_PUBLIC_APP_URL=http://localhost:3000
# Supabase Auth and database
NEXT_PUBLIC_SUPABASE_URL=http://127.0.0.1:54321
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=...
SUPABASE_SECRET_KEY=...
DATABASE_URL=postgresql://postgres:<password>@127.0.0.1:54322/postgres
# Stripe settings shared by lifetime and subscription billing
NEXT_PUBLIC_STRIPE_MODE=test
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
# Lifetime billing IDs: Individual and Team (one-time payments)
# Optional: configure only the lifetime offers you sell.
# Individual
NEXT_PUBLIC_STRIPE_INDIVIDUAL_PRODUCT_ID=prod_replace_me
NEXT_PUBLIC_STRIPE_INDIVIDUAL_PRICE_ID=price_replace_me
# Team
NEXT_PUBLIC_STRIPE_TEAM_PRODUCT_ID=prod_replace_me
NEXT_PUBLIC_STRIPE_TEAM_PRICE_ID=price_replace_me
# Subscription billing IDs: Starter, Pro, and Business (monthly/yearly)
# Optional: configure only the subscription offers and cadences you sell.
# Starter
NEXT_PUBLIC_STRIPE_STARTER_PRODUCT_ID=prod_replace_me
NEXT_PUBLIC_STRIPE_STARTER_PRICE_ID_MONTHLY=price_replace_me
NEXT_PUBLIC_STRIPE_STARTER_PRICE_ID_YEARLY=price_replace_me
# Pro
NEXT_PUBLIC_STRIPE_PRO_PRODUCT_ID=prod_replace_me
NEXT_PUBLIC_STRIPE_PRO_PRICE_ID_MONTHLY=price_replace_me
NEXT_PUBLIC_STRIPE_PRO_PRICE_ID_YEARLY=price_replace_me
# Business
NEXT_PUBLIC_STRIPE_BUSINESS_PRODUCT_ID=prod_replace_me
NEXT_PUBLIC_STRIPE_BUSINESS_PRICE_ID_MONTHLY=price_replace_me
NEXT_PUBLIC_STRIPE_BUSINESS_PRICE_ID_YEARLY=price_replace_me
# Email delivery
EMAIL_DELIVERY_MODE=mock
# Optional: hosted email capture tests
# TEST_EMAIL_CAPTURE_TOKEN=... # only for test_capture
# Optional: real email delivery
# RESEND_API_KEY=... # only for real email
# RESEND_FROM_EMAIL=onboarding@example.com # only for real emailLocal variables
App and Supabase
| Variable | Required locally | Runtime | Description |
|---|---|---|---|
NEXT_PUBLIC_APP_URL | Yes | Browser/server | Local app origin, usually http://localhost:3000. |
NEXT_PUBLIC_SUPABASE_URL | Yes | Browser/server | Local Supabase API URL from bunx supabase status. |
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY | Yes | Browser/server | Local Supabase publishable key generated by ./coremvp env sync. |
SUPABASE_SECRET_KEY | Yes (generated) | Server only | Local Supabase secret key generated by ./coremvp env sync; never expose it to browser code. |
DATABASE_URL | Yes | Server only | Local Supabase Postgres connection string. |
Shared Stripe settings
These entries apply when using Stripe. Keep them for retained Stripe billing
even when PAYMENT_PROVIDER=polar selects new checkout.
| Variable | Required locally | Runtime | Description |
|---|---|---|---|
NEXT_PUBLIC_STRIPE_MODE | No; defaults to test | Browser/server | Selects test or live independently of NODE_ENV. Use test locally. |
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY | Billing only | Browser/server | Stripe test publishable key for checkout UI. |
STRIPE_SECRET_KEY | Billing only | Server only | Stripe test secret key for checkout and billing routes. |
STRIPE_WEBHOOK_SECRET | Webhooks only | Server only | Stripe CLI webhook secret from stripe listen --forward-to localhost:3000/api/webhooks/stripe. |
STRIPE_SECRET_KEY_LIVE | No | Server only | Optional live-mode key override; ignored when the selected mode is test. |
Lifetime billing IDs
| Variable | Required locally | Runtime | Description |
|---|---|---|---|
NEXT_PUBLIC_STRIPE_INDIVIDUAL_PRODUCT_ID | Optional | Browser/server | Individual Product ID from your Stripe test account. |
NEXT_PUBLIC_STRIPE_INDIVIDUAL_PRICE_ID | Offered tier/period | Browser/server | Individual one-time Price ID from your Stripe test account. |
NEXT_PUBLIC_STRIPE_TEAM_PRODUCT_ID | Optional | Browser/server | Team Product ID from your Stripe test account. |
NEXT_PUBLIC_STRIPE_TEAM_PRICE_ID | Offered tier/period | Browser/server | Team one-time Price ID from your Stripe test account. |
Subscription billing IDs
| Variable | Required locally | Runtime | Description |
|---|---|---|---|
NEXT_PUBLIC_STRIPE_STARTER_PRODUCT_ID | Optional | Browser/server | Starter Product ID from your Stripe test account. |
NEXT_PUBLIC_STRIPE_STARTER_PRICE_ID_MONTHLY | Offered tier/period | Browser/server | Starter monthly Price ID from your Stripe test account. |
NEXT_PUBLIC_STRIPE_STARTER_PRICE_ID_YEARLY | Offered tier/period | Browser/server | Starter yearly Price ID from your Stripe test account. |
NEXT_PUBLIC_STRIPE_PRO_PRODUCT_ID | Optional | Browser/server | Pro Product ID from your Stripe test account. |
NEXT_PUBLIC_STRIPE_PRO_PRICE_ID_MONTHLY | Offered tier/period | Browser/server | Pro monthly Price ID from your Stripe test account. |
NEXT_PUBLIC_STRIPE_PRO_PRICE_ID_YEARLY | Offered tier/period | Browser/server | Pro yearly Price ID from your Stripe test account. |
NEXT_PUBLIC_STRIPE_BUSINESS_PRODUCT_ID | Optional | Browser/server | Business Product ID from your Stripe test account. |
NEXT_PUBLIC_STRIPE_BUSINESS_PRICE_ID_MONTHLY | Offered tier/period | Browser/server | Business monthly Price ID from your Stripe test account. |
NEXT_PUBLIC_STRIPE_BUSINESS_PRICE_ID_YEARLY | Offered tier/period | Browser/server | Business yearly Price ID from your Stripe test account. |
Email delivery
A production runtime includes a local built Node server, Docker, VPS, and Vercel.
Set EMAIL_DELIVERY_MODE=real with RESEND_API_KEY and RESEND_FROM_EMAIL,
or EMAIL_DELIVERY_MODE=test_capture with TEST_EMAIL_CAPTURE_TOKEN. When a
send path validates delivery, an unset mode or mock fails. A successful build
or liveness check alone does not validate email delivery.
| Variable | Required locally | Runtime | Description |
|---|---|---|---|
EMAIL_DELIVERY_MODE | Production runtime | Server only | Local development defaults to mock. For a production runtime, choose real or test_capture explicitly. See Email Testing. |
TEST_EMAIL_CAPTURE_TOKEN | Test capture only | Server only | Secret token for hosted capture lookup. Not needed for the normal local path. |
RESEND_API_KEY | Real email only | Server only | Resend API key when EMAIL_DELIVERY_MODE=real. This can work locally, but it sends real email and is not part of first-run billing setup. |
RESEND_FROM_EMAIL | Real email only | Server only | Sender address when EMAIL_DELIVERY_MODE=real. |
Stripe catalog
Use your own Stripe test account's Product and Price IDs. IDs come only from environment settings. You can start the app without test IDs; checkout stays unavailable for any Price ID you have not configured. Both test and live mode accept a partial catalog. Configure a Price ID for each tier and billing period you offer; other offers remain unavailable. Product IDs are optional in either mode. Malformed supplied Price or Product IDs are rejected when the offer is resolved, and availability and checkout fail closed.
Shared pricing contains presentation only. Stripe offer lookup and legacy
Price authorization live in src/config/stripe-catalog.ts.
Keep displayed amounts and features in src/config/pricing.config.ts aligned
with Stripe. The server requires a secret key matching the selected mode.
Restart bun run dev after local catalog changes.
Production environment
Preview and Production use the same variable names with environment-specific values. See Hosted Environment Variables for the shared copyable block and hosted requirements.
Security rules
| Rule | Reason |
|---|---|
Never commit .env or .env.local | These files contain secrets. |
Never expose server keys as NEXT_PUBLIC_* | Browser bundles can read public variables. |
| Do not print setup links or secret values | Keeps test artifacts safe for sharing. |
What's next
Run Locally
Start the app with the configured local values.
Choose mock, hosted capture, or real email delivery.
Deployment
Copy the shared Preview and Production inputs.
Was this page helpful?