I
ICON Docs
v1.0.0
Need help?

ICON Documentation

ICON is a complete, production-grade e-commerce platform: one Flutter app for iOS & Android, a NestJS REST API, and a Next.js admin panel — all sharing a single typed contract. Thank you for your purchase!

Everything is included — no external system to buy or host. Unlike app-only templates that need a separate, third-party backend or CMS to function, ICON ships its own complete backend (REST API, database schema, migrations and seed data) and its own admin panel. Clone it, add your keys, and run — front to back, it's one self-contained product you fully own.

How the pieces fit together

  Flutter app  ───►  REST API  ───►  PostgreSQL
  (iOS/Android)      (/api/v1)        (your data)
                        ▲
  Next.js admin  ───────┘   ← runs your store from a browser

The app and the admin both talk to the same API under /api/v1. That API is the only contract between clients and the server, and it is documented as committed openapi.yaml / openapi.json and a Postman collection.

Tech stack

Flutter (Dart 3)Riverpodgo_router NestJSPrismaPostgreSQL Next.js (App Router)React TypeScript + ZodOpenAPI 3.1Docker

What's in the package

The download is a pnpm monorepo. Each part is independent but speaks the same API contract.

FolderWhat it is
app/The Flutter customer app (iOS & Android), built with Riverpod and go_router.
server/The NestJS REST API + Prisma schema, database migrations and seed data.
admin/The Next.js admin panel you run your store from.
packages/shared/Shared TypeScript types + Zod schemas — one source of truth for the API contract.
openapi.yaml / .jsonThe generated OpenAPI 3.1 spec (the API contract of record).
postman_collection.jsonReady-to-import Postman collection for trying the API.
docker-compose.ymlOne-command Postgres + API stack.
.env.exampleEvery environment variable, documented and ready to copy.
README.md · RELEASE.md · KNOWN_GAPS.mdSetup, store-submission checklist, and tracked follow-ups.

Requirements

You only need these to build and run the project locally. Docker is optional — it just makes the database one command.

ToolVersionUsed for
Node.js≥ 20API, admin, shared package
pnpm≥ 9Workspace package manager (npm i -g pnpm)
Flutter≥ 3.29 (Dart 3)The mobile app
PostgreSQL16Database (via Docker or Homebrew)
Redis7Cache / queues (dev infra)
XcodelatestiOS builds (macOS only)
JDK17Android builds

No Postgres installed? Run docker compose up --build to start the database and API together — migrations apply automatically on first boot.

Quick start

From an empty machine to a running store and app in a few minutes.

  1. Install dependencies

    From the project root, install all JavaScript workspaces (API, admin, shared) at once.

    pnpm install
  2. Start the database

    Use Docker, or your local Postgres.

    # Option A — Docker
    pnpm db:up
    
    # Option B — Homebrew Postgres (no Docker)
    brew services start postgresql@16
    createdb icon   # first time only
  3. Create your environment file

    Copy the example. The server reads its own server/.env.

    cp .env.example .env
    cp .env.example server/.env

    The defaults run out of the box. See Configuration for going live.

  4. Set up the database schema & demo data

    pnpm --filter @icon/server prisma:migrate
    pnpm --filter @icon/server seed

    The seed loads 4 brands, 4 categories, 12 products and 2 coupons, plus two logins (password Password123!): admin@iconcommerce.app (admin) and maya@example.com (shopper).

  5. Run the API

    pnpm dev:server   # http://localhost:3000/api/v1
                      # health check at /api/v1/health
  6. Run the admin panel

    pnpm dev:admin    # http://localhost:3001

    Sign in with admin@iconcommerce.app / Password123!.

  7. Run the app

    The app defaults to mock mode — a built-in in-memory backend, so it demos with zero server. To see live data from your API and admin edits, run in live mode:

    cd app
    flutter pub get
    
    # Live mode — talks to your API
    flutter run --dart-define=USE_MOCK=false \
      --dart-define=API_BASE_URL=http://localhost:3000/api/v1

Mock vs live. In mock mode a corner “DEMO DATA” ribbon appears and changes made in the admin will not show in the app — because the app never contacts the server. Run with USE_MOCK=false and an API_BASE_URL to connect to your backend.

Run the whole API stack in Docker

docker compose up --build   # Postgres + API on http://localhost:3000/api/v1

Make it yours — Rebranding

Turn ICON into your brand in minutes. Each step below gives the exact file and the exact line to change — no guesswork.

