# Payfrit Business Platform Main CFML/JS repository: API + Portal + KDS + HUD + Receipt. Git repo on Forgejo; local clone at `C:\dev\payfrit-biz`. ## Build & Deploy ```bash # Deploy: SCP to /tmp on dev, then sudo cp to biz.payfrit.com/ path ssh dev.payfrit.com # Canonical source of truth on dev: # /opt/lucee/tomcat/webapps/ROOT/biz.payfrit.com/ # No local Lucee — all development happens on the dev server ``` **Dev symlinks** on dev server: `/api`, `/portal`, `/config`, `/cron` are all symlinks to `biz.payfrit.com/`. **Always edit under `biz.payfrit.com/` — never the root symlinks directly.** ## Project Structure ``` payfrit-biz/ ├── Application.cfm # Root app config: sessions (30min), datasource, layout wrapper ├── index.cfm # Legacy entry point, mode-based routing ├── onrequestend.cfm # Footer/cleanup ├── register.cfm # Account registration ├── confirm_email.cfm # Email verification ├── confirm_mobile.cfm # Mobile verification ├── reset.cfm # Password reset ├── show_order.cfm # Order detail (redirects to /receipt/) ├── privacy.html # Privacy policy │ ├── api/ # REST API (~135 endpoints, 23 domains) │ ├── Application.cfm # Token auth gate (X-User-Token, X-Business-ID headers) │ ├── config/ │ │ ├── stripe.cfm # Stripe API keys │ │ └── environment.cfm # Dev vs prod settings │ ├── addresses/ # CRUD delivery addresses (add, delete, list, setDefault, states, types) │ ├── admin/ # Admin utilities │ │ ├── _scripts/ # One-off migration scripts (git-ignored) │ │ ├── quickTasks/ # Task type/category management │ │ └── scheduledTasks/ # Cron coordination │ ├── app/ # App info (about.cfm) │ ├── assignments/ # Employee assignments (delete, list, save) │ ├── auth/ # Authentication (login, loginOTP, sendOTP, verifyOTP, profile, avatar) │ ├── beacons/ # Beacon management (CRUD, lookup, reassign) │ ├── businesses/ # Business profiles (get, list, update, hours, hiring, brandColor) │ ├── chat/ # Order chat (send, get, markRead, close) │ ├── debug/ # Debug endpoints (localhost-protected) │ ├── dev/ # Dev tools (seedData, timeTravel) │ ├── import/ # Menu import (crimson_menu.cfm) │ ├── menu/ # Menu management (items, categories, menus, builder, photos) │ ├── orders/ # Order lifecycle (cart, submit, status, KDS list, history) │ ├── portal/ # Portal-specific (team, stats, settings, searchUser) │ ├── ratings/ # Customer ratings (create, list, submit) │ ├── servicepoints/ # Service point CRUD │ ├── setup/ # Business onboarding wizard (analyze, import, save) │ ├── stations/ # Kitchen stations (list) │ ├── stripe/ # Payments (createPaymentIntent, onboard, status, webhook) │ ├── tasks/ # Task system (accept, complete, create, list, types, categories) │ ├── users/ # User search │ └── workers/ # Worker system (account, onboarding, tier, ledger, earlyUnlock) │ ├── portal/ # Business management SPA │ ├── index.html # Main dashboard │ ├── login.html # Portal login │ ├── signup.html # Business registration │ ├── menu-builder.html # Interactive menu builder │ ├── setup-wizard.html # Onboarding wizard │ ├── station-assignment.html # Station management │ ├── quick-tasks.html # Quick task interface │ ├── portal.js # Core JS (~169KB, vanilla JS, no framework) │ └── portal.css # Portal styles (~22KB) │ ├── kds/ # Kitchen Display System │ ├── index.html # Full-screen kitchen display │ ├── admin.html # KDS admin │ ├── debug.html # Debug tools │ └── kds.js # KDS logic (~17KB, 5-second polling) │ ├── hud/ # Task HUD (Heads Up Display) │ ├── index.html # Full-screen task bars │ └── hud.js # HUD logic (~12KB, 60s target times) │ ├── receipt/ # Public receipt page (isolated app) │ ├── Application.cfm # Minimal config (no parent layout) │ └── index.cfm # Order receipt by UUID │ ├── admin/ # Admin tools │ ├── beacons.cfm # Beacon management │ ├── beacon_servicepoint.cfm # Beacon-SP mapping │ ├── email_users.cfm # Bulk email │ ├── god_mode.cfm # Super-admin panel │ └── servicepoints.cfm # Service point management │ ├── library/cfc/ # ColdFusion components │ ├── businessMaster.cfc # Business data abstraction │ └── twilio.cfc # SMS integration │ ├── includes/ # Shared includes │ ├── menu.cfm # Menu rendering │ └── track_visitors.cfm # Analytics │ ├── uploads/ # User-generated content │ ├── categories/ # Category images │ ├── headers/ # Business header images │ ├── items/ # Menu item photos │ ├── logos/ # Business logos │ └── users/ # User profile images │ ├── cron/ │ └── expireStaleChats.cfm # Cleanup old chats │ ├── css/ # Bootstrap 5.3.0 + custom ├── js/ # jQuery 1.11/2.1, Bootstrap JS ├── fonts/ # Glyphicons ├── images/ # Logo assets ├── cfpayment/ # Payment gateway library └── twilio/ # Twilio SMS ``` ## Key Files | File | Purpose | |------|---------| | `api/Application.cfm` | Token auth, public endpoint allowlist, perf tracking, datasource config | | `Application.cfm` | Root session management (30min timeout), layout wrapper, business/twilio objects | | `portal/portal.js` | Main portal SPA logic (169KB, vanilla JS, Fetch API) | | `kds/kds.js` | Kitchen display polling & order status management | | `hud/hud.js` | Task visualization with time tracking & beacon alerts | ## API Authentication - **Header**: `X-User-Token` (64-char hex, SHA-256 based) - **Business Context**: `X-Business-ID` header - **Token Table**: `UserTokens` (Token, UserID) - **Public Endpoints**: ~50+ whitelisted in `api/Application.cfm` (no token required) - **OTP Flow**: sendOTP → verifyOTP → token issued - **Magic OTP**: `123456` bypass for App Store review ## Environment Detection ``` Localhost (127.0.0.1): wwwrootprefix = /biz.payfrit.com/ image_display_prefix = http://127.0.0.1:8888/biz.payfrit.com/uploads/ uploads_dir = C:\lucee\tomcat\webapps\ROOT\biz.payfrit.com\uploads\ Production: wwwrootprefix = / image_display_prefix = https://biz.payfrit.com/uploads/ uploads_dir = /var/www/biz.payfrit.com/uploads/ ``` ## Tech Stack - **Backend**: CFML (Lucee), Application.cfm (not .cfc) - **Portal/KDS/HUD**: Vanilla JavaScript (no framework) - **Root pages**: jQuery 1.12.4 (CDN) + Bootstrap 5.3.0 (CDN) - **Payments**: Stripe (webhook at `/api/stripe/webhook.cfm`) - **SMS**: Twilio via `twilio.cfc` - **Database**: `payfrit` datasource ## Database - **Production**: `payfrit` on db.payfrit.com (still uses OLD prefixed column names) - **Development**: `payfrit_dev` on db.payfrit.com (migrated to clean names) **Dev DB uses clean, unprefixed names.** PKs are always `ID`. FKs reference their parent table (e.g., `BusinessID`, `UserID`). No table-name prefixes on columns. Key tables (dev schema): | Table | PK | Key Columns | |-------|----|-------------| | Businesses | `ID` | `Name`, `UserID`, `Phone`, `BrandColor`, `TaxRate`, `OrderTypes`, `StripeAccountID`, `ParentBusinessID` | | Users | `ID` | `FirstName`, `LastName`, `ContactNumber`, `EmailAddress`, `StripeCustomerId`, `UUID` | | Orders | `ID` | `UUID`, `UserID`, `BusinessID`, `StatusID`, `OrderTypeID`, `ServicePointID`, `PaymentStatus` | | Items | `ID` | `BusinessID`, `Name`, `Price`, `CategoryID`, `ParentItemID`, `IsActive`, `SortOrder`, `ImageExtension` | | Categories | `ID` | `BusinessID`, `MenuID`, `ParentCategoryID`, `Name`, `OrderTypes`, `SortOrder` | | OrderLineItems | `ID` | `ParentOrderLineItemID`, `OrderID`, `ItemID`, `StatusID`, `Price`, `Quantity`, `Remark` | | Tasks | `ID` | `BusinessID`, `TaskTypeID`, `Title`, `Details`, `ClaimedByUserID`, `OrderID` | | ServicePoints | `ID` | `BusinessID`, `Name`, `TypeID`, `Code`, `SortOrder`, `IsActive`, `BeaconID` | | Beacons | `ID` | `BusinessID`, `UUID`, `Name`, `IsActive` | | Menus | `ID` | `BusinessID`, `Name`, `IsActive` | ## Order Status Flow 1. **Cart** (StatusID 1) → 2. **Submitted** (2) → 3. **Preparing** (3) → 4. **Ready** (4) → 5. **Completed** (5) ## Receipt Page - **URL**: `/receipt/index.cfm?UUID={orderUuid}` - Separate `Application.cfm` — bypasses root layout - Public (no auth) — secured by v4 UUID unguessability - No internal data exposed (no platform fees, commissions, Stripe IDs) ## Performance Tracking API requests track: - `request._perf_start` (getTickCount) - `request._perf_queryCount` - `request._perf_queryTimeMs` ## Git Configuration **Ignored**: `config/claude.json`, `*.tmp`, `*.bak`, `api/admin/_scripts/` ## Common Tasks ### Add new API endpoint 1. Create `.cfm` file in appropriate `api/` subdirectory 2. Public endpoints: add to allowlist in `api/Application.cfm` 3. Authenticated endpoints work automatically via `X-User-Token` ### Add new portal page 1. Create HTML file in `portal/` 2. Add routing/navigation in `portal.js` ### KDS customization - Refresh interval: `config.refreshInterval` in `kds.js` - Station filtering via URL params