Seit 2020 muss in Deutschland jedes elektronische Kassensystem jede Geschäftstransaktion durch eine zertifizierte Technische Sicherheitseinrichtung signieren lassen. Auf jedem Beleg müssen Signatur, Signaturzähler und Seriennummer stehen — in der Praxis als QR-Code.
Unsere Rechnungsstellung läuft in DataFlex. Eine TSE dort direkt einzubauen hätte bedeutet, in eine gewachsene Altanwendung HTTPS-Kommunikation, JSON-Verarbeitung und Zertifikatshandling einzuziehen. Genau das sollte vermieden werden.
Entscheidung: Die Signatur wandert komplett aus DataFlex heraus in ein eigenes Skript. DataFlex tauscht nur noch eine simple Textdatei mit vier Feldern aus und weiß nichts von HTTP, Tokens oder JSON.
Architektur
Drei Beteiligte, zwei Übergabepunkte. DataFlex schreibt eine Eingabedatei und startet das Skript; das Skript erledigt die HTTPS-Kommunikation mit dem Signaturdienst und legt das Ergebnis als Ausgabedatei zurück. Dieses Muster war bereits für andere Anbindungen etabliert und konnte unverändert übernommen werden.
DataFlex CPS fiskaly
│ │ │
│ GO\...9AW │ │
├────────────────►│ POST /auth │
│ ├───────────────────►│
│ │ PUT tx rev=1 │
│ ├───────────────────►│
│ │ PUT tx rev=2 │
│ ├───────────────────►│
│ OK\...9AW │◄─── Signatur ──────┤
│◄────────────────┤ │
│ │ │
Beleg mit QR
Der Datenaustausch
Bewusst so einfach wie möglich gehalten: KEY: VALUE-Zeilen, die beide Seiten
ohne Parser lesen können. Eingabe sind vier Felder, von denen zwei
Vorbelegungen haben.
──── Eingabe ────────────────────────
RNR: 12345
BRUTTO: 29.99
MWST: NORMAL
ZAHLART: CASH
──── Ausgabe ────────────────────────
BACK: 0
RECEIPT_TYPE: RECEIPT
QR_TEXT: V0;KASSE-01;Beleg;...
QR_SIGNATUR: ;MEUCIQD...;BFqE...
Der Rückgabewert BACK ist die einzige Zahl, die DataFlex auswerten muss — die
Fehlerklasse ist bereits darin kodiert.
| Back | Bedeutung | Ursache |
|---|---|---|
0 | Signiert | Alles in Ordnung, TSE-Daten liegen bei |
1 | Eingabefehler | Pflichtfeld fehlt in der Eingabedatei |
7 | HTTP / API | Netz weg, Timeout, Dienst meldet Fehler |
9 | Anmeldung | Key oder Secret ungültig |
Was gebaut wurde
Dreizehn Skripte, die sich sauber in drei Gruppen teilen: einmalige Einrichtung, laufender Betrieb, Diagnose. Nur die Betriebsskripte werden aus DataFlex heraus aufgerufen.
Laufender Betrieb
| Skript | Zweck | Rückgabe |
|---|---|---|
fisk_bon.cps | Beleg signieren — der produktive Pfad | QR in zwei Teilen |
fiskaly_rechnung.cps | Signieren mit zusätzlichem Druckbeleg | volle TSE-Felder |
fiskaly_storno.cps | Storno eines abgeschlossenen Belegs | volle TSE-Felder |
fiskaly_export.cps | DSFinV-K-Export für die Betriebsprüfung | Pfad zur ZIP-Datei |
Einrichtung und Diagnose
| Skript | Zweck |
|---|---|
fiskaly_tss_create.cps | Sicherheitseinrichtung anlegen, liefert den PUK |
fiskaly_tss_init.cps | TSS scharfschalten, Admin-PIN setzen |
fiskaly_client.cps | Kasse als Client registrieren |
fiskaly_setup.cps | Alles in einem Durchlauf — früher Stand |
fiskaly_auth.cps | Nur anmelden: prüft die Zugangsdaten |
fiskaly_tss_info.cps | Zeigt Zustand und Zähler der TSS |
fiskaly_tx.cps | Einzelne Transaktion ohne Dateiaustausch |
Dazu zwei Dokumente: eine Projektübersicht und eine Schritt-für-Schritt-Anleitung für den Wechsel von Test auf Live samt Rückfallweg.
Eine Signatur, drei Aufrufe
Ein Beleg entsteht in drei HTTPS-Aufrufen. Die Transaktion wird zuerst eröffnet und erst im zweiten Schritt mit den Beträgen abgeschlossen — so ist auch ein abgebrochener Kassiervorgang nachvollziehbar, wie es die Verordnung verlangt.
1. POST /auth
→ Token, rund 30 Minuten gültig
2. PUT /tss/{tss}/tx/{uuid}?tx_revision=1
{ "state": "ACTIVE", "client_id": ... }
→ Transaktion eröffnet
3. PUT /tss/{tss}/tx/{uuid}?tx_revision=2
{ "state": "FINISHED",
"schema": { "standard_v1": { "receipt": {
"receipt_type": "RECEIPT",
"amounts_per_vat_rate": [...],
"amounts_per_payment_type": [...] }}}}
→ Signatur, Zähler, QR-Daten
Jeder Skriptlauf holt sich einen frischen Token. Auf ein Zwischenspeichern wurde verzichtet: Die Skripte leben nur Sekunden, und ein abgelaufener Token wäre die unangenehmere Fehlerquelle als ein zusätzlicher Aufruf.
Der QR-Code und das 255-Zeichen-Problem
Die schönste Fundsache des Projekts. Die QR-Daten sind länger als eine DataFlex-Zeichenkette fassen kann. Statt DataFlex umzubauen, trennt das Skript den QR-String direkt vor der Signatur und liefert zwei Teile zurück — das führende Semikolon bleibt am zweiten Teil. DataFlex hängt beide aneinander und ist fertig.
Storno
Ein Storno ist technisch eine eigene signierte Transaktion vom Typ ANNULATION
mit negierten Beträgen, optional mit Verweis auf die ursprüngliche
Rechnungsnummer. Im produktiven Skript genügt ein negativer Betrag in der
Eingabedatei — der Belegtyp schaltet automatisch um.
Export für die Betriebsprüfung
Der Export läuft asynchron: anstoßen, dann warten, dann laden. Das Skript fragt alle fünf Sekunden nach, höchstens fünf Minuten lang, und lädt dann die ZIP-Datei. Ein Zeitraum lässt sich optional mitgeben, sonst kommen alle Daten.
Was uns aufgehalten hat
Test und Live sind zwei getrennte Welten hinter derselben Adresse. Nicht die URL entscheidet, sondern der API-Key — Test-Keys tragen ein
test_davor. Die Folge: TSS und Client mussten für Live komplett neu angelegt werden, die Test-Objekte existieren dort nicht.Der PUK wird genau einmal gezeigt. Wer ihn nicht im selben Moment sichert, kann die TSS nie wieder administrieren. Deshalb schreiben die Skripte ihn sofort weg und stellen ihn zusätzlich hervorgehoben in die Konsole.
Storno heißt
ANNULATION, nichtCANCELLATION. Der zweite Begriff bedeutet „Kassiervorgang mittendrin abgebrochen" und ist etwas völlig anderes. Eine Verwechslung hier erzeugt formal falsche Belege.Das Scharfschalten der TSS ist keine einzelne Aktion. Es sind sechs aufeinander aufbauende Schritte über drei Zustandswechsel und eine Admin-Anmeldung hinweg. Das Einrichtungsskript prüft deshalb zuerst den Ist-Zustand und steigt an der passenden Stelle ein.
Die Signatur allein genügt dem Finanzamt nicht. Der DSFinV-K-Tagesabschluss der Kassenbuchhaltung bleibt Aufgabe der DataFlex-Seite; der Signaturdienst liefert nur den TSE-Anteil.
Projektverlauf
| Datum | Schritt |
|---|---|
| 24.04. | Anbindung steht. Anmeldung, TSS, Client, Transaktion, Rechnung und Storno — an einem Tag gegen die Testumgebung gebaut. |
| 25.04. | Export ergänzt. DSFinV-K-Export mit asynchronem Warten auf die Fertigstellung. |
| 05.05. | Umstellung auf Live. Neue TSS und neuer Client in der Live-Umgebung; Ablauf und Rückfallweg dokumentiert. |
| 10.05. | Projektübersicht. Architektur, Dateiübersicht und Stolperfallen festgehalten. |
| 26.05. | Belegvariante finalisiert. QR-Aufteilung für DataFlex und automatische Storno-Erkennung am negativen Betrag. |
| 01.08. | Ordnung und Absicherung. Eigener Projektordner; Zugangsdaten vor der Übernahme ins Repository ausgeschlossen und durch Vorlagen ersetzt. |
Offene Punkte
Die Anbindung läuft produktiv. Drei Dinge sind bekannt und unerledigt — keines davon blockiert den Betrieb.
Die Ablage der Zugangsdaten ist noch nicht verschlüsselt. Das Skriptsystem bringt die Mittel dafür bereits mit; sie werden hier noch nicht genutzt.
Das All-in-one-Einrichtungsskript ist überholt. Es stammt aus der ersten Sitzung und schaltet die TSS über einen verkürzten Weg scharf, den die späteren Einzelskripte korrigiert haben. Für eine Neueinrichtung sind die Einzelskripte der richtige Weg.
Kein Wiederholungsversuch bei Netzstörungen. Fällt die Leitung während der Signatur aus, meldet das Skript einen Fehler und bricht ab. Für den Alltag genügt das, weil der Bediener es sofort sieht — eine automatische Wiederholung gibt es aber nicht.
Wenn das Thema wieder aufgegriffen wird
Zum Aufwärmen genügen drei Schritte: das Anmeldeskript prüft die Zugangsdaten,
die TSS-Info muss INITIALIZED zeigen, und ein Testbeleg über die
Eingabedatei beweist die ganze Kette. Erst danach lohnt es sich, in die Skripte
zu schauen.