Your store name, currencies and home-screen layout are edited with no code at all in Admin → Settings. The five steps here change the app's built-in identity (colour, name, font, icon, server) for the version you publish to the stores.

  1. 1. Change the brand / accent colour

    One accent colour drives buttons, links, badges and highlights across the whole product. Change it in the app and in the admin so they match.

    Light & dark use different shades. The app picks a slightly lighter accent in dark mode for contrast, so you must change two values — otherwise the colour won't appear to change in the mode you're testing. (If you only edited accent600 and saw no change, you were almost certainly in dark mode.)

    App — file app/lib/design_system/tokens.dart. Replace the hex on both lines (keep the 0xFF prefix, then your 6-digit colour):

    // LIGHT mode accent — line 22
    static const Color accent600 = Color(0xFF059669);  // was 0xFF4F46E5
    
    // DARK mode accent — line 21 (use the same colour, or a slightly lighter shade)
    static const Color accent400 = Color(0xFF34D399);  // was 0xFF818CF8

    Then do a full restart of the app (stop it and run again) — colour constants don't always update on hot-reload.

    Admin — file admin/app/globals.css (line 3). Use your main colour:

    :root {
      --accent: #059669;   /* was #4f46e5 */

    Want one uniform accent everywhere? Set accent400 and accent600 to the same hex. For a pixel-perfect rebrand, tokens.dart also has the full scale (accent50accent900) used for hover/pressed shades — replace those too.

  2. 2. Change the app name

    This is the name shown under the icon on the phone's home screen.

    Android — file app/android/app/src/main/AndroidManifest.xml (line 3):

    android:label="Your Store"   <!-- was: android:label="icon" -->

    iOS — file app/ios/Runner/Info.plist, the value under the CFBundleDisplayName key:

    <key>CFBundleDisplayName</key>
    <string>Your Store</string>   <!-- was: Icon -->

    Optional — the title shown inside the app's top bar comes from your store name in Admin → Settings (no code). To change the built-in fallback, edit "appTitle" (line 3) in app/lib/l10n/app_en.arb (and the app_es.arb / app_ar.arb copies).

  3. 3. Change the app font

    One line restyles the typography everywhere. File app/lib/design_system/tokens.dart (line 213):

    static const String fontFamily = 'Poppins';   // was 'Manrope'

    If your font is not a built-in system font, bundle it: drop the .ttf files into app/assets/fonts/ and declare them in app/pubspec.yaml under fonts: (a commented template is already there, around line 114):

    flutter:
      fonts:
        - family: Poppins
          fonts:
            - asset: assets/fonts/Poppins-Regular.ttf
            - asset: assets/fonts/Poppins-Bold.ttf
              weight: 700

    Then run cd app && flutter pub get.

  4. 4. Replace the app logo & launcher icon

    Drop in two images, then regenerate every iOS/Android size automatically with flutter_launcher_icons (already configured).

    Replace these files with your artwork (keep the same names):

    • app/assets/branding/app_icon.png — your square app icon, 1024×1024.
    • app/assets/branding/splash_logo.png — your splash-screen logo.

    The generator config lives in app/pubspec.yaml — set the icon background to your brand colour:

    flutter_launcher_icons:
      image_path: assets/branding/app_icon.png
      background_color_ios: "#059669"   # your brand colour

    Regenerate the native icons and splash:

    cd app
    dart run flutter_launcher_icons
    dart run flutter_native_splash:create

    No artwork yet? ICON can generate placeholder branding for you: edit the colours/shape in app/tool/generate_icon.dart, run dart run tool/generate_icon.dart, then the two commands above.

  5. 5. Set the API base URL

    Point the published app at your live server (instead of localhost).

    Recommended — set it at build time (no code change), and turn off demo mode:

    flutter run --dart-define=USE_MOCK=false \
      --dart-define=API_BASE_URL=https://api.yourdomain.com/api/v1

    Or change the built-in default — file app/lib/core/env.dart (line 15):

    static const String apiBaseUrl = String.fromEnvironment(
      'API_BASE_URL',
      defaultValue: 'https://api.yourdomain.com/api/v1',   // was http://localhost:3000/api/v1
    );

    Remember to ship with USE_MOCK=false so the app uses your real backend — see Quick start.

That's the full rebrand. Colour, name, font, icon and server — five files, a few minutes, and the app is yours. For store-submission specifics (signing, bundle id) continue to Building for release.

Configuration — environment & keys

All server configuration lives in server/.env (copied from .env.example). Secrets stay on the server and are never committed or bundled into the app — the app fetches only its one public key (the Stripe publishable key) at runtime from GET /config.

Core settings

VariableWhat it does
DATABASE_URLPostgres connection string.
JWT_ACCESS_SECRET
JWT_REFRESH_SECRET
Sign-in tokens. Use strong, distinct values — generate with openssl rand -base64 48.
JWT_ACCESS_TTL / JWT_REFRESH_TTLToken lifetimes (e.g. 15m / 30d).
CORS_ORIGINSComma-separated list of origins allowed to call the API (your admin URL goes here).
PORT · API_PREFIX · NODE_ENVServer port, API path prefix, and environment.

Safe by default. Payments, social login and email all ship fully coded and run in a simulated mode until you add credentials — so the demo works end-to-end out of the box. Adding a key switches that feature to the real provider with no code changes.

Payments

ICON supports card payments (Stripe), PayPal, a Buy-Now-Pay-Later option, and Cash on Delivery. Merchants can turn each method on or off from the admin. Card payments via Stripe are fully integrated:

server/.env keyWhere to get itStatus
STRIPE_SECRET_KEYStripe dashboard → Developers → API keys (sk_test_…)Real when set
STRIPE_PUBLISHABLE_KEYStripe dashboard → API keys (pk_test_…)Served to the app
STRIPE_WEBHOOK_SECRETStripe → Webhooks → Signing secret (whsec_…)Confirms payment
  • Webhook: point Stripe at https://YOUR_API_HOST/api/v1/payments/webhook/stripe and subscribe to payment_intent.succeeded and payment_intent.payment_failed.
  • No key? Checkout returns a simulated payment so orders still flow end-to-end for demos. With a key, the app opens the real native Payment Sheet and the webhook marks the order paid.
  • PayPal & BNPL ship as documented redirect-flow stubs behind the same payment interface — drop in your provider's call to go live. Cash on Delivery needs no keys and works out of the box.

Social login

Google, Apple and Facebook sign-in are coded against POST /api/v1/auth/oauth, which verifies the provider token server-side and links (or creates) a passwordless account. Each button stays visible but returns “Sign in with X is not configured” until both the env value and the matching native client config are in place.

server/.env keyProvider console
GOOGLE_CLIENT_IDSGoogle Cloud Console → OAuth client IDs (comma-separate iOS/Android/Web)
APPLE_CLIENT_IDSApple Developer → your Bundle ID / Services ID
FACEBOOK_APP_ID · FACEBOOK_APP_SECRETMeta for Developers → app → Settings → Basic

The per-platform native steps (Info.plist / manifest snippets) are in RELEASE.md under Buyer setup.

Email — password reset & marketing

Transactional and marketing email run through one provider seam.

VariableWhat it does
RESEND_API_KEYEmpty → emails are logged, not sent (the reset & marketing flows still work end-to-end in dev). Add the key to send for real. Add to go live
EMAIL_FROMThe “from” name and address on outgoing mail.
APP_WEB_URLThe host of the web page the password-reset link points at (/reset-password?token=…).

The provider call lives in one method (EmailService) — swap Resend for SendGrid, SES, Postmark, etc. without touching the callers.

Admin panel guide

Run your whole store from the browser at http://localhost:3001. Sign in with a staff or admin account (shopper accounts are rejected). The admin opens on a Dashboard that explains, in one line each, what every section is for — and forms include plain-language helper text so a non-technical owner can use it confidently.

Dashboard

Store totals (products, orders, customers, revenue) and a guide to each section.

Products

Add & edit items — photos, description, status (draft / active / archived), and variants with their own SKU, price, compare-at price and stock.

Categories

Group products so shoppers can browse. New categories appear in the app automatically.

Brands

The makers/labels you tag products with.

Orders

Every order with a full detail view (items, address, totals). Advance the status — cancelling or refunding restocks the items and reverses the payment.

Customers

Who signed up or ordered, with lifetime spend; promote staff/admin roles.

Coupons

Percent or fixed-amount discount codes, optional minimum-subtotal, with an active toggle.

Marketing

Send a promotional email to customers who opted in.

Settings

Store name, supported currencies & exchange rates, and the app's home-screen layout.

Audit log

A history of changes made in the admin.

App features

The Flutter app is a complete storefront for iOS and Android.

Browse & discover

  • Home — a merchandisable home screen (hero banner, featured & deals rails) plus an automatic “New arrivals” rail, a “Shop by category” rail and an “Explore the collection” grid.
  • Badges — a NEW badge on recently-added products and a discount badge (e.g. -17%) on sale items.
  • Search, Shop all & category pages with sorting and an in-stock filter.
  • Product detail — image gallery, optional product video, variant picker, ratings & reviews, wishlist, and product comparison (up to four items).

Cart & checkout

  • Cart with quantity controls and coupon-code entry.
  • Shipping address form with country and state/region dropdowns.
  • Choose a payment method — card / Apple Pay / Google Pay (Stripe), PayPal, Buy-Now-Pay-Later, or Cash on Delivery (whichever the merchant has enabled).

Account & orders

  • Sign in / register, social login, and password reset.
  • Order history with tracking, and customer-initiated cancellation (restocks & refunds).
  • Address book, wishlist, and a notifications inbox.
  • Language & currency switchers, and privacy controls (export your data, delete your account).

Multi-currency, RTL & languages

Currencies

USD, EUR and GBP. Exchange rates are set in the admin, and shoppers switch currency in-app.

Languages

Fully localized in English, Spanish and Arabic.

Right-to-left

Complete RTL layout support for Arabic — the whole UI mirrors correctly.

Money is stored as integer minor units with an ISO currency code, so totals are always exact. To add a language, add a translation file (ARB) and regenerate localizations; to add a currency, extend the supported set and provide a rate in Settings.

Building for release

Follow RELEASE.md for the complete, top-to-bottom store-submission checklist. The essentials:

Pre-flight gates

pnpm --filter @icon/shared build && pnpm --filter @icon/shared test
pnpm --filter @icon/server lint && pnpm --filter @icon/server typecheck && pnpm --filter @icon/server test
pnpm --filter @icon/admin lint && pnpm --filter @icon/admin typecheck && pnpm --filter @icon/admin build
cd app && flutter analyze && flutter test

iOS — App Store

  1. After any clone/pull, run app/tool/ios_dev_setup.sh, then flutter pub get.
  2. In Xcode, set the Release signing team for the bundle id app.iconcommerce.icon.
  3. Point the app at your production API and do not ship USE_MOCK=true.
  4. Build and upload:
    flutter build ipa --release

Android — Google Play

  1. Configure Play App Signing; the applicationId is app.iconcommerce.icon.
  2. Build the app bundle:
    flutter build appbundle --release
  3. Complete the Play Console listing (data-safety form, content rating, screenshots) and roll out.

The app icon and splash are generated from app/tool/generate_icon.dart; replace the source artwork (or edit the colours) and re-run the launcher-icon and splash generators to rebrand.

Troubleshooting

The app shows a “DEMO DATA” ribbon and my admin edits don't appear

You're in mock mode. Run the app with --dart-define=USE_MOCK=false and --dart-define=API_BASE_URL=http://localhost:3000/api/v1 to use your live backend.

iOS build fails with “Undefined symbol: _Flutter…”

Stale build state after a clone/pull/merge. Run app/tool/ios_dev_setup.sh, then flutter pub get, and rebuild.

The admin can't reach the API / CORS errors in the browser console

Add your admin origin (e.g. http://localhost:3001) to CORS_ORIGINS in server/.env and restart the API.

Database connection errors on boot

Check that Postgres is running and DATABASE_URL is correct. With Homebrew, create the database once with createdb icon. With Docker, use pnpm db:up or docker compose up --build.

Payments aren't charging a real card

With no STRIPE_SECRET_KEY, checkout runs in simulated mode. Add your Stripe keys and register the webhook to take real payments.

Password-reset / marketing emails aren't arriving

With no RESEND_API_KEY, emails are logged to the server console instead of sent. Add the key to deliver real mail.

Signed out of the admin unexpectedly

Access tokens are short-lived and refresh automatically; if the refresh token has expired, just sign in again.

Changelog

v1.0.0 — Initial release

  • Flutter app (iOS & Android): catalog, search, product detail with video & reviews, wishlist, comparison, cart with coupons, multi-method checkout, orders with cancellation, account & notifications.
  • NestJS REST API with PostgreSQL, JWT auth with refresh-token rotation, and a documented OpenAPI 3.1 contract.
  • Next.js admin panel: products, categories, brands, orders (with detail), customers, coupons, marketing email, settings and an audit log.
  • Payments: Stripe (card / Apple Pay / Google Pay), PayPal & BNPL seams, and Cash on Delivery.
  • Social login (Google / Apple / Facebook) and email/password with password reset.
  • Multi-currency (USD/EUR/GBP), three languages (English / Spanish / Arabic) and full RTL.
  • Docker compose stack, seed data, and a store-submission checklist.

Support

If something isn't covered here, these files in the package go deeper:

  • README.md — project overview and developer commands.
  • RELEASE.md — full store-submission & buyer-setup checklist.
  • SECURITY.md · PRIVACY.md — the policies the code enforces.
  • KNOWN_GAPS.md — tracked, deferred follow-ups.
  • openapi.yaml + postman_collection.json — try the API directly.

For purchase-related questions, please reach out through your CodeCanyon item page (the Comments tab) or the author's profile. Include your environment (OS, Node, Flutter versions) and the exact command and error so we can help quickly.

Thank you for choosing ICON. We hope it ships your store fast. ⭐