GiroCode and the EPC Payment QR Format — featured illustration
Technology

GiroCode and the EPC Payment QR Format

6 min read
Create a QR code

An invoice from a German utility arrives with a small square printed beside the bank details. Opened in the bank's own app, it fills a transfer form: recipient, IBAN, amount, reference, all already there. Nothing was fetched from a server to do that. Every one of those values was printed into the symbol when the invoice was generated, and they will still be there in five years.

That is a GiroCode, a narrower thing than most people assume, and worth understanding precisely because the format bakes decisions into ink.

What a GiroCode is

A GiroCode is an ordinary QR code carrying a text payload defined by the European Payments Council, in a guideline normally cited as EPC069-12. The payload describes a SEPA credit transfer in euro. A banking app reads it and pre-fills a transfer form; the payer still authorises the payment. No money moves because a code was scanned.

What it is not is the Swiss QR-bill, a different format on a different payment slip with a Swiss cross in the middle of the symbol. A German banking app will not treat one as the other.

The payload is lines in a fixed order

The content is plain text: fields one per line, separated by line feeds. Position carries the meaning, so the order is not negotiable.

  1. Service tag, always BCD.
  2. Version, either 001 or 002.
  3. Character set, one digit.
  4. Identification, always SCT.
  5. BIC of the beneficiary bank, required at version 001 and optional at 002 inside the EEA.
  6. Beneficiary name, up to 70 characters.
  7. Beneficiary IBAN, up to 34 characters.
  8. Amount, written as EUR followed by the figure.
  9. Purpose code, four characters.
  10. Structured creditor reference.
  11. Unstructured remittance text, up to 140 characters.
  12. A note to the payer.

Either the structured reference or the unstructured text is used, never both, and the whole payload is capped at 331 bytes. An unused field is an empty line rather than an omitted one, though trailing empties can be dropped.

Because position carries the meaning, a payload that is almost right fails in an interesting way. Drop one line feed and every value after it slides up a slot, so the amount arrives where the purpose code should be. The app does not see a malformed code; it sees a differently filled form.

Which apps read it, and which do not

Support lives in German and Austrian retail banking apps, which is also the limit of the format's usefulness. The savings banks and cooperative banking apps, the major commercial banks' apps, and in Austria the main retail apps all handle it to varying degrees. Because that list changes with app releases, check the app your customers use rather than trusting any published one, this paragraph included.

Everything else decodes the text and does nothing with it. A phone camera or a general scanner app reads a GiroCode perfectly well and shows twelve lines beginning with BCD, which is correct and no use at all: starting a transfer needs an app connected to a bank account. Outside the euro area, a GiroCode is decoration on an invoice.

Everything important is baked into the symbol

This is a static payload. The IBAN, the amount and the reference are part of the printed pattern, and nothing is fetched at scan time. Three consequences follow, and all three catch billers out.

A wrong IBAN or amount is a reprint, not an edit. There is no dashboard behind a GiroCode because there is no redirect in it, so none of the options in changing a printed destination without reprinting apply. A URL in a symbol can be repointed; an IBAN in a symbol is the IBAN forever.

An amount printed on an invoice stays the amount the code fills in. Part payments, credits and instalment arrangements all diverge from it, and the code keeps offering the original figure long after the balance has changed. For anything payable in parts, leaving the amount empty is more honest than printing a number that will go stale.

And per-invoice codes mean per-invoice symbols, which is a data merge inside the billing system rather than a design job.

The character set is narrower than the format suggests

The payload declares a character set in its third line, and a QR symbol will carry any bytes you hand it. The restriction is in the payment underneath, not in the code. The SEPA schemes accept a restricted Latin set, broadly the unaccented letters, digits, space and a short list of punctuation.

Umlauts and the sharp s are the everyday casualty. A beneficiary name gets transliterated, so Müller becomes Mueller, either by the biller or by something further down the chain, and names in a non-Latin script fare worse. This is a scheme rule rather than a QR limitation, but it lands on whoever builds the payload, and the result is a mangled recipient name on the one screen the payer is supposed to check. Non-Latin text and encoding covers the general problem; here the restriction is stricter than anything the symbology imposes.

The swapped sticker is the whole security story

A GiroCode carries no signature and no evidence of who created it: anyone can generate a valid one for any IBAN. So the attack is physical and cheap: a label pressed over the code on an invoice, a reminder or a donation box, and every scan afterwards fills in a form addressed somewhere else.

What makes it work is that nothing looks wrong. The app opens, the form is populated, the reference matches the invoice in the payer's hand, and the only field that changed is the one most people do not read. The beneficiary name and IBAN do appear on that confirmation screen, which is the only check in the system. It works only if somebody looks at it.

Three things help. Print the IBAN as text beside the code so a payer can compare the two. Treat any payment request that arrives unexpectedly as suspect regardless of the letterhead, which is the pattern behind most quishing attempts. And if you are the biller, avoid adhesive codes on documents, because a sticker that belongs there teaches customers that a sticker is normal. The same logic from the market-stall side runs through payment codes on a stall sign.

Where to get one, and whether you want one

QRolling does not generate EPC payloads. Our types are URL, text, wifi, vCard, email, SMS, phone and the rest of the list in the generator, and none of them produces a SEPA credit transfer. If you need one, the two sensible sources are your own banking app, several of which produce a GiroCode for your account, or a tool built for the EPC format. Prefer a purpose-built tool over a generic generator for one reason: getting the field order, the line feeds, the character set and the 331-byte cap right is the entire job, and a generic text field checks none of it.

There is also a fair chance a GiroCode is not what you need. If the requirement is "a customer scans and pays", a hosted payment page behind a URL code reaches any phone in any country with no dependency on the payer's bank, handles cards as well as transfers, and produces a receipt. That is what the payment code pages cover, and because every code here is dynamic, the destination stays editable on the paid plan, which is the one thing a printed IBAN can never be.

Before a GiroCode goes onto an invoice template, test the finished document in the banking apps your customers use, and print the IBAN beside the symbol so a payer can check the form against the paper.

Share this article