No description
  • Python 50.7%
  • Svelte 24.2%
  • TypeScript 21.2%
  • CSS 3.5%
  • JavaScript 0.3%
  • Other 0.1%
Find a file
Oliver Großkloß e24f0b4bca
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Harden scanner pairing with persistent sessions and integration tests
2026-04-02 01:14:38 +02:00
.woodpecker olli init 2026-02-14 13:03:54 +01:00
backend Harden scanner pairing with persistent sessions and integration tests 2026-04-02 01:14:38 +02:00
docker Harden scanner pairing with persistent sessions and integration tests 2026-04-02 01:14:38 +02:00
docs Split SumUp Solo/Tap providers and improve POS payment flow 2026-03-23 17:24:45 +01:00
frontend Harden scanner pairing with persistent sessions and integration tests 2026-04-02 01:14:38 +02:00
scanner Harden scanner pairing with persistent sessions and integration tests 2026-04-02 01:14:38 +02:00
.dockerignore Harden scanner pairing with persistent sessions and integration tests 2026-04-02 01:14:38 +02:00
.env.example Harden scanner pairing with persistent sessions and integration tests 2026-04-02 01:14:38 +02:00
.gitignore Harden scanner pairing with persistent sessions and integration tests 2026-04-02 01:14:38 +02:00
AndereAnbieter.md olli init 2026-02-14 13:03:54 +01:00
docker-compose.truenas.yml Harden scanner pairing with persistent sessions and integration tests 2026-04-02 01:14:38 +02:00
IdeasorToDo.md plan 2026-02-14 14:26:16 +01:00
README.md Align POS header status dot and document scanner stability fixes 2026-03-28 02:41:13 +01:00

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

Quick Start (Docker)

cp .env.example .env
# Edit .env with your credentials
docker compose -f docker/docker-compose.yml up --build

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:

  1. Create backend/app/payments/providers/newprovider.py
  2. 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/scan endpoint.
  • 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 OPTIONS requests to avoid backend 400 errors during preflight handling.
  • Backend CORS allows X-Scanner-Id in preflight requests for POST /api/v1/scanner/scan.
  • Token validation now uses GET /api/v1/scanner/validate-token (with X-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:

  • cash is always available.
  • sumup_solo is available when SUMUP_API_KEY, SUMUP_MERCHANT_CODE, and SUMUP_AFFILIATE_KEY are configured and the SumUp API is reachable.
  • sumup_tap is available when SUMUP_AFFILIATE_KEY is configured.
  • fake_card is 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_CENTS
  • PAYMENT_MINIMUM_SUMUP_SOLO_CENTS
  • PAYMENT_MINIMUM_SUMUP_TAP_CENTS
  • PAYMENT_MINIMUM_FAKE_CARD_CENTS (default 100 = 1,00 EUR)
  • PAYMENT_MINIMUM_TOPUP_BARCODE (default VERSCHENKT-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.