QODY

QODY — AI-native QR restaurant ordering platform (SnowIT/ALAI)

QODY Architecture & Operations

QODY Architecture & Operations

Product: QODY — AI-native QR-based restaurant ordering and payment platform
Operator: SnowIT d.o.o. Sarajevo (powered by ALAI Holding AS)
Market: Bosnia and Herzegovina (BiH) primary, Norway secondary
Status: LIVE — Demo environment (rg-qody-demo), Clean production (rg-qody-prod)
Updated: 2026-06-26

What is QODY?

QODY is a sit / order & pay web application for hospitality venues (restaurants, cafes, bars). Guests scan a QR code at their table, browse a digital menu, place an order, and pay — all without installing an app or creating an account. Orders appear in real-time on the kitchen display system (KDS), and staff manage the order lifecycle through to delivery.

Core Flow

  1. Guest scans QR code on table → loads venue menu (anonymous, no login)
  2. Guest builds cart → submits order → pays via Stripe (test) or Monri (BiH payment gateway, when live)
  3. Order instantly appears on kitchen display (WebSocket + SSE realtime)
  4. Staff accepts → preps → marks ready → delivers
  5. Guest receives receipt, can track status live

Architecture Overview

Tech Stack

The 4 Apps + Landing

AppPurposeDNS (demo)Port (local)
qody-apiBackend: orders, payments, auth, realtime hubapi.qody.alai.no8080
qody-guestGuest ordering (anonymous)qody.alai.no5173
qody-adminVenue manager dashboard (menu CRUD, QR gen, reports, settings)admin.qody.alai.no5175
qody-staff-kitchenKitchen display system (KDS) — live order boardkuhinja.qody.alai.no5174
landingMarketing site (premium "Concept A" design)(future qody.ba)N/A

Deployment Environments

Demo/Stage (LIVE)

Production/Clean (DEPLOYED, no custom DNS yet)

Note: rg-qody-demo is the live demo CEO/Asmir use. Always deploy there for active testing. rg-qody-prod exists but nothing points to it yet.

Feature Catalog (Talas 0→6)

Built over 6 waves (2026-06-22 to 2026-06-24):

Phase 0 (Foundation)

Phase 1 (Vertical Slice)

Phase 2 (AI + Payments)

Talas 1 (Foundation++)

Talas 2A (Super-Admin Backend)

Talas 2B (Super-Admin UI)

Talas 3 (qLub Parity)

Talas 4 (Handover + Geofence)

Talas 5 (Gap-Fill)

Talas 6 (M-07 Operating Hours)

Additional Features (Security, Payments, I18n)

Database Schema

Migrations

V1–V19 in apps/api/src/main/resources/db/migration/

Key Tables

Demo Seed

V7__demo_seed_idempotent.sql creates "QODY Demo Bistro" with menu, 2 tables, QR tokens, staff accounts. Idempotent (ON CONFLICT UPSERT). Loads only when ENV=demo.

Payment Architecture

Stripe Connect (Live Test Mode)

Monri (Architecture Ready, Model B)

Recommendation: Start with Model B manual invoicing (certain, no dependency). Explore Monri split-payment API if they confirm support.

Regulatory Compliance (BiH): QODY is a software service provider, NOT a payment intermediary. No e-money/PI license needed under Model B. PCI-DSS scope = SAQ-A (Monri.js hosted checkout). BiH payment lawyer consult recommended (~500 EUR, confirm no PI license needed).

Security

Internationalization

Deployment Procedure

Build Images (Local Docker, amd64 Required for ACA)

cd apps/api
docker buildx build --platform linux/amd64 -t qodydemoacr.azurecr.io/qody-api:$(git rev-parse --short HEAD) .

cd ../guest
docker buildx build --platform linux/amd64 \
  --build-arg VITE_API_BASE_URL=https://api.qody.alai.no \
  --build-arg VITE_STRIPE_PUBLISHABLE_KEY=pk_test_... \
  -t qodydemoacr.azurecr.io/qody-guest:$(git rev-parse --short HEAD) .

# Repeat for admin and staff-kitchen with VITE_API_BASE_URL

Push to ACR

az acr login --name qodydemoacr
docker push qodydemoacr.azurecr.io/qody-api:$(git rev-parse --short HEAD)
docker push qodydemoacr.azurecr.io/qody-guest:$(git rev-parse --short HEAD)
# ... push admin and staff-kitchen

Update Container Apps

TAG=$(git rev-parse --short HEAD)

# API (creates new revision, verify before promoting)
az containerapp update \
  --name qody-api \
  --resource-group rg-qody-demo \
  --image qodydemoacr.azurecr.io/qody-api:$TAG

# Verify /health returns 200 + bypassRls=false
curl https://api.qody.alai.no/health

# Promote to 100% traffic (or use traffic split for canary)
az containerapp ingress traffic set \
  --name qody-api \
  --resource-group rg-qody-demo \
  --revision-weight qody-api--0000XXX=100

# Guest/Admin/Kitchen (single-revision mode, auto-100%)
az containerapp update --name qody-guest --resource-group rg-qody-demo \
  --image qodydemoacr.azurecr.io/qody-guest:$TAG
az containerapp update --name qody-admin --resource-group rg-qody-demo \
  --image qodydemoacr.azurecr.io/qody-admin:$TAG
az containerapp update --name qody-staff-kitchen --resource-group rg-qody-demo \
  --image qodydemoacr.azurecr.io/qody-staff-kitchen:$TAG

