How the system fits together
Infotech POS is one API, one web app and one database. Everything else (print agents, payment gateways, the license server) talks to the API. This page explains the parts and how they connect, so the other developer pages make sense.
The parts
| Part | Folder in the repo | Technology | What it does |
|---|---|---|---|
| API | spring-admin | Java 21, Spring Boot 3.5, JDBC, Flyway, OpenAPI | All business rules, authentication, WebSocket live updates, mail queue, billing jobs |
| Database | — | PostgreSQL 16 | All data, including translations and platform settings. Flyway creates and upgrades the schema on start |
| Web app | infotech-admin-web | React 18, TypeScript, Vite, Tailwind | The super-admin console, the merchant back office, the POS till and the kitchen display, in one app |
| Print agent | print-agent | Node.js 22 | Optional. Runs in a restaurant and prints kitchen tickets on network printers |
| Release tooling | release | Bash, Docker, Caddy | Builds the install packages buyers download |
| VPS deployment | deploy | Bash, Docker Compose, nginx, certbot | The author's own server setup and auto-deploy |
The API is a modular monolith: one Spring Boot application with feature packages, not separate services. The main packages are:
security/ login, tokens, recovery, mail queue, encryption
users/ shops/ subscriptions/ addons/ marketplace/
merchant/ shop sign-up, portal, settings
merchant/pos/ till: pricing, sales, payments, receipts
merchant/inventory/ stock ledger, on hand, valuation
merchant/hrm/ staff, roles, stores, warehouses
i18n/ reports/ operations/ media/ audit/ shared/
src/main/resources/db/migration/ versioned PostgreSQL schema
Controllers validate typed requests, services own transactions and rules, and a shared database helper binds parameters.
Who uses what
The API has four roles:
| Role | Uses |
|---|---|
SUPER_ADMIN | The platform console (/api/v1/admin/**): shops, plans, add-ons, payments, translations, license |
SUPER_ADMIN_VIEWER | The same console, read-only |
SHOP_OWNER | The merchant portal and till (/api/v1/shop/**), with every shop permission |
SHOP_STAFF | The merchant portal and till, limited by their shop role |
Super administrators cannot use the shop API, and shop users cannot use the admin API. The web app reads the signed-in user's context and builds its menu from it.
Authentication uses bearer tokens kept in the browser's memory (never in local storage). Tokens last 30 minutes by default and are refreshed; role and account state are checked in the database on every request.
Tenants and add-ons
A shop is a tenant (one company). Its stores are its branches, each with warehouses. Product areas are sold as add-ons switched on per shop: POS (on by default), Inventory, Purchasing, Accounting, ZATCA Phase 2 and the Kitchen display feature, among others. The API refuses a shop request with 403 ADDON_NOT_ENABLED when the shop lacks the add-on, and the web app hides those pages. Add-on rules live in spring-admin/docs/ADDONS.md.
Live updates
Open screens (drafts, tables, kitchen display, print stations, approvals) are told about changes through one WebSocket at /api/v1/live:
change in a transaction ──▶ PostgreSQL NOTIFY ──▶ COMMIT
every API instance (LISTEN) ──▶ connected browsers allowed to see it ──▶ screen reloads via the API
There is no Redis or message broker: PostgreSQL fans events out, so several API instances work without extra parts. A browser authenticates with its first message; print agents sign in with an agent key. Any reverse proxy in front of the API must pass the WebSocket upgrade (see Deploy the API and web app to a VPS).
Translations and branding
All UI text is stored in PostgreSQL and edited at runtime by super administrators. English, Bangla and Arabic (right-to-left) are seeded from spring-admin/src/main/resources/i18n/*.json; restarts add missing keys but never overwrite edits. The product name, URL and support email come from the platform settings, so the app can be rebranded without code changes.
Offline till
The production build of the web app includes a service worker that caches the app. The till keeps its catalog, context, open cart and offline sales in the browser's IndexedDB, and sends queued sales in order when the connection returns. The server still prices and records every sale.
Outside services
- License server: activation and daily check-in (default
https://license.infotech.center). A license problem locks the platform console and sign-up, never the shops' selling. - Payment gateways: Stripe, PayPal and Razorpay call webhooks under
/api/v1/billing/webhooks/…. - SMTP: sign-up codes, password resets and billing emails go through an encrypted mail outbox.
- ZATCA: Phase 2 onboarding and invoice reporting for Saudi shops.
Two ways to deploy
| Buyer release (Install) | Author's VPS (Deploy to a VPS) | |
|---|---|---|
| Containers | postgres, api, web (Caddy) | postgres, api |
| Web app served by | Caddy in the web container | Host nginx, static files in /var/www/… |
| Addresses | One origin: Caddy proxies /api/* and WebSockets to the API | Two origins: pos.example.com and api.example.com |
| HTTPS | Caddy, automatic | certbot with the nginx plugin |
Needs CORS_ORIGINS / VITE_API_URL | No | Yes |
| Updates | ./update.sh with a new package | git push to main (GitHub Actions) |
Related pages
Last updated 17 Oct 2026