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 | <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>CashFlow Commander - Stripe for Physical Cash</title> | |
| <link rel="stylesheet" href="style.css"> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script> | |
| <script src="https://unpkg.com/feather-icons"></script> | |
| </head> | |
| <body class="bg-gray-50"> | |
| <custom-navbar></custom-navbar> | |
| <main class="container mx-auto px-4 py-8"> | |
| <section class="hero bg-gradient-to-r from-indigo-500 to-purple-600 text-white rounded-xl p-8 mb-8"> | |
| <div class="max-w-3xl mx-auto text-center"> | |
| <h1 class="text-4xl font-bold mb-4">Welcome to CashFlow Commander</h1> | |
| <p class="text-xl mb-6">The easiest way to manage physical cash deposits with digital convenience</p> | |
| <div class="flex gap-4 justify-center"> | |
| <a href="/dashboard.html" class="bg-white text-indigo-600 px-6 py-3 rounded-lg font-medium hover:bg-gray-100 transition">Merchant Dashboard</a> | |
| <a href="/merchants.html" class="bg-indigo-700 text-white px-6 py-3 rounded-lg font-medium hover:bg-indigo-800 transition">Public Registry</a> | |
| </div> | |
| </div> | |
| </section> | |
| <section class="features grid grid-cols-1 md:grid-cols-3 gap-6 mb-12"> | |
| <div class="feature-card bg-white p-6 rounded-lg shadow-md hover:shadow-lg transition"> | |
| <div class="text-indigo-500 mb-4"> | |
| <i data-feather="dollar-sign" class="w-8 h-8"></i> | |
| </div> | |
| <h3 class="text-xl font-semibold mb-2">Instant Cash Deposits</h3> | |
| <p class="text-gray-600">Convert physical cash to digital funds instantly with our seamless integration.</p> | |
| </div> | |
| <div class="feature-card bg-white p-6 rounded-lg shadow-md hover:shadow-lg transition"> | |
| <div class="text-indigo-500 mb-4"> | |
| <i data-feather="shield" class="w-8 h-8"></i> | |
| </div> | |
| <h3 class="text-xl font-semibold mb-2">Secure Transactions</h3> | |
| <p class="text-gray-600">Bank-level security with end-to-end encryption and fraud detection.</p> | |
| </div> | |
| <div class="feature-card bg-white p-6 rounded-lg shadow-md hover:shadow-lg transition"> | |
| <div class="text-indigo-500 mb-4"> | |
| <i data-feather="pie-chart" class="w-8 h-8"></i> | |
| </div> | |
| <h3 class="text-xl font-semibold mb-2">Real-time Analytics</h3> | |
| <p class="text-gray-600">Track all your deposits and earnings with beautiful, interactive dashboards.</p> | |
| </div> | |
| </section> | |
| <section class="cta bg-white rounded-xl shadow-lg overflow-hidden mb-12"> | |
| <div class="md:flex"> | |
| <div class="md:w-1/2 p-8 bg-indigo-50"> | |
| <h2 class="text-2xl font-bold mb-4">Ready to get started?</h2> | |
| <p class="text-gray-600 mb-6">Join hundreds of merchants already simplifying their cash management.</p> | |
| <a href="/signup.html" class="bg-indigo-600 text-white px-6 py-3 rounded-lg font-medium inline-block hover:bg-indigo-700 transition">Sign Up Now</a> | |
| </div> | |
| <div class="md:w-1/2 p-8"> | |
| <div class="flex items-center gap-4 mb-4"> | |
| <div class="bg-green-100 p-3 rounded-full"> | |
| <i data-feather="check" class="text-green-600 w-5 h-5"></i> | |
| </div> | |
| <div> | |
| <h3 class="font-medium">No hidden fees</h3> | |
| <p class="text-sm text-gray-500">Transparent pricing with no surprises</p> | |
| </div> | |
| </div> | |
| <div class="flex items-center gap-4 mb-4"> | |
| <div class="bg-blue-100 p-3 rounded-full"> | |
| <i data-feather="clock" class="text-blue-600 w-5 h-5"></i> | |
| </div> | |
| <div> | |
| <h3 class="font-medium">Quick setup</h3> | |
| <p class="text-sm text-gray-500">Get started in under 5 minutes</p> | |
| </div> | |
| </div> | |
| <div class="flex items-center gap-4"> | |
| <div class="bg-purple-100 p-3 rounded-full"> | |
| <i data-feather="headphones" class="text-purple-600 w-5 h-5"></i> | |
| </div> | |
| <div> | |
| <h3 class="font-medium">24/7 support</h3> | |
| <p class="text-sm text-gray-500">Dedicated team ready to help</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| </main> | |
| <custom-footer></custom-footer> | |
| <script src="components/navbar.js"></script> | |
| <script src="components/footer.js"></script> | |
| <script src="script.js"></script> | |
| <script> | |
| feather.replace(); | |
| </script> | |
| <script src="https://huggingface.co/deepsite/deepsite-badge.js"></script> | |
| </body> | |
| </html> |