Post-Deploy Verification (ZAKON PI2)

curl https://api.qody.alai.no/health  # Must return {"status":"ok","db":{"rlsRoleCheck":{"bypassRls":false,"status":"PASS"}}}
curl -I https://qody.alai.no          # 200 + HTML
curl -I https://admin.qody.alai.no    # 200 + HTML
curl -I https://kuhinja.qody.alai.no  # 200 + HTML

# Real browser UAT: load guest menu, add item to cart, verify API calls work (not localhost)

⚠️ CRITICAL: MFE Build Args

All three MFEs (guest, admin, staff-kitchen) MUST receive VITE_API_BASE_URL at build time. Vite bakes env vars into the bundle. If missing, the MFE defaults to localhost:8080 → deploy succeeds, page loads, but API calls fail. Always verify build logs show:

===== QODY <MFE> BUILD: API base resolves to https://api.qody.alai.no =====

Recurring Lessons (Do NOT Re-Break)

  1. Build-arg or localhost bakes in: MFE Dockerfiles default VITE_API_BASE_URL=http://localhost:8080 if not passed. Deploy looks fine (200), but guest can't reach API. Always pass --build-arg VITE_API_BASE_URL=https://api.qody.alai.no.
  2. Webhook / system DB calls MUST bypass RLS: Any background job or webhook handler that queries across venues (e.g., SuperAdminService, billing export, webhook sync) must use the admin datasource (BYPASSRLS connection), NOT the per-request tenant-scoped datasource. Symptom: webhook succeeds but order not updated (RLS blocked the write).
  3. i18n single top-level key: All i18n keys must be unique at root level (e.g., guest.menu.addToCart, not nested menu: { addToCart: ... }). The i18n library flattens keys. Duplicate keys across modules = one overwrites the other.
  4. Idempotent seed migrations: Seed migrations (demo venue, tables, menu) must use ON CONFLICT ... DO UPDATE or existence guards that run AFTER dependent rows exist. Existence guards that check BEFORE the row is created silently skip INSERTs → "deploy OK" but data missing.
  5. Landing scroll-reveal fail-safe: Intersection Observer animations must have a fallback timeout (fade-in after 3s) or IntersectionObserver polyfill. Safari sometimes doesn't fire isIntersecting on initial load.
  6. Verify by live outcome, not green build: curl 200 + green CI ≠ "works". Always run real-browser UAT (Playwright or manual) that exercises the feature end-to-end (e.g., guest menu → add item → checkout → order reaches KDS).

Secrets & Credentials

Storage: Bitwarden items (alem@alai.no vault)

Azure Container Apps secrets (encrypted at rest, per-app): db-password, jwt-secret, qr-token-secret, stripe-secret-key, stripe-webhook-secret, monri-api-key (when live)

NEVER commit secrets. Always reference Bitwarden item names in documentation, not actual keys.

External Dependencies & Blocked Items

Waiting on Asmir/SnowIT

Pending BiH Compliance (Pre-Commercial Launch)

Operational Continuity Decisions (OCD Register)

IDDateOwnerDecisionRationale
OCD-QODY-0012026-06-22FlowForgeAzure ACA (not GCP Cloud Run)Tenant isolation: QODY needs own RG, not Bilko's GCP project
OCD-QODY-0022026-06-22AlemDNS: qody.alai.no (not qody-demo.alai.no)Short, brandable URL for demo/pilot
OCD-QODY-0032026-06-22FlowForgeUnleash deferred (not deployed)Feature flags optional for demo; reduces cost/complexity
OCD-QODY-0042026-06-22FlowForgeStripe TEST keys (no live payments)Demo only; payment flow tested with test cards
OCD-QODY-0052026-06-22FlowForgePostgreSQL RLS enforced (NOBYPASSRLS)Fail-closed security; multi-tenant isolation at DB layer
OCD-QODY-0062026-06-24CEOPricing: 0.5% platform fee (max 0.7%), 39-49 KM/monthPRD §2.3 model (NOT 5% — corrected from earlier prototype)
OCD-QODY-0072026-06-24FinvergeMonri Model B (per-venue merchant accounts)Bank-agnostic, no PI license needed, QODY bills separately
OCD-QODY-0082026-06-24CodeCraftMFE build-args NON-OPTIONALVITE_API_BASE_URL must be passed at docker build; missing = localhost bake-in
OCD-QODY-0092026-06-24CodeCraftSubscription billing: Stripe BAM (not EUR)39 KM Pro / 49 KM Enterprise per month (BiH market)
OCD-QODY-0102026-06-26SkillforgeBookStack canonical docshttps://docs.alai.no/books/qody — architecture/decisions/features source of truth

Support & Escalation

Owner: FlowForge (DevOps company, ALAI Holding AS)
Escalation: John (AI Director) → Alem (CEO, alem@alai.no, +47 404 74 251)

Runbooks: See repo RUNBOOK.md for deploy, rollback, health checks, secrets rotation, DB ops.

Incidents: Follow ~/system/runbooks/azure-aca-incident.md

Monitoring: Azure Monitor (6 alerts → alem@alai.no). Future: Sentinel ops-watchdog integration.

Support Model: See /tmp/qody-prd/support-model.md for full issue catalog, SLA tiers, resolution playbooks, and proactive monitoring design.

Account Aliases (for MC / Task Management)


Last Updated: 2026-06-26
Status: LIVE demo (rg-qody-demo), prod clean (rg-qody-prod), Talas 0–6 shipped, V19 migrations applied
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>