Spaces:
Running
Running
You are a senior staff engineer. Build a production-ready MVP called “FanMark” that merchants can use today to simulate “Stripe for physical cash” deposits. Create a monorepo with a typed backend API, a polished Next.js merchant dashboard, Supabase persistence with RLS, and seed data. Include everything needed to run locally in under 10 minutes: setup scripts, .env templates, DB schema/migrations, seeders, test scripts, OpenAPI docs, Postman collection, and a clickable demo script. ## Tech stack Monorepo: pnpm workspaces. Frontend: Next.js 14 (App Router, TypeScript), TailwindCSS, shadcn/ui, lucide-react, Zustand, React Hook Form, Zod, qrcode, jsQR reader (for camera QR), React Query. Backend: FastAPI (Python 3.11), Pydantic v2, Uvicorn, supabase-py, python-jose (JWT verification), httpx. Database: Supabase Postgres + Auth. Infra/Tooling: Docker Compose for local (db + studio optional), Makefile, pre-commit with Black/ruff/eslint/prettier, OpenAPI JSON + Swagger UI, Postman collection auto-export. Auth: Supabase JWT (merchant) + Service Role key (server only). ## Monorepo structure fanmark/ packages/ shared/ src/types.ts src/constants.ts src/incentiveRules.ts (pure functions for bonus logic) package.json apps/ api/ (FastAPI) app/main.py app/deps/auth.py app/routers/{deposits.py,kyc.py,merchant.py,registry.py,routing.py,health.py} app/models/{schemas.py} app/services/{routing.py,incentives.py,audit.py,kyc.py,earnings.py} app/db/supabase.py app/core/{config.py,logging.py} tests/ (pytest) openapi/ (generated) pyproject.toml .env.example Makefile web/ (Next.js) app/(dashboard pages: overview, routing, compliance, incentives, registry, settings) app/(public)/merchants/page.tsx (public registry) components/* (KPI cards, charts, QR components, forms) lib/supabaseClient.ts lib/api.ts (typed fetchers) styles/globals.css next.config.js package.json .env.local.example supabase/ schema.sql (tables, indices) seed.sql (sample merchants, deposits, users) policies.sql (RLS) scripts/ dev-setup.sh (bootstrap, env generation, supabase setup, seed) run-local.sh (concurrent API + web) generate-postman.ts (builds Postman collection from OpenAPI) README.md .tool-versions (python, node) .pre-commit-config.yaml ## Supabase schema (write exact SQL) Tables: merchants(id uuid pk, name text, address text, city text, state text, postal_code text, latitude numeric, longitude numeric, phone text, logo_url text, verified boolean default false, registry_public boolean default true, rail_preference text check in (‘auto’,’unit’,’moov’,’synctera’) default ‘auto’, created_at timestamptz default now(), owner_user_id uuid references auth.users(id)); merchant_settings(merchant_id uuid pk references merchants(id), incentives jsonb default ‘{}’, weekend_multiplier numeric default 1.0, first_deposit_bonus_cents int default 100, loyalty_multiplier numeric default 1.0); deposits(id uuid pk default gen_random_uuid(), merchant_id uuid references merchants(id), customer_id uuid, amount_cents int check (amount_cents>0), status text check in (‘initiated’,’processing’,’completed’,’failed’) default ‘initiated’, rail text check in (‘unit’,’moov’,’synctera’), estimated_fee_cents int default 0, route_meta jsonb default ‘{}’, incentives_applied jsonb default ‘[]’, created_at timestamptz default now(), completed_at timestamptz); kyc_events(id uuid pk default gen_random_uuid(), subject_type text check in (‘customer’,’merchant’), subject_id uuid, status text check in (‘pending’,’approved’,’rejected’), risk_level text check in (‘low’,’medium’,’high’), payload jsonb, created_at timestamptz default now()); audit_logs(id uuid pk default gen_random_uuid(), actor text, action text, data jsonb, created_at timestamptz default now()); earnings(merchant_id uuid references merchants(id), period_date date, total_cents int, incentives_cost_cents int, net_cents int, primary key(merchant_id, period_date)). Indices: deposits_idx on (merchant_id, status, created_at desc), merchants_geo_idx on (city,state). RLS: enable on merchants, deposits, merchant_settings, earnings. Policy: merchant owners (auth.uid() = owner_user_id) can select/update their resources; public can select merchants where registry_public = true (project only columns needed). Backend uses service role key to bypass RLS when necessary for server-side operations (never expose). ## Backend API (FastAPI) Implement these endpoints with full request/response models and OpenAPI docs. JWT middleware validates Supabase JWT (auth header Bearer). 1) POST /api/deposit/initiate body: {amount_cents:int, customer_id:uuid, merchant_id:uuid, rail_preference?:‘auto’|‘unit’|‘moov’|‘synctera’} steps: validate merchant ownership (if merchant calling) or allow public client token variant; compute route via services/routing.py choose rail with mock fee/latency table (unit:{fee_bps:80,latency_ms:700}, moov:{60,500}, synctera:{70,600}); write deposit row with status=‘processing’, estimated_fee_cents; call incentives engine to precompute applicable incentives (first deposit bonus if customer’s first with merchant, weekend multiplier based on UTC weekday, loyalty multiplier if customer has >=N prior deposits—use simple threshold N=3); write audit log; return {deposit_id, status, estimated_fee_cents, rail, route_meta, incentives_applied}. 2) GET /api/deposit/status/{id} returns status, rail, timestamps, incentives_applied; add a “simulate_complete” query flag for demo that finalizes deposit (status→completed, sets completed_at, updates earnings rollup). 3) POST /api/kyc/verify body: {subject_type, subject_fields{name,phone,id_type}, id_image_base64?} simulate approval with deterministic rule: if name contains “TEST-REJECT” → rejected with risk=high; else approved risk=low; write kyc_events and audit_logs. 4) GET /api/merchant/earnings/{merchant_id} returns rolling sums: total_cents, pending_settlement, incentives_cost_cents, net_cents, last_30d chart series grouped by day. 5) POST /api/merchant/onboard body: {name,address,city,state,postal_code,phone} creates merchant, sets owner_user_id=auth.uid(), default settings row, verified=false; returns merchant_id and onboarding checklist states. 6) GET /api/registry/public returns paginated list of merchants where registry_public=true with {name, address, geo, verified, 30d deposit_count, avg_ticket}. 7) PATCH /api/merchant/settings/{merchant_id} body: partial update for incentives (weekend_multiplier, first_deposit_bonus_cents, loyalty_multiplier, registry_public, rail_preference). 8) GET /api/health returns {ok:true}. Provide robust error handling with structured error format {error_code, message, details?}. Add CORS for http://localhost:3000. ## Incentives engine (packages/shared/src/incentiveRules.ts mirrored in backend services/incentives.py) Rules: FirstDepositBonus: if customer’s first ever deposit with merchant → add bonus_cents = merchant_settings.first_deposit_bonus_cents. WeekendMultiplier: if weekday in (Sat, Sun) → multiply bonus on incentives_applied amounts by weekend_multiplier. LoyaltyMultiplier: if prior successful deposits with merchant >= 3 → apply loyalty_multiplier to bonus_cents (floor to int). Return array [{rule, amount_cents, reason}] and aggregate incentives_cost_cents. Ensure deterministic and testable. ## Frontend (Next.js, Tailwind, shadcn/ui) Pages: /dashboard (Overview KPIs + recent deposits table + incentive events), /dashboard/routing (rail preferences, real-time audit of routing decisions), /dashboard/compliance (KYB checklist: business info form, file upload mocked, KYC events timeline), /dashboard/incentives (controls for first_deposit_bonus, weekend_multiplier, loyalty_multiplier with live preview of example customer), /dashboard/registry (toggle public listing, preview public card), /dashboard/settings (logo upload, address, phone). Components: KpiCard, DepositsTable (sortable, paginated), IncentiveRuleCard, RailSelector, AuditTimeline, QrPanel (generates a “Deposit Here” QR that encodes {merchant_id, amount?}, plus a camera-based QR reader to simulate customer scan flow). Public: /merchants (public registry; map-like list with search/filter; “Deposit here” CTA that opens a simple depositor flow modal). Depositor Modal Flow: enter amount, scan QR or pick merchant, click “Initiate Deposit” → calls POST /api/deposit/initiate → auto-poll status endpoint, with a “Mark complete (demo)” button that hits /status?id=…&simulate_complete=true. ## Security Hardening Validate all input with Zod (frontend) and Pydantic (backend). Enforce JWT presence for merchant dashboard calls; public registry is anonymous-read. Never send service role key to client. Add rate limiting (simple token bucket in memory for demo). Add CSRF protection on dashboard forms via same-site cookies. ## Developer Experience Provide: 1) README with one-command setup using scripts/dev-setup.sh (installs pnpm, Python venv, writes .env files, runs Supabase migrations, seeds data, starts API and Web). 2) .env templates with keys: SUPABASE_URL, SUPABASE_ANON_KEY, SUPABASE_SERVICE_ROLE_KEY, NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY, API_BASE_URL. 3) Dockerfile for api and docker-compose.yml (optional Postgres, but prefer external Supabase; still allow local dev with Docker). 4) OpenAPI auto-exposed at /docs and /openapi.json; generate Postman collection to /apps/api/openapi/fanmark.postman.json and link it from README. 5) Test suite: pytest for incentives and routing decisions; Playwright or Cypress smoke test for web (optional, include a minimal spec). 6) Sample merchant owner account and test JWT (doc how to sign in via Supabase Auth UI or provide a CLI that creates a merchant and returns a JWT for local dev). ## Seed data Create: 3 merchants (Unit Café, Moov Mart, Synctera Stop) with varied rail preferences, verified true/false mix; 50 deposits over last 14 days with varied status; 1 owner user per merchant. Generate realistic amounts (5–80 USD). Add KYC events mix. Mark one merchant public=false for toggle demo. ## Demo script (print in README) 1) Sign in as “owner@unitcafe.test” via Supabase Auth (password: Passw0rd!). 2) Go to Dashboard → see KPIs. 3) Toggle rail preference to “Moov”; trigger a new deposit from Depositor Modal; see routing decision and audit log. 4) Enable FirstDepositBonus=200 cents; simulate a first-time customer; see incentives applied on the deposit details and earnings reflect net_cents. 5) Toggle registry_public=true; open /merchants and see the card live. 6) Use “Mark complete (demo)” to finalize a deposit and watch charts update. ## Quality bar Ensure pixel-clean UI (shadcn components, responsive, keyboard accessible). Type everything. Lint passes. Tests pass. Commands: ./scripts/dev-setup.sh then ./scripts/run-local.sh. Output: a working local environment at http://localhost:3000 (web) and http://localhost:8000 (api). Provide final console instructions and any caveats. ---
c5761bb
verified