Environment Setup

Running the merch app locally

Prerequisites

  • Node.js 22 LTS
  • PostgreSQL running with DATABASE_URL set
  • AWS credentials for S3 access
  • Stripe test keys
  • SendGrid API key (for email)

Quick Start

# Terminal 1: Backend API (required)
cd apps/zooly-app && npm run dev     # Port 3004

# Terminal 2: Merch frontend
cd apps/zooly-merch && npm run dev   # Port 3008

Visit http://localhost:3008/teststore/lexnour to see the test campaign.

Seed Data

The merch app requires campaign/product data in the database. Run the seed script:

DATABASE_URL="postgresql://postgres:admin@localhost:5432/zoolylocal" \
  npx tsx packages/db/src/seed/merch-seed.ts

This creates 3 test campaigns:

CampaignURLProducts
Lexnour/teststore/lexnourT-Shirt ($69), Hoodie ($109)
LAS VEGAS ACES/Las%20Vegas%20Aces/las-vegas-acesTeam Tee ($45), Pro Hoodie ($85), Championship Plaque ($59)
Plaque/plaque/plaqueT-Shirt ($69), Hoodie ($109), Plaque ($39)

Environment Variables

Frontend (Vite SPA)

Set in apps/zooly-merch/.env or .env.local:

VariableDefaultPurpose
VITE_APP_URLhttp://localhost:3004Backend API base URL
VITE_MEDIA_BASE_URL${VITE_APP_URL}/api/mediaS3/CloudFront media origin

Backend (zooly-app)

Set in apps/zooly-app/.env.local:

VariablePurpose
ALLOWED_DOMAINS_CORSComma-separated allowed CORS origins (include http://localhost:3008)
STRIPE_SECRET_KEYStripe server key (sk_test_...)
STRIPE_PUBLISHABLE_KEYStripe publishable key (pk_test_...)
SENDGRID_API_KEYSendGrid email delivery
NEXT_PUBLIC_UI_HOSTMerch experience URL for email links (http://localhost:3008)
SUPPORT_EMAIL_ADDRESSESComma-separated; first used as "from" address
MERCH_MOCKUP_MODEcomposite (default) or ai-tryon
FAL_KEYFAL API key for AI generation
AWS_ACCESS_KEY_IDS3 access
AWS_SECRET_ACCESS_KEYS3 access
AWS_REGIONS3 region
AWS_BUCKET_NAMES3 bucket
DATABASE_URLPostgreSQL connection

Build

cd apps/zooly-merch && npm run build   # Outputs dist/

E2E Tests

cd apps/zooly-merch
npx playwright test                    # Run all tests
npx playwright test --ui               # Interactive UI mode
npx playwright show-report             # View HTML report

Deployment

apps/zooly-merch/vercel.json configures SPA mode:

{ "rewrites": [{ "source": "/(.*)", "destination": "/index.html" }] }

Production: merch.zooly.ai reverse proxies to app.zooly.ai/merch-app.

Manual Verification Walkthrough

  1. Campaign list: http://localhost:3008/ shows 3 campaign cards
  2. Landing: http://localhost:3008/teststore/lexnour shows hero, CTAs, legal footer
  3. Camera: Click "Take a Selfie" for camera capture flow
  4. Upload: Click "Upload Photo" for file upload flow
  5. Product select: Grid of products after photo review
  6. Size: 3-column size grid with auto-navigate
  7. Create (/create): Logarithmic progress + email capture + carousel during generation; then art selection (if multiple candidates) and cart view on the same URL; reselfie via overlay; BUY from art adds to cart on-page, BUY from cart continues to shipping
  8. Shipping: Address form with country dropdowns
  9. Payment: Stripe PaymentElement (test card: 4242 4242 4242 4242)
  10. Confirm: PAID badge, save/share, SHOP AGAIN

Troubleshooting

IssueFix
CORS errorsAdd http://localhost:3008 to ALLOWED_DOMAINS_CORS in zooly-app .env.local
Images not loadingS3 assets from seed don't exist locally; expected behavior
Stripe errorsEnsure STRIPE_SECRET_KEY and STRIPE_PUBLISHABLE_KEY are set
Emails not sendingCheck SENDGRID_API_KEY, SUPPORT_EMAIL_ADDRESSES, NEXT_PUBLIC_UI_HOST
Camera not workingHTTPS required for getUserMedia in most browsers; use localhost or ngrok