Configuration

ShipCommerce is configured entirely through environment variables. This page lists every variable the product reads, what it does, where the value comes from, and whether you can skip it.

The setup wizard (recommended)

You don't have to write .env.local by hand. From a local clone, run:

npm run setup

The wizard walks you through the required values, generates a CRON_SECRET for you if you leave it blank, and writes .env.local. Everything optional — Stripe, email, AI, analytics — can be skipped and added later by re-running the wizard or editing the file.

Prefer editing by hand? Copy .env.example to .env.local and fill in the values from the tables below. The file is annotated with the same links.

Required variables

The store will not run without these seven. Everything else on this page is optional.

# Supabase
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=
SUPABASE_SERVICE_ROLE_KEY=
SUPABASE_DB_URL=

# Site
NEXT_PUBLIC_SITE_NAME=My Store
NEXT_PUBLIC_SITE_URL=http://localhost:3000

# Cron (required in production)
CRON_SECRET=
VariableWhat it doesWhere to get it
NEXT_PUBLIC_SUPABASE_URLYour Supabase project endpoint. Used by browser and server.Supabase → Project Settings → API → Project URL
NEXT_PUBLIC_SUPABASE_ANON_KEYPublic key for browser requests. Safe to expose; Row Level Security applies.Supabase → Project Settings → API → anon public
SUPABASE_SERVICE_ROLE_KEYServer-only key for admin operations. Bypasses Row Level Security.Supabase → Project Settings → API → service_role
SUPABASE_DB_URLDirect Postgres connection. Used only by db:setup and migrate.Supabase → Project Settings → Database → Connection string → URI (transaction mode)
NEXT_PUBLIC_SITE_NAMEStore name shown in the UI, page titles, and emails.You choose it
NEXT_PUBLIC_SITE_URLPublic base URL. Used for links, redirects, and webhook targets.http://localhost:3000 locally, your domain in production
SITE_NOINDEXSet to true on a deployment that must stay out of search engines — a staging copy, a preview. Adds a noindex header and meta tag to every page and stops advertising the sitemaps. Leave it unset on the live store.Optional. Unset by default.
CRON_SECRETProtects the /api/cron/* endpoints. Required in production.Generate one: openssl rand -hex 32

Security: Never expose SUPABASE_SERVICE_ROLE_KEY or SUPABASE_DB_URL on the client side. Only variables prefixed with NEXT_PUBLIC_ are sent to the browser — that prefix is the whole distinction, so never add it to a secret.

Optional variables

Each group below is independent. Leave a group empty and the matching feature stays off — nothing else breaks.

Payments (Stripe)

Stripe is optional. Cash on delivery and bank transfer work out of the box, so you can launch a working store with Supabase alone and add card payments later.

VariableWhat it doesWhere to get it
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEYPublic key used to mount the card form in the browser.Stripe Dashboard → Developers → API keys
STRIPE_SECRET_KEYServer key used to create and capture payments.Stripe Dashboard → Developers → API keys
STRIPE_WEBHOOK_SECRETVerifies incoming webhooks. Without it, orders are never marked paid.Written for you by npm run stripe:setup, or printed by npm run stripe:listen locally

Email (Resend)

VariableWhat it doesWhere to get it
RESEND_API_KEYSends order confirmations and marketing email. Unset = no email is sent.resend.com/api-keys
RESEND_FROM_EMAILThe From address on outgoing mail.Defaults to onboarding@resend.dev; use an address on a domain you verified in Resend for production

Uploads (Vercel Blob)

VariableWhat it doesWhere to get it
BLOB_READ_WRITE_TOKENServes product images from Vercel Blob. Optional — when unset, uploads fall back to Supabase Storage, which works but is slower.Vercel Dashboard → Storage → Blob → Create token

AI content generation

VariableWhat it doesWhere to get it
ANTHROPIC_API_KEYPowers AI content generation in the admin panel.console.anthropic.com
OPENAI_API_KEYAlternative provider for AI content generation.platform.openai.com/api-keys

Analytics

VariableWhat it doesWhere to get it
NEXT_PUBLIC_GA_MEASUREMENT_IDEnables Google Analytics 4. Leave empty to disable.Google Analytics → Admin → Property settings
NEXT_PUBLIC_GTM_IDEnables Google Tag Manager. Leave empty to disable.tagmanager.google.com
NEXT_PUBLIC_PLAUSIBLE_DOMAINEnables Plausible, a privacy-friendly alternative. Leave empty to disable.plausible.io — the domain you registered there

Anti-bot (Cloudflare Turnstile)

VariableWhat it doesWhere to get it
NEXT_PUBLIC_TURNSTILE_SITE_KEYRenders the Turnstile challenge on login and signup forms.Cloudflare Dashboard → Turnstile
TURNSTILE_SECRET_KEYVerifies the challenge server-side. Set both keys or neither.Cloudflare Dashboard → Turnstile

Integrations

VariableWhat it doesWhere to get it
ORDER_WEBHOOK_URLPOSTs every new order as JSON to this URL — Zapier, Make, n8n, Slack, Discord, or your own backend. Unset, the built-in plugin is a silent no-op.The inbound webhook URL of whichever service you want to pipe orders into

Privacy notice: the order webhook payload contains the customer's email address. If you forward it to a third-party SaaS, you are exporting personal data under GDPR/CCPA — make sure your privacy policy and processor agreements cover it.

Admin

VariableWhat it doesWhere to get it
ADMIN_IP_WHITELISTComma-separated CIDR ranges allowed to reach /admin. Empty means any IP.Your own office or VPN ranges

Supabase setup

  1. Create a project

    Go to supabase.com/dashboard and click "New Project".

  2. Copy the API keys

    Project Settings → API:

    • Project URL → NEXT_PUBLIC_SUPABASE_URL
    • anon public → NEXT_PUBLIC_SUPABASE_ANON_KEY
    • service_role → SUPABASE_SERVICE_ROLE_KEY
  3. Copy the connection string

    Project Settings → Database → Connection string → URI (transaction mode) SUPABASE_DB_URL.

  4. Create the schema

    With those four values in .env.local, run:

    npm run db:setup

    This creates the tables, functions, Row Level Security policies, and the storage bucket. It is idempotent — re-running it is safe, and it is also how you apply new migrations after pulling an update.

Prefer running the SQL yourself? The migrations live in supabase/migrations/. Paste 00000000000000_baseline.sql into the Supabase SQL Editor and run it, then do the same with 00000000000001_storage.sql, in that order.

Stripe setup

Skip this section entirely if you are launching with cash on delivery and bank transfer. You can add Stripe at any time afterwards.

  1. Copy your API keys

    Go to the Stripe Dashboard → API keys. Stripe has two independent sets of keys, and the toggle in the dashboard decides which you are looking at:

    • Test mode — keys start with pk_test_ / sk_test_. No real money moves. Use these while you build.
    • Live mode — keys start with pk_live_ / sk_live_. Real charges. Use these only in production.

    Test and live keys are never interchangeable, and neither are their webhook secrets — switching modes means replacing all three Stripe variables.

  2. Register the webhook (deployed sites)

    Once your site is live at NEXT_PUBLIC_SITE_URL, run:

    npm run stripe:setup

    This creates the webhook endpoint with the correct events and appends the resulting whsec_... secret to .env.local. It is idempotent — it reuses an existing endpoint with the same URL rather than creating duplicates. Copy the secret into your hosting provider's environment variables too.

  3. Forward webhooks locally

    Stripe cannot reach localhost, so don't use stripe:setup during local development. Use the Stripe CLI instead:

    # Install the Stripe CLI (macOS)
    brew install stripe/stripe-cli/stripe
    
    stripe login
    npm run stripe:listen

    Leave it running in its own terminal. It prints a whsec_... secret — paste that into STRIPE_WEBHOOK_SECRET in .env.local and restart npm run dev.

You do not create products in Stripe. Your catalogue lives in the ShipCommerce admin panel and prices are sent to Stripe per payment, so there are no Stripe Price IDs to copy anywhere. See Products.

Resend setup

  1. Create an API key

    Sign up at resend.com and create a key → RESEND_API_KEY.

  2. Verify your domain (production)

    Add the DNS records Resend gives you, then set RESEND_FROM_EMAIL to an address on that domain. Until you do, the default onboarding@resend.dev sender works for testing only.

Verify your configuration

Start the site and walk through a purchase:

npm run dev

# Then check:
# - Sign up a new user
# - Add a product to the cart
# - Place an order with cash on delivery
# - If Stripe is configured, pay with test card 4242 4242 4242 4242
#   (any future expiry, any CVC)

Before going live, npm run post-deploy checks that the critical variables are present and warns you if your Stripe keys are still in test mode.