Environment Variables
This page is the canonical list of environment variables used across the codebase (backend, frontend, admin frontend, mobile apps). It is kept in sync with actual usage in code.
Backend Environment Variables
Create backend/.env file:
# Server Configuration
PORT=8080
# Database Configuration
DATABASE_URL=postgres://username:password@localhost:5432/filesharing?sslmode=disable
# JWT Authentication (must be 32+ characters in production)
JWT_SECRET=your-super-secret-jwt-key-at-least-32-characters-long
# CORS (required in production; comma-separated origins, no *)
# Development: leave unset for localhost defaults
ALLOWED_ORIGINS=https://yourapp.vercel.app,https://admin.yourapp.vercel.app
# Payment Gateway (Razorpay)
RAZORPAY_KEY_ID=rzp_test_xxxxxxxxxxxxx
RAZORPAY_KEY_SECRET=your_razorpay_secret_here
RAZORPAY_WEBHOOK_SECRET=your_webhook_secret_here
RAZORPAY_ENV=test # 'test' or 'live'
# Email Service (Resend API - Recommended)
RESEND_API_KEY=re_xxxxxxxxxxxxx
FROM_EMAIL=noreply@yourdomain.com
FROM_NAME=Qprint
FRONTEND_URL=http://localhost:3000
# Email Service (SMTP - Alternative)
# SMTP_HOST=smtp.resend.com
# SMTP_PORT=587
# SMTP_USER=your_smtp_user
# SMTP_PASSWORD=your_smtp_password
# File Storage
# Option 1: Local Storage (Default)
USE_LOCAL_STORAGE=true
UPLOADS_DIR=uploads
# Option 2: AWS S3 (Production)
# AWS_S3_BUCKET=your-bucket-name
# AWS_REGION=us-east-1
# AWS_ACCESS_KEY_ID=your_access_key
# AWS_SECRET_ACCESS_KEY=your_secret_key
# Production: set so test mode is disabled
ENVIRONMENT=development # 'development' or 'production'
TEST_MODE=false # Do not set true in production
# Optional: Restrict admin panel to specific IPs (comma-separated). Leave unset to allow all IPs.
# ALLOWED_ADMIN_IPS=1.2.3.4,5.6.7.8
# Optional: HTTPS directly on server (cert and key paths)
# TLS_CERT_FILE=/path/to/fullchain.pem
# TLS_KEY_FILE=/path/to/privkey.pem
# Optional: Shop auto-close sweeper (heartbeat + web activity)
# SHOP_SWEEPER_INTERVAL_SECONDS=60 # How often to run (default: 60)
# SHOP_APP_HEARTBEAT_TIMEOUT_MINUTES=12 # Close if app heartbeat older than this (default: 12)
# SHOP_WEB_ACTIVITY_TIMEOUT_MINUTES=25 # Close if web activity older than this (default: 25)
# Optional: Stuck-printing sweeper (revert "printing" → "uploaded" when shop heartbeat is stale, e.g. app crashed)
# SHOP_PRINTING_STALE_MINUTES=10 # Revert printing orders if no heartbeat for this many minutes (default: 10)
# SHOP_STUCK_PRINTING_INTERVAL_SECONDS # How often to run stuck-printing sweep (default: same as SHOP_SWEEPER_INTERVAL_SECONDS)
# Optional: Price per page (in rupees). Print cost = pages × copies × rate; rate depends on color_mode.
# COST_PER_PAGE_BW=1 # Black & white (default 1)
# COST_PER_PAGE_COLOR=10 # Colour (default 10)
# Optional: Auth cookie — set to true to disable setting auth cookie on login.
# When true, only requests with Authorization: Bearer <token> are authenticated;
# opening the API URL (e.g. /shops) in the browser will not send credentials (401).
# SKIP_AUTH_COOKIE=true
# Optional: Bypass sign-up email OTP (dev/test only). When true, POST /register does not require signup_token.
# SKIP_SIGNUP_OTP=true
# Optional: Referral (Refer & Earn) — see docs/development/referral-and-earn.md
# REFERRAL_BASE_URL=https://qprint.co.in
# REFERRAL_BONUS_REFERRER=50
# REFERRAL_BONUS_REFEREE=25
# REFERRAL_INVITE_MAX_PER_DAY=20
# REFERRAL_QUALIFY_DAYS=30
# REFERRAL_CAP_PER_REFERRER=100
# Optional: App download / shop redirect URLs (e.g. for QR or links)
# SHOP_REDIRECT_WEBSITE_URL=https://qprint.co.in
# ANDROID_APP_URL=https://play.google.com/store/apps/details?id=...
# IOS_APP_URL=https://apps.apple.com/app/...
# Optional: Support email (shown in app/emails)
# SUPPORT_EMAIL=support@qprint.co.in
# Optional: Behind reverse proxy (e.g. Render) — set so rate-limit uses X-Forwarded-For
# TRUST_PROXY_HEADERS=true
# Optional: Push notifications (FCM) — JSON path or inline JSON
# FIREBASE_SERVICE_ACCOUNT_JSON=/path/to/service-account.json
# GOOGLE_APPLICATION_CREDENTIALS=/path/to/credentials.json
# Optional: Debug/Logging
# LOG_LEVEL=debug # Set to 'debug' for verbose logging
# DEBUG=true # Alternative to LOG_LEVEL
# PAYMENT_LINK_EXPIRY_MINUTES=30 # Payment link expiry (default: 30)
Frontend Environment Variables
Create frontend/.env.local (optional, defaults to localhost:8080):
NEXT_PUBLIC_API_URL=http://localhost:8080
Note: Razorpay key is fetched from the backend during payment order creation, so no frontend Razorpay key is needed.
Admin Frontend Environment Variables
Create admin_frontend/.env.local:
NEXT_PUBLIC_API_URL=http://localhost:8080
# Optional: Main site URL for "Preview download page" link on App Download Links page
# NEXT_PUBLIC_MAIN_APP_URL=https://yourapp.vercel.app
Mobile Apps Configuration
Environment values for mobile apps are set at build time via --dart-define (not runtime .env).
Customer App (customer_app/)
| Dart define | Description | Example |
|---|---|---|
BASE_URL | Backend API base URL | https://qprint-72wr.onrender.com |
MAPS_API_KEY | Google Maps API key (optional; can use AndroidManifest/iOS plist) | AIza... |
CERT_PINS | Comma-separated SHA-256 cert pins for pinning (optional) | - |
- Release builds: If
BASE_URLis empty or localhost, the app uses the hardcodedproductionBaseUrlinlib/config/app_config.dart. Override with--dart-define=BASE_URL=https://...for a different backend. - Debug:
BASE_URLfrom dart-define or production default. - Example:
flutter run --dart-define=BASE_URL=https://your-api.com - Google Maps can also be set in
android/app/src/main/AndroidManifest.xmland iOS plist.
Shopkeeper App (shopkeeper_app/)
| Dart define | Description |
|---|---|
BASE_URL | Backend API base URL (same as customer app). |
Example: flutter run --dart-define=BASE_URL=https://your-api.com
Environment Variable Reference
Required Variables
| Variable | Description | Example / Notes |
|---|---|---|
DATABASE_URL | PostgreSQL connection string | postgres://user:pass@localhost:5432/db |
JWT_SECRET | Secret key for JWT tokens; must be 32+ characters | openssl rand -base64 32 |
PORT | Backend server port | 8080 (local), 10000 (Render) |
ALLOWED_ORIGINS | CORS allowed origins (production); comma-separated | https://yourapp.vercel.app (no * in production) |
Optional Variables
| Variable | Description | Default |
|---|---|---|
ENVIRONMENT | development or production; production disables test mode | - |
TEST_MODE | Bypass payment validation (do not use in production) | false |
RAZORPAY_KEY_ID | Razorpay API key ID | - |
RAZORPAY_KEY_SECRET | Razorpay API secret | - |
RAZORPAY_WEBHOOK_SECRET | Required for payment webhooks | - |
RESEND_API_KEY | Resend API key for emails | - |
FRONTEND_URL | Base URL for password-reset links | - |
USE_LOCAL_STORAGE | Use local file storage | true |
AWS_S3_BUCKET, AWS_REGION, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY | S3 file storage | - |
TLS_CERT_FILE, TLS_KEY_FILE | Paths to TLS cert and key for HTTPS on server | - |
SHOP_SWEEPER_INTERVAL_SECONDS | Interval (seconds) for shop auto-close sweeper | 60 |
SHOP_APP_HEARTBEAT_TIMEOUT_MINUTES | Auto-close shop if app heartbeat older than this | 12 |
SHOP_WEB_ACTIVITY_TIMEOUT_MINUTES | Auto-close shop if web activity older than this | 25 |
COST_PER_PAGE_BW | Price per page for black & white (rupees). Invalid or negative → 1. | 1 |
COST_PER_PAGE_COLOR | Price per page for colour (rupees). Invalid or negative → 10. | 10 |
SKIP_AUTH_COOKIE | If true, do not set auth cookie on login; only Bearer token auth is accepted (prevents direct API URL access in browser) | - |
SKIP_SIGNUP_OTP | If true, POST /register does not require signup_token (bypass email verification). Use only in dev/test. | - |
RAZORPAY_ENV | Razorpay mode: test or live (must match key type) | - |
PAYMENT_LINK_EXPIRY_MINUTES | Payment link/order expiry in minutes | 30 |
REFERRAL_BASE_URL | Base URL for referral links (e.g. https://qprint.co.in) | - |
REFERRAL_BONUS_REFERRER | Bonus (₹) to referrer when referee qualifies | 50 |
REFERRAL_BONUS_REFEREE | Welcome bonus (₹) to referee | 25 |
REFERRAL_INVITE_MAX_PER_DAY | Max invite emails per referrer per 24h | 20 |
REFERRAL_QUALIFY_DAYS | Days after signup within which referee must qualify (omit = no limit) | - |
REFERRAL_CAP_PER_REFERRER | Max credited referrals per referrer lifetime (omit = no cap) | - |
SHOP_REDIRECT_WEBSITE_URL | Website URL for shop redirect / QR | - |
ANDROID_APP_URL | Play Store URL for app download links | - |
IOS_APP_URL | App Store URL for app download links | - |
SUPPORT_EMAIL | Support email shown in app or emails | - |
TRUST_PROXY_HEADERS | If true, use X-Forwarded-For for client IP (e.g. behind Render) | - |
FIREBASE_SERVICE_ACCOUNT_JSON | Path or JSON string for FCM (push notifications) | - |
GOOGLE_APPLICATION_CREDENTIALS | Fallback path for FCM credentials | - |
LOG_LEVEL | Set to debug for verbose logging | - |
DEBUG | Set to true for debug logging (alternative to LOG_LEVEL) | - |
ALLOWED_ADMIN_IPS | Comma-separated IPs allowed for admin panel (optional; omit to allow all) | - |
Security Notes
- Never commit
.envfiles to version control - JWT_SECRET must be at least 32 characters in production (server will not start otherwise)
- ALLOWED_ORIGINS must be set in production to your real frontend URL(s); no default
* - Set ENVIRONMENT=production on production hosts (e.g. Render)
- Keep API keys and secrets secure; use different values for development and production
- Consider using secret management services in production