- Python 50.7%
- Svelte 24.2%
- TypeScript 21.2%
- CSS 3.5%
- JavaScript 0.3%
- Other 0.1%
|
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
|
||
|---|---|---|
| .woodpecker | ||
| backend | ||
| docker | ||
| docs | ||
| frontend | ||
| scanner | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| AndereAnbieter.md | ||
| docker-compose.truenas.yml | ||
| IdeasorToDo.md | ||
| README.md | ||
POS System - CashCardRegister
Ein modulares Point-of-Sale System mit pluggable Payment Providern.
Features
- Product Management: Produkte mit Barcode, Name, Preis verwalten
- Checkout Flow: Barcode-Scanner → Warenkorb → Zahlung
- Payment Providers: Pluggable Architecture (Cash, SumUp Solo, SumUp Tap, erweiterbar)
- Real-time Updates: WebSocket für Live-Warenkorb
- Admin UI: Produktverwaltung
Docs
- Andere Anbieter: Kosten & Empfehlungen für Kartenzahlungsterminals
- Ideen & ToDos: Ideen, ToDos & Anforderungen
- DB Schema & Payment Flow: Datenbankschema und Zahlungsfluss
Quick Start (Docker)
cp .env.example .env
# Edit .env with your credentials
docker compose -f docker/docker-compose.yml up --build
- Frontend: http://localhost:3000
- Backend API: http://localhost:8000
- API Docs: http://localhost:8000/docs
Development
Backend
cd backend
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
uvicorn app.main:app --reload
Frontend
cd frontend
npm install
npm run dev
Tests
cd backend
pytest tests/ -v
Tests in Docker (like CI)
docker compose -f docker/docker-compose.test.yml up --build --abort-on-container-exit
Architecture
Payment Provider Pattern
Adding a new payment provider requires only:
- Create
backend/app/payments/providers/newprovider.py - Register in
backend/app/payments/factory.py
No other code changes needed.
Demo Data
Demo products are seeded on startup only when APP_ENV=demo.
With APP_ENV=production, demo seeding is skipped.
To reset demo data in demo mode:
# Delete the database and restart
rm backend/pos.db
Production Security
When APP_ENV=production, ADMIN_TOKEN is required and must:
- Be at least 24 characters
- Include both letters and numbers
- Not use known weak defaults
In production, SCANNER_API_SECRET and SCANNER_PAIRING_SIGNING_SECRET follow the same minimum strength rules.
Scanner Pairing & Auth
The scanner now uses a per-scanner paired client flow:
- The POS web client requests a prefixed pairing secret from
GET /api/v1/scanner/pairing-secret. - The secret is shown as a barcode in the POS UI (CODE128 by default).
- The hardware scanner scans that barcode once and sends it to
POST /api/v1/scanner/scan. - If the scanned value has the pairing prefix, backend activates that web client for the scanner ID.
- Normal product scans are sent to the same
POST /api/v1/scanner/scanendpoint. - Each scanner ID can have max one active web client.
- Pairing secrets are stateless and valid for about 30 days. Expired secrets are rejected and must be re-scanned.
Environment variables:
SCANNER_API_SECRET: Shared scanner-to-backend secret (X-Scanner-Secret).SCANNER_DEFAULT_ID: Fallback scanner ID when no scanner ID is provided.SCANNER_ID: Scanner runtime ID used by the scanner client (X-Scanner-Id).SCANNER_PAIRING_PREFIX: Prefix that marks scanner pairing barcodes (default:POSLINK-).SCANNER_PAIRING_SIGNING_SECRET: HMAC key used by backend to sign/verify pairing secrets.SCANNER_PAIRING_MAX_AGE_DAYS: Pairing secret validity window (default:30).SCANNER_PAIRING_BARCODE_FORMAT: Barcode format sent to POS UI (default:CODE128).SCANNER_PAIRING_BARCODE_MAX_LENGTH: Maximum generated pairing barcode length.
Recent scanner stability notes:
- Frontend proxy excludes request bodies for
OPTIONSrequests to avoid backend 400 errors during preflight handling. - Backend CORS allows
X-Scanner-Idin preflight requests forPOST /api/v1/scanner/scan. - Token validation now uses
GET /api/v1/scanner/validate-token(withX-Scanner-Secret) instead of probing a product barcode that intentionally returns 404.
Timezone Configuration
Set TIMEZONE to a valid IANA timezone (for example Europe/Berlin).
If omitted, the backend defaults to Europe/Berlin.
SumUp Provider Modes
sumup_solo: Cloud API checkout for standalone SumUp Solo terminals.sumup_tap: App Switch URL flow for Tap to Pay on mobile.
Availability in the POS UI is driven by /api/v1/payments/providers/available:
cashis always available.sumup_solois available whenSUMUP_API_KEY,SUMUP_MERCHANT_CODE, andSUMUP_AFFILIATE_KEYare configured and the SumUp API is reachable.sumup_tapis available whenSUMUP_AFFILIATE_KEYis configured.fake_cardis always available and is useful for local card-flow tests.
Provider metadata (label, minimum_payment_cents, and optional minimum_topup_barcode) is sent by backend so the POS client can render payment labels and minimum hints without hardcoded mappings.
Minimum payment settings (all in cents):
PAYMENT_MINIMUM_CASH_CENTSPAYMENT_MINIMUM_SUMUP_SOLO_CENTSPAYMENT_MINIMUM_SUMUP_TAP_CENTSPAYMENT_MINIMUM_FAKE_CARD_CENTS(default100= 1,00 EUR)PAYMENT_MINIMUM_TOPUP_BARCODE(defaultVERSCHENKT-1C)
When a provider has a minimum amount and the cart is below it, the POS UI shows the missing difference as (+x.xx €) under the payment button. For providers that expose minimum_topup_barcode (for example fake_card), the client adds the matching 1-cent top-up product (Verschenkt) with the required quantity before submitting payment.
Backend always validates the minimum amount server-side and rejects transactions below the provider minimum.