A Next.js 16 marketing & ordering site for Square POS cafes. Zero database — launch in hours.
| Feature | Details |
|---|---|
| Marketing Site | Hero, menu preview, reviews, hours, map, Instagram, about, contact |
| Custom Checkout | Fully branded checkout with Square Web Payments SDK |
| Cart | Zustand store with localStorage persistence, modifier support, pickup/delivery toggle |
| Menu / Catalog | Managed in Square Dashboard, served via ISR (revalidate: 300) |
| Orders | Create, retrieve, search via Square Orders API; status tracking |
| Payments | Square Payments API (SDK) with verification token support; sandbox card 4111 1111 1111 1111 |
| SMS Notifications | Twilio via Square order webhooks (order.updated → confirmed → preparing → ready) |
| Content / CMS | Outstatic — Markdown stored in GitHub, no database |
| Admin Dashboard | JWT session auth protected; retrieve/modify/upsert Square catalog items |
| Demo Mode | Runs full app with mock Square responses — no credentials required |
| Customer Loyalty | Square Loyalty API integration (Phase 6) |
| ISR / SSG | Incremental static regeneration for marketing pages |
| Webhook Verification | HMAC-SHA256 verification of Square webhooks |
| Mobile Support | Responsive layout; mobile photo upload (Phase 13) |
| AI / Code Agents | MCP support for Square, Twilio, and Next.js devtools (see below) |
Next.js 16 + Tailwind v4
┌────────────────────────────────────────────────────┐
│ Marketing Site │
│ Hero, Menu, About, Contact, Social │
│ │
│ ┌─ Custom Checkout ───────────────────────────┐ │
│ │ Cart (Zustand) → Checkout Form → Square │ │
│ │ Payment via Square Web Payments SDK │ │
│ │ Order tracking via Square Orders API │ │
│ └─────────────────────────────────────────────┘ │
└───────────────────────┬────────────────────────────┘
│
┌───────────┴───────────┐
▼ ▼
┌──────────────────────┐ ┌──────────────────────┐
│ Square APIs │ │ Twilio SMS │
│ ─ Catalog (REST+ISR) │ │ Order notifications │
│ ─ Orders (SDK) │ │ via Square webhooks │
│ ─ Payments (SDK) │ └──────────────────────┘
│ ─ Webhooks │
│ ─ Loyalty (SDK) │ [Phase 6]
└──────────────────────┘
Status: Phase 3 — Renderer MVP complete. Generates visually distinct pages from hand-authored
SiteConfigJSON.
The project includes a generator architecture that produces unique, industry-specific websites using a stack of design tools: json-render (rendering), taste-engine (continuous tuners), soltana-ui (design language archetypes), and @lisse (shape geometry).
SiteConfig JSON → Soltana archetype + Taste Engine tuners → json-render spec → rendered page
A SiteConfig defines the industry, tone, design language (relief/finish/shape), tuner values (warmth, density, motion, contrast, narrative), and a json-render spec with gene components (hero, features, CTA variants).
npm run dev
# Open http://localhost:3000/preview?config=cafe.json
# Or http://localhost:3000/preview?config=saas-glass.jsonThe /preview route renders any SiteConfig from src/test-configs/. The toolbar shows current config info and quick-relief-switch buttons for visual comparison. A "Download HTML" button exports a self-contained standalone HTML file.
The generation loop is agent-guided — an AI agent (Claude) edits SiteConfig JSON between previews:
- Interpret the user's brief (industry, tone, style)
- Write a
SiteConfigJSON insrc/test-configs/ - Run the dev server and open the preview
- Iterate by editing the JSON and hot-reloading
- Verify uniqueness using
skills/theme-uniqueness/
See skills/agent/SKILL.md for the full agent workflow, tuner reference tables, industry→archetype guidance, and iteration troubleshooting.
| Phase | Status | Description |
|---|---|---|
| 0-3 | ✅ Complete | Foundation, gene library, tuners, renderer MVP |
| 4 | 🔲 Planned | Sequencing engine — automated page assembly from industry profiles |
| 5 | 🔲 Planned | Ticonderoga integration + snapshot history |
| 5b | 🔲 Planned | Interactive dev panel (tuner sliders, section swapping) |
| 6-9 | 🔲 Planned | Content generation, validation, gene expansion, migration |
Full details: docs/roadmap/generator-architecture/README.md
src/test-configs/
├── cafe.json # Warm, inviting — flat relief
├── saas-glass.json # Cool, modern — glassmorphic relief
└── portfolio-neumorphic.json # Minimal, elegant — neumorphic relief| Category | Variants | Location |
|---|---|---|
| Hero | Centered, Split, Minimal | src/genes/hero/ |
| Features | Grid, Alternating | src/genes/features/ |
| CTA | Simple, Split | src/genes/cta/ |
Each gene is a React component registered in @json-render/react's catalog, consuming soltana-ui archetype tokens and taste-engine tuners via context hooks.
The generator system has four distinct layers with clear ownership and handoff points:
| Layer | Owns | Handoff To |
|---|---|---|
| Human Input | Business profiles, catalogues, CMS content, dimension specs, archetype catalog, reference configs | AI pipeline reads these as source material |
| AI / Skills | Pipeline orchestration, archetype selection, content generation, workflow instructions | Writes to content/cms/site/pages.json; invokes Code |
| Code | Sequencer, renderer, genes, schemas, AI pipeline logic, API routes | Reads from Human Input; produces SiteConfig / PageBundle |
| Config | Env vars, dimension specs, bundle isolation, MCP servers, build settings | Enforced by Code at startup and build time |
Human Input → AI/Skills → Code → Rendered Output
↓ ↓ ↓
content/ skills/ src/ + lib/
+ scratch/ + .kilo/ + app/ + components/
-
Human Input provides the source material:
content/site-profile/— business datacontent/catalogue/— product datacontent/cms/site/pages.json— editable CMS content (human ↔ AI handoff)content/dimensions/specs/— theme design configscontent/archetypes/catalog.json— archetype knowledge basesrc/test-configs/— referenceSiteConfigJSONs
-
AI / Skills decides what to generate:
skills/— workflow instructions for the agent.kilo/— Kilo IDE/runtime configmcp.json— MCP server definitionsAGENTS.md— project-wide AI rules- Invokes Code via
npm runcommands and edits source files
-
Code performs deterministic generation and rendering:
src/generator/sequencer/— rule-based assembly from industry + tonelib/ai/multi-source-pipeline.ts— LLM-or-fallback pipelinesrc/renderer/—SiteConfig→ React DOMsrc/genes/— atomic visual blocksapp/+components/+lib/— legacy production app
-
Config controls the environment and build:
next.config.ts— theme bundle isolation (.next-a/b/c).env.local— secrets and feature flagscontent/dimensions/specs/+content/dimensions/bundles/— theme dimension configslib/env.ts— enforces required vars at startup (requireEnv)
- Node.js 20+
- npm 9+
- A Square Developer account (developer.squareup.com)
| Layer | Choice |
|---|---|
| Framework | Next.js 16 (App Router, Turbopack) |
| Language | TypeScript |
| Styling | Tailwind v4 |
| UI | Custom components (Button, Card, Dialog) |
| Icons | lucide-react |
| State (cart) | Zustand with localStorage persist |
| Data fetching | SWR (client-side), fetch + ISR (server) |
| CMS | Outstatic |
| SMS | Twilio |
| Payments | Square Web Payments SDK + Orders API |
| Testing | Vitest + React Testing Library + jsdom |
| Auth | JWT session tokens (crypto-agile, no server DB) |
| Site generation (new) | json-render, taste-engine, soltana-ui, @lisse, useinkjet, @design-guard |
This project applies AI-era security measures in three categories: what is implemented today, what is architecturally ready, and what is planned.
Dashboard sessions are signed with HMAC (HS256/HS384/HS512) selected at runtime via the JWT_ALGORITHM env var. Verification accepts all supported algorithms simultaneously, enabling rolling key rotation without invalidating active sessions. The signing path is isolated with the server-only directive so key material never reaches the client bundle.
- Code:
lib/auth/session.ts - Decision record:
docs/adr/011-crypto-agile-jwt-signing.md
Square webhooks are verified with constant-time HMAC-SHA256 using the SQUARE_WEBHOOK_SIGNATURE_KEY. Replay attacks are prevented by timestamp validation inside lib/webhooks/square.ts.
| Control | Implementation |
|---|---|
| npm script suppression | .npmrc sets ignore-scripts=true and min-release-age=7; CI uses npm ci --ignore-scripts |
| Vulnerability gate | npm audit --audit-level=high fails CI on high/critical findings |
| Pinned CI actions | All GitHub Actions pinned to commit SHAs; least-privilege permissions: contents: read |
| Dependency pinning | overrides in package.json for high-risk transitive deps (dompurify, cookie, markdown-it) |
| Dependabot | Weekly npm + GitHub Actions updates grouped by production/development |
| Direct-dependency rule | Any package imported in source must be listed in dependencies, preventing silent transitive drift |
| SAST | GitHub CodeQL (javascript-typescript) runs on every push/PR to main |
Policy details: docs/patterns/supply-chain-hardening.md
Misconfiguration throws a meaningful error at startup (requireEnv in lib/env.ts) rather than silently degrading to an insecure default.
The JWT signing abstraction in lib/auth/session.ts is structured to support ML-DSA (FIPS 204, the NIST post-quantum signature standard) once the jose library exposes it. The getAlgorithm() gate already lists SUPPORTED_ALGORITHMS as a single source of truth — adding ML-DSA there is the only step required for the verification and signing paths.
- Why not yet activated:
josev5.x does not yet expose ML-DSA;.tspspecs inspecs/square.tspmodel future hybrid schemes (ML-KEM + ECDH) for transport-layer key exchange. - Decision record:
docs/adr/011-crypto-agile-jwt-signing.md
The following are tracked in the project roadmap but not yet enforced in next.config.ts or proxy.ts:
- Content-Security-Policy — restrict script/style/image sources; nonce-based inline script allowance
- Referrer-Policy —
strict-origin-when-cross-originorno-referrer - Permissions-Policy — disable camera, microphone, geolocation on pages that don't need them
- Strict-Transport-Security — HSTS with
max-age=31536000; includeSubDomains - X-Frame-Options —
DENYto prevent clickjacking - Fingerprint mitigation — block third-party analytics/tracker scripts; avoid
navigatorproperty leaks
Roadmap reference: docs/roadmap/phase_7_security_hardening.md
The project ships with mcp.json for AI coding agents and MCP-aware tools:
{
"mcpServers": {
"next-devtools": {
"command": "npx",
"args": ["-y", "next-devtools-mcp@latest"]
},
"square": {
"command": "npx",
"args": ["-y", "@square/square-mcp-server"],
"env": {
"SQUARE_ACCESS_TOKEN": "${SQUARE_ACCESS_TOKEN}"
}
},
"twilio": {
"command": "npx",
"args": ["-y", "@twilio-alpha/mcp", "${TWILIO_ACCOUNT_SID}:${TWILIO_AUTH_TOKEN}"],
"env": {
"TWILIO_ACCOUNT_SID": "${TWILIO_ACCOUNT_SID}",
"TWILIO_AUTH_TOKEN": "${TWILIO_AUTH_TOKEN}"
}
}
}
}| Server | Purpose |
|---|---|
| next-devtools | Next.js debugging, route inspection, server logs |
| square | Direct Square API access (catalog, orders, payments, webhooks) via MCP |
| twilio | Send SMS / manage Twilio resources via MCP |
Skills are organized by the four architectural boundaries documented above. Each major AI-generated config artifact has a dedicated skill.
| Skill | Boundary | Purpose |
|---|---|---|
skills/website-generator/SKILL.md |
AI/Skills (orchestrator) | Top-level generation loop: interpret brief → run pipeline → preview → iterate |
skills/business-profile/SKILL.md |
Human Input → Code | Extract and validate BusinessProfile from raw business data |
skills/layout-selector/SKILL.md |
AI/Skills → Code | Select archetypes and variants per page, produces LayoutOutput |
skills/content-generator/SKILL.md |
AI/Skills → Code | Generate block data maps from layout + business profile, produces PageBundle |
skills/sequencer/SKILL.md |
Code | Industry profiles, section templates, pacing rules, produces SiteConfig |
skills/tuner-system/SKILL.md |
Code | Configure 5 Taste Engine tuners and Soltana archetype tokens |
skills/gene-designer/SKILL.md |
Code | Create new gene variants as json-render components |
| Skill | Boundary | Purpose |
|---|---|---|
skills/legacy-theme-dimensions/SKILL.md |
Config → Code | 9-dimension design system for old CMS theme injection |
skills/legacy-theme-uniqueness/SKILL.md |
Code (audit) | Hardcoded value audit for legacy dimension system |
skills/legacy-website-builder/SKILL.md |
Human Input → Code | End-to-end CMS site builder using old content/cms/ pipeline |
| Skill | Boundary | Purpose |
|---|---|---|
skills/ticonderoga/SKILL.md |
AI/Skills | Wraps standalone Ticonderoga Design Genome Lab CLI (Phase 5+) |
git clone <repo>
cd cafe-template
cp .env.local.example .env.local # then fill in your keys (see below)
npm install
npm run dev # → http://localhost:3000The app runs in demo mode by default (NEXT_PUBLIC_DEMO_MODE=true) — see Demo Mode.
npm run dev # Start dev server with Turbopack → http://localhost:3000
npm run build # Production build → .next/
npm run start # Serve production build
npm run lint # ESLint across all .ts/.tsx filesLinting uses eslint.config.mjs. Pre-commit hooks run lint-staged on staged .ts/.tsx files via simple-git-hooks.
Create a Square Developer application. You need both sandbox credentials (for development) and production credentials (for live deployments).
| Variable | Where to find it |
|---|---|
SQUARE_ACCESS_TOKEN |
Dev Dashboard → your app → Credentials → copy the Access Token |
SQUARE_LOCATION_ID |
Dev Dashboard → your app → Locations → your location ID |
SQUARE_ENVIRONMENT |
sandbox (dev) or production (live) |
NEXT_PUBLIC_SQUARE_APP_ID |
Dev Dashboard → Credentials → Application ID |
NEXT_PUBLIC_SQUARE_LOCATION_ID |
Same as SQUARE_LOCATION_ID (used client-side by Web Payments SDK) |
NEXT_PUBLIC_SQUARE_ORDERING_PROFILE_URL |
(Optional) Square-hosted ordering page as fallback |
In Square Developer Dashboard → your app → Webhooks, add a subscription to order.updated pointing to:
https://yourdomain.com/api/square/webhook
Copy the Signature Key into SQUARE_WEBHOOK_SIGNATURE_KEY for HMAC verification.
Add https://yourdomain.com to your Square app's Allowed CORS origins in the Developer Dashboard.
| Variable | Description |
|---|---|
SQUARE_LOYALTY_PROGRAM_ID |
From Square Dev Dashboard → Loyalty → Program ID |
Full env reference: docs/roadmap/phase_4_full_square_integration.md
Tests use Vitest + React Testing Library + jsdom. Located in __tests__/ mirroring the source tree.
npm run test # Run all tests once (CI mode)
npx vitest # Watch mode for development__tests__/
├── lib/
│ ├── utils.test.ts
│ ├── utils-format.test.ts
│ ├── webhooks/square.test.ts
│ ├── square/
│ │ ├── client.test.ts
│ │ ├── catalog.test.ts
│ │ ├── orders.test.ts
│ │ └── payments.test.ts
│ ├── store/
│ │ └── cart.test.ts
│ └── twilio/client.test.ts
├── hooks/
│ ├── useCart.test.tsx
│ ├── useMenu.test.tsx
│ └── useOrderStatus.test.tsx
├── components/
│ ├── order-button.test.tsx
│ ├── cart/
│ ├── checkout/
│ ├── menu/
│ └── order/
├── app/
│ └── api/square/webhook/
│ └── route.test.ts
└── proxy.test.ts
Config: vitest.config.mts — environment jsdom, @/ path aliases via vite-tsconfig-paths.
Set NEXT_PUBLIC_DEMO_MODE=true to run without any real API credentials. All Square calls return mock data:
- Menu — 8 demo items with Unsplash images and modifiers
- Orders — 3 demo orders in
COMPLETED,IN_PROGRESS,PROPOSEDstates; new orders generate randomly - Payments — always returns
COMPLETED - Location — hardcoded Melbourne cafe address
A Demo Mode badge appears in the UI when active. Check programmatically via lib/demo/config.ts (isDemoMode()).
Devin is an AI software engineer that can automate the release workflow from a clean main branch all the way through to a live Netlify deployment. This section documents the expected setup and the exact steps Devin should follow.
- Netlify account linked to your GitHub org
- Netlify Personal Access Token with
publish:updateandsites:readscopes - Netlify Site ID for the production site (found in Site settings → General → Site ID)
- GitHub repo pushed and Netlify site connected via Git (auto-deploy enabled on
main) - Production env vars configured in Netlify Dashboard → Site settings → Environment variables
Create a .env.netlify file (gitignored) or store these in your secret manager so Devin can access them:
NETLIFY_AUTH_TOKEN=your-netlify-personal-access-token
NETLIFY_SITE_ID=your-site-idAlso ensure production variables are set in the Netlify Dashboard:
| Variable | Value |
|---|---|
SQUARE_ENVIRONMENT |
production |
SQUARE_ACCESS_TOKEN |
Square production token |
NEXT_PUBLIC_SQUARE_APP_ID |
Square production Application ID |
NEXT_PUBLIC_SQUARE_LOCATION_ID |
Production location ID |
NEXT_PUBLIC_DEMO_MODE |
false |
NEXT_PUBLIC_SITE_URL |
https://yourdomain.com |
SQUARE_WEBHOOK_SIGNATURE_KEY |
Production webhook signature key |
SQUARE_LOYALTY_PROGRAM_ID |
Production loyalty program ID |
When asked to "release to production" or "publish to Netlify", Devin should run the following sequence:
-
Confirm branch state
git status git log --oneline -5
Ensure working tree is clean and HEAD is up to date with
origin/main. If there are uncommitted changes, ask the user before proceeding. -
Run the full CI signal locally
npm run lint:quiet npm run typecheck npm run test:fast
All three must pass before a release proceeds. If any fail, stop and report the failure.
-
Install Netlify CLI (if not already installed)
npm install -g netlify-cli
-
Build the production bundle locally
npm run build
Verify the build completes without errors. If the build fails, stop and report.
-
Deploy to Netlify (production)
netlify deploy --prod \ --dir=.next \ --site="$NETLIFY_SITE_ID" \ --auth="$NETLIFY_AUTH_TOKEN"
The
netlify.tomlat the repo root configures the Next.js plugin automatically. -
Verify the deploy
- Open the deploy URL returned by the CLI
- Confirm the site loads without console errors
- Spot-check the menu, cart, and checkout pages
- Verify the site is running against production Square credentials (not sandbox)
-
Run post-deploy sanity checks
# Smoke-test the catalog API curl -s https://yourdomain.com/api/square/catalog | head -c 200 # Smoke-test the order API (requires a valid order ID from the live site) # curl -s https://yourdomain.com/api/square/order/ORDER_ID
If either endpoint returns a 5xx, stop and alert the user.
-
Report the release Output the Netlify deploy URL, commit SHA, and a summary of what changed. If any step fails, report the exact error and the step that failed without retrying.
| Issue | Fix |
|---|---|
netlify deploy returns 401 |
Regenerate Netlify Personal Access Token and update NETLIFY_AUTH_TOKEN |
| Deploy succeeds but site shows old content | Check Netlify Deploy Preview vs Production; ensure --prod flag was used |
Build fails with SQUARE_ENVIRONMENT missing |
Set the variable in Netlify Dashboard → Environment variables, then trigger a new deploy |
| Webhook SMS not sending after deploy | Confirm SQUARE_WEBHOOK_SIGNATURE_KEY matches Square Dashboard; verify webhook URL points to production domain |
Railway runs Node.js apps with zero-config and built-in env management.
- Push your repo to GitHub
- Create a Railway account
- Have Square production credentials
# Install Railway CLI
npm install -g @railway/cli
# Login and link project
railway login
railway link
# Add environment variables
railway variables set SQUARE_ACCESS_TOKEN="your-prod-token"
railway variables set SQUARE_ENVIRONMENT=production
railway variables set SQUARE_LOCATION_ID="your-location-id"
railway variables set NEXT_PUBLIC_SQUARE_APP_ID="your-app-id"
railway variables set NEXT_PUBLIC_SQUARE_LOCATION_ID="your-location-id"
railway variables set NEXT_PUBLIC_SITE_URL="https://your-app.up.railway.app"
railway variables set NEXT_PUBLIC_DEMO_MODE=false
# Deploy
railway up- Railway Dashboard → New Project → Deploy from GitHub repo
- Select your repo — Railway detects Next.js automatically
- The build runs
npm run build; the start command isnpm start - Add all environment variables from
.env.localin the Variables tab - Railway exposes a public URL (e.g.
https://your-app.up.railway.app)
| Concern | Recommendation |
|---|---|
| Port | Next.js respects PORT env var automatically in production |
| Node version | Ensure Node 20+ (set via NODE_VERSION or .node-version) |
| Static files | Next.js output dir .next is served correctly by Railway's Node runtime |
| ISR | Works normally — pages regenerate on demand |
| Cron / Webhooks | Railway can expose the /api/square/webhook route publicly |
Create nixpacks.toml at repo root if you need explicit build control:
[phases.setup]
nixPkgs = ["nodejs-20_x", "npm-9_x"]
[phases.install]
cmds = ["npm ci --ignore-scripts"]
[phases.build]
cmds = ["npm run build"]
[start]
cmd = "npm start"GitHub Actions pipeline runs on push/PR to main:
audit—npm audit --audit-level=highlint— ESLintbuild— Production buildtest— Vitestcodeql— GitHub CodeQL security analysis
| Route | Description |
|---|---|
/ |
Home — hero, menu preview, reviews, hours, map, Instagram |
/menu |
Full dynamic menu (items, modifiers, categories from Square) |
/preview |
SiteConfig preview — render any config from src/test-configs/ via query param |
/cart |
Full cart page with item management |
/checkout |
Custom checkout with Square Web Payments form |
/checkout/confirmation |
Post-payment confirmation with order details |
/order/[orderId] |
Order status tracking with timeline |
/about |
Story and values |
/contact |
Contact details and map |
/outstatic |
Outstatic CMS admin (when configured) |
/dashboard |
Admin dashboard — catalog management (JWT auth required) |
| Route | Description |
|---|---|
GET /api/square/catalog |
Menu items, categories, images from Square |
POST /api/square/order |
Create order (pickup or delivery) |
GET /api/square/order/[orderId] |
Retrieve order by ID |
POST /api/square/payment |
Process payment via Square Payments API |
POST /api/square/webhook |
Square order webhook → Twilio SMS |
POST /api/twilio/sms |
Send SMS via Twilio |
| `GET | POST /api/outstatic/[...]` |
GET /api/preview |
List / serve SiteConfig test configs |
POST /api/preview |
Validate and echo a SiteConfig |
# ─── Square ──────────────────────────────────────────────────────────
SQUARE_ACCESS_TOKEN=
SQUARE_ENVIRONMENT=sandbox
SQUARE_LOCATION_ID=
NEXT_PUBLIC_SQUARE_APP_ID=
NEXT_PUBLIC_SQUARE_LOCATION_ID=
# Optional: Square hosted ordering profile (fallback URL)
NEXT_PUBLIC_SQUARE_ORDERING_PROFILE_URL=
# ─── Twilio ──────────────────────────────────────────────────────────
TWILIO_ACCOUNT_SID=
TWILIO_AUTH_TOKEN=
TWILIO_PHONE_NUMBER=
# ─── Square Webhooks ──────────────────────────────────────────────────
SQUARE_WEBHOOK_SIGNATURE_KEY=
# ─── Outstatic CMS ───────────────────────────────────────────────────
OUTSTATIC_API_KEY=
# ─── Dashboard Auth ──────────────────────────────────────────────────
DASHBOARD_PASSWORD=
# ─── RBAC via Square Team API ─────────────────────────────────────────
# Email used to look up Square team membership for role assignment.
# Optional - defaults to owner role if unset.
DASHBOARD_ADMIN_EMAIL=
# Comma-separated emails granted the developer role (bypasses Square lookup)
DASHBOARD_DEVELOPER_EMAILS=
# ─── MFA via Twilio SMS ───────────────────────────────────────────────
# Admin phone for MFA OTP delivery (Twilio-compatible E.164 format)
DASHBOARD_ADMIN_PHONE=
# OTP validity window in seconds (default: 300 = 5 minutes)
MFA_CODE_TTL=300
# ─── App ─────────────────────────────────────────────────────────────
NEXT_PUBLIC_SITE_URL=
# Logging verbosity: debug, info, warn, error (default: debug in dev, warn in production)
LOG_LEVEL=debug
# ─── Square Loyalty ──────────────────────────────────────────────────
# From Square Developer Dashboard > Loyalty > Program ID
SQUARE_LOYALTY_PROGRAM_ID=
# ─── Demo Mode ───────────────────────────────────────────────────────
# Set to "true" to use mock data (no real Square API calls)
NEXT_PUBLIC_DEMO_MODE=falseTo test the full ordering flow locally against Square's sandbox:
- Go to the Square Developer Dashboard
- Create a new application (or use an existing one)
- In Credentials, copy:
- Sandbox Access Token →
SQUARE_ACCESS_TOKEN - Application ID →
NEXT_PUBLIC_SQUARE_APP_ID
- Sandbox Access Token →
- In Locations, copy your location ID →
SQUARE_LOCATION_IDandNEXT_PUBLIC_SQUARE_LOCATION_ID
cp .env.local.example .env.localSet these values in .env.local:
# Square sandbox credentials
SQUARE_ACCESS_TOKEN=your-sandbox-access-token
SQUARE_ENVIRONMENT=sandbox
SQUARE_LOCATION_ID=your-location-id
NEXT_PUBLIC_SQUARE_APP_ID=your-app-id
NEXT_PUBLIC_SQUARE_LOCATION_ID=your-location-id
# Optional: override defaults
SQUARE_DEFAULT_CURRENCY=AUD
SQUARE_PLATFORM_FEE_RATE=0.05Note: When
NODE_ENV=developmentandSQUARE_ENVIRONMENTis not set, the app automatically uses sandbox mode.
- In the Developer Dashboard, open your sandbox location
- Go to Items and add at least one item with a price
- Optionally create categories and upload images
npm run dev
# → http://localhost:3000The menu page (/menu) will pull live catalog data from the Square sandbox. You can add items to cart and complete a full checkout using Square's test card:
4111 1111 1111 1111
Expiry: any future date
CVC: any 3 digits
ZIP: any 5 digits
- After placing an order, go to the Square Developer Dashboard → Sandbox → Orders
- You should see the order in
PROPOSEDstate - You can manually transition it through
IN_PROGRESS→COMPLETEDto test webhook SMS notifications
Square webhooks require a public HTTPS endpoint. To test webhooks locally:
- Use a tunnel like ngrok:
ngrok http 3000
- In the Developer Dashboard → Webhooks, add the ngrok URL:
https://your-ngrok-id.ngrok.io/api/square/webhook - Copy the Signature Key into
.env.local:SQUARE_WEBHOOK_SIGNATURE_KEY=your-signature-key
- Restart the dev server after changing
.env.local
If you log into the admin dashboard with an email listed in DASHBOARD_DEVELOPER_EMAILS, all Square API calls automatically use sandbox credentials regardless of SQUARE_ENVIRONMENT. This lets you test admin catalog operations safely.