Since 2020, every electronic cash register system in Germany must have each business transaction signed by a certified Technical Security Device (TSE). Every receipt must show the signature, signature counter and serial number — in practice as a QR code.
Our invoicing runs in DataFlex. Building a TSE directly into it would have meant pulling HTTPS communication, JSON processing and certificate handling into a long-established application. That was exactly what we wanted to avoid.
Decision: The signature moves completely out of DataFlex into a separate script. DataFlex only exchanges a simple text file with four fields and knows nothing about HTTP, tokens or JSON.
Architecture
Three participants, two handoff points. DataFlex writes an input file and starts the script; the script handles the HTTPS communication with the signing service and writes the result back as an output file. This pattern was already established for other integrations and could be adopted unchanged.
DataFlex CPS fiskaly
│ │ │
│ GO\...9AW │ │
├────────────────►│ POST /auth │
│ ├───────────────────►│
│ │ PUT tx rev=1 │
│ ├───────────────────►│
│ │ PUT tx rev=2 │
│ ├───────────────────►│
│ OK\...9AW │◄─── Signature ─────┤
│◄────────────────┤ │
│ │ │
Receipt with QR
The Data Exchange
Kept as simple as possible: KEY: VALUE lines that both sides can read without
a parser. Input is four fields, two of which have defaults.
──── Input ──────────────────────────
RNR: 12345
BRUTTO: 29.99
MWST: NORMAL
ZAHLART: CASH
──── Output ─────────────────────────
BACK: 0
RECEIPT_TYPE: RECEIPT
QR_TEXT: V0;KASSE-01;Beleg;...
QR_SIGNATUR: ;MEUCIQD...;BFqE...
The return value BACK is the only number DataFlex needs to evaluate — the
error class is already encoded in it.
| Back | Meaning | Cause |
|---|---|---|
0 | Signed | Everything OK, TSE data is available |
1 | Input error | Required field missing in the input file |
7 | HTTP / API | Network down, timeout, service reports error |
9 | Authentication | Key or secret invalid |
What Was Built
Thirteen scripts, cleanly divided into three groups: one-time setup, ongoing operation, diagnostics. Only the operation scripts are called from DataFlex.
Ongoing Operation
| Script | Purpose | Return |
|---|---|---|
fisk_bon.cps | Sign receipt — the production path | QR in two parts |
fiskaly_rechnung.cps | Sign with additional print receipt | full TSE fields |
fiskaly_storno.cps | Cancel a completed receipt | full TSE fields |
fiskaly_export.cps | DSFinV-K export for tax audit | path to ZIP file |
Setup and Diagnostics
| Script | Purpose |
|---|---|
fiskaly_tss_create.cps | Create security device, returns the PUK |
fiskaly_tss_init.cps | Activate TSS, set admin PIN |
fiskaly_client.cps | Register cash register as client |
fiskaly_setup.cps | Everything in one run — early version |
fiskaly_auth.cps | Login only: verifies credentials |
fiskaly_tss_info.cps | Shows status and counters of the TSS |
fiskaly_tx.cps | Single transaction without file exchange |
There were also two documents: a project overview and a step-by-step guide to switching from test to live, including the fallback procedure.
One signature, three calls
A receipt is created in three HTTPS calls. The transaction is opened first and only completed with the amounts in the second step, so an interrupted checkout remains traceable as required by the regulation.
1. POST /auth
→ Token, valid for around 30 minutes
2. PUT /tss/{tss}/tx/{uuid}?tx_revision=1
{ "state": "ACTIVE", "client_id": ... }
→ Transaction opened
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": [...] }}}}
→ Signature, counter, QR data
Each script run requests a fresh token. Tokens are not cached: the scripts run for only seconds, and an expired token would be a more troublesome source of errors than an additional call.
The QR code and the 255-character problem
This was the project’s most satisfying discovery. The QR data exceeds the capacity of a DataFlex string. The script splits the QR string immediately before the signature and returns two parts, keeping the leading semicolon on the second part. DataFlex simply joins them together.
Cancellation
A reversal is a separate signed transaction of type ANNULATION with
negated amounts and an optional reference to the original invoice number.
In the production script, a negative amount in the input file is enough:
the receipt type switches automatically.
Export for a tax audit
The export is asynchronous: start it, wait, then download it. The script polls every five seconds for up to five minutes and then downloads the ZIP file. A date range can be supplied; otherwise, all data is exported.
What held us up
Test and live are separate environments behind the same address. The API key determines the environment; test keys start with
test_. The TSS and client had to be created again for live use, because the test objects do not exist there.The PUK is shown exactly once. If it is not saved immediately, the TSS can never be administered again. The scripts therefore save it at once and also display it prominently in the console.
Reversing a completed receipt requires
ANNULATION.CANCELLATIONmeans a checkout was interrupted before completion. Confusing the two produces formally incorrect receipts.Activating the TSS involves six dependent steps. These span three state changes and an administrator login. The setup script first checks the current state and starts at the appropriate step.
The signature alone does not meet every tax authority requirement. The DSFinV-K daily close for cash accounting remains the responsibility of the DataFlex side; the signing service supplies only the TSE component.
Project Timeline
| Date | Step |
|---|---|
| 24 Apr | Integration complete. Auth, TSS, client, transaction, receipt and cancellation — built in one day against the test environment. |
| 25 Apr | Export added. DSFinV-K export with async waiting for completion. |
| 05 May | Switch to live. New TSS and client in the live environment; process and fallback documented. |
| 10 May | Project overview. Architecture, file list and pitfalls recorded. |
| 26 May | Receipt variant finalised. QR split for DataFlex and automatic cancellation detection on negative amount. |
| 01 Aug | Housekeeping. Own project folder; credentials excluded from repository and replaced with templates. |
Open Points
The integration is running in production. Three things are known and unresolved — none of them block operation.
- Credential storage is not yet encrypted. The script system already has the means; they are just not used here yet.
- The all-in-one setup script is outdated. It dates from the first session and activates the TSS via a shortcut that the later individual scripts corrected. For a new setup, the individual scripts are the right path.
- No retry on network failures. If the line drops during signing, the script reports an error and aborts. For everyday use this is sufficient — the operator sees it immediately — but there is no automatic retry.
Returning to the project
Three checks are enough to get started again: the login script verifies
credentials, TSS info must show INITIALIZED, and a test receipt passed
through the input file proves the entire chain. Only then is it worth
looking into the scripts.