Das Quadrat unten auf einer Rechnung, das die Banking-App scannt und daraus die Überweisung ausfüllt, heißt in Deutschland GiroCode. Es ist kein Produkt einer Bank, sondern ein offener Standard: der EPC-QR-Code des European Payments Council, beschrieben im Dokument EPC069-12, Quick Response Code: Guidelines to Enable Data Capture for the Initiation of a SEPA Credit Transfer. Lizenzfrei, und kurz genug, um ihn an einem Nachmittag zu lesen.
Wir haben ihn diese Woche zweimal gelesen: einmal für einen GiroCode-Generator, der vollständig im Browser läuft, und einmal, als der dafür geschriebene Prüfer unsere eigenen Beispielrechnungen ablehnte. Darum geht es unten auch.
Zwölf Zeilen Text
Ein GiroCode enthält keinen Link und keine Kennung, nur bis zu zwölf Zeilen reinen Text, getrennt durch Zeilenumbrüche. Die App liest sie und füllt ihr Überweisungsformular; bestätigen muss der Zahler dort selbst.
| Zeile | Inhalt | Grenze nach EPC069-12 |
|---|---|---|
| 1 | Service-Tag BCD |
fest |
| 2 | Version 001 oder 002 |
ab 002 ist die BIC innerhalb des EWR optional |
| 3 | Zeichensatz, 1 bis 8 |
1 steht für UTF-8 |
| 4 | Kennung SCT (SEPA Credit Transfer) |
fest |
| 5 | BIC | 8 oder 11 Zeichen |
| 6 | Name des Empfängers | höchstens 70 Zeichen |
| 7 | IBAN | höchstens 34 Zeichen, nur IBAN |
| 8 | Betrag, EUR gefolgt von der Zahl |
EUR0.01 bis EUR999999999.99, optional |
| 9 | Zweck-Code (AT-T007) | höchstens 4 Zeichen, optional |
| 10 | strukturierte Referenz | höchstens 35 Zeichen, optional |
| 11 | unstrukturierter Verwendungszweck | höchstens 140 Zeichen, optional |
| 12 | Hinweis an den Zahler | höchstens 70 Zeichen, optional |
Dazu kommen drei Regeln, die über einzelne Zeilen hinausgehen: Zeile 10 und Zeile 11 schließen sich gegenseitig aus, hinter der letzten gefüllten Zeile folgt kein Zeilenumbruch mehr, und der ganze Inhalt darf 331 Bytes nicht überschreiten.
Woran erzeugte Codes scheitern
Keiner der folgenden Fehler fällt beim Erzeugen auf. Die meisten Bibliotheken kodieren, was man ihnen gibt, und das Ergebnis ist ein einwandfreier QR-Code mit einem Inhalt, den eine App, die sich an den Standard hält, nicht übernehmen kann. Bemerkt wird das erst beim Zahler — nachdem er die Rechnung gescannt hat.
Die Prüfziffer der IBAN
Die beiden Ziffern hinter dem Ländercode einer IBAN sind eine Prüfsumme über den Rest, gerechnet
nach ISO 7064 Mod 97-10: die ersten vier Zeichen ans Ende stellen, Buchstaben in Zahlen umsetzen (A = 10 bis
Z = 35), und das Ganze muss modulo 97 den Rest 1 ergeben.
Für Musterdaten nimmt man üblicherweise eine IBAN aus lauter Nullen, DE00 0000 0000 0000 0000 00.
Die kann es nicht geben: 00 ist nie eine gültige Prüfziffer. Für eine deutsche Kontonummer aus
lauter Nullen ist die richtige 36, also DE36 0000 0000 0000 0000 00 — weiterhin niemandes Konto,
aber gültig.
Wir haben das auf die unangenehme Art gelernt: Unsere eigene Beispielrechnung und unsere
Beispielmahnung trugen seit Wochen DE00… im GiroCode. Erst der Prüfer, den wir für den Generator
schrieben, hat sie abgelehnt. Beide sind inzwischen korrigiert.
Wer die Prüfung selbst einbaut, stolpert noch über etwas anderes: Die umgestellte Zahl hat je nach
Land 20 bis über 30 Stellen, bei einer deutschen IBAN 24, und liegt damit weit über den rund 16
Stellen, die eine Gleitkommazahl genau darstellt. % 97 auf die ganze Zahl liefert still ein
falsches Ergebnis; gerechnet wird Ziffer für Ziffer.
const umgestellt = iban.slice(4) + iban.slice(0, 4);
const ziffern = [...umgestellt]
.map((c) => (c >= 'A' && c <= 'Z' ? c.charCodeAt(0) - 55 : c))
.join('');
let rest = 0;
for (const d of ziffern) rest = (rest * 10 + Number(d)) % 97;
return rest === 1;
Das Komma im Betrag
Zeile 8 verlangt einen Punkt als Dezimaltrenner: EUR1234.50. Wer den Betrag mit einer deutschen
Zahlformatierung erzeugt, schreibt EUR1234,50 — lesbar für Menschen, aber kein Betrag im Sinne des
Standards. Der Fehler entsteht meist nicht im GiroCode-Code selbst, sondern eine Schicht darüber, wo
jemand den Betrag für die Anzeige formatiert und denselben String weiterreicht.
Strukturiert oder frei
Zeile 10 ist die strukturierte Gläubigerreferenz; der Standard nennt dafür die RF-Referenz nach
ISO 11649, die mit RF und zwei Prüfziffern beginnt. Eine Rechnungsnummer wie 2026-0042 ist
keine solche Referenz, und eine Banking-App, die das Feld als strukturiert behandelt, kann damit
nichts anfangen. Die Rechnungsnummer gehört als Verwendungszweck in Zeile 11. Beide Zeilen zu füllen
ist nicht erlaubt, und welche davon eine Bibliothek dann verwirft, ist ihre Sache, nicht die des
Standards.
Auch hier waren unsere Beispiele betroffen: Sie trugen die Rechnungsnummer in Zeile 10, obwohl die Mahnung sie in ihrer eigenen Bildunterschrift „Verwendungszweck“ nannte.
331 Bytes, nicht Zeichen
Die Grenze ist in Bytes angegeben, und in UTF-8 kostet ein Umlaut zwei davon. Ein Verwendungszweck
von 140 Zeichen, der die Feldgrenze einhält, kann mit genug ä, ö und ü die Gesamtgrenze reißen.
Die Zahl selbst ist kein Zufall: Der Standard legt Fehlerkorrektur-Stufe M fest und erlaubt höchstens QR-Version 13, 69 × 69 Module. Genau 331 Bytes passen in einen solchen Code.
Der erklärte und der tatsächliche Zeichensatz
Zeile 3 erklärt, wie der Rest kodiert ist. Erklärt ein Code 1 (UTF-8), schreibt den Text aber in
ISO 8859-1, erscheinen Umlaute im Empfängernamen als Zeichensalat. Der Code ist dann nicht ungültig,
nur falsch — was schlimmer ist, weil es niemand prüft.
In der Vorlage
In einer Formfeed-Vorlage entsteht der GiroCode aus den Daten der Rechnung, mit dem Helper epcQr.
So steht er in unserer Beispielrechnung, die der Render-Server ausgibt:
<img src="{{ epcQr({
name: company.name,
iban: company.iban,
bic: company.bic,
amount: net + vat,
text: 'Rechnung ' ~ invoice.number
}, { size: 110 }) }}" alt="GiroCode">
Der Betrag kommt aus derselben Rechnung wie die Summe darüber, die Rechnungsnummer als
Verwendungszweck in Zeile 11. Wie Liquid und Handlebars den Aufruf schreiben, zeigt die
Helper-Referenz. Die Felder sind in allen
dreien dieselben: text füllt Zeile 11, und reference gehört nur zu einer RF-Gläubigerreferenz.

Was der Helper tut und was nicht: epcQr baut die Zeilen nach dem Standard und zeichnet den
Code, mit Fehlerkorrektur M. Er prüft die Daten beim Rendern nicht; das tut der
Generator, der dieselben Zeilen aus demselben Code baut und vorher die
Prüfziffer, die Längen, den Betragsbereich und die 331 Bytes kontrolliert. Wer seine Stammdaten
einmal dort einfügt, sieht, ob der Code auf der nächsten Rechnung stimmt.
Was ein GiroCode nicht ist
Er ist eine Überweisung und nichts sonst: keine Lastschrift, keine zweite Währung, keine Zahlungsaufforderung, die ohne den Zahler etwas auslöst. Und er ist keine E-Rechnung. ZUGFeRD, Factur-X und XRechnung machen die Rechnung selbst maschinenlesbar für die Buchhaltung des Empfängers; der GiroCode hilft einem Menschen beim Bezahlen. Beides passt auf dieselbe Rechnung.
Der Standard und die Angaben zu den Feldern wurden am 21. September 2026 gegen EPC069-12 in Version 3.1 (19. März 2024) geprüft.