Files
QR-Invoice/docs/DEVELOPER-GUIDE.md
2026-02-27 16:04:50 +01:00

5.9 KiB

Swiss QR-Bill - Entwickler-Anleitung

Offizielle Spezifikation

Die einzige verbindliche Quelle ist die SIX Group Spezifikation:

Swiss Implementation Guidelines QR-Rechnung v2.3 https://www.six-group.com/dam/download/banking-services/standardization/qr-bill/ig-qr-bill-v2.3-en.pdf

Dieses Dokument ist Pflichtlektüre - es definiert alle Regeln.


Wichtigste Regeln

1. IBAN-Typen und Referenzen

Referenztyp IBAN-Typ IID (Stelle 5-9) Prüfziffer
QRR QR-IBAN 30000-31999 Modulo 10 rekursiv
SCOR Normale IBAN < 30000 oder > 31999 Modulo 97 (ISO 11649)
NON Normale IBAN < 30000 oder > 31999 Keine

Wichtig: QRR-Referenz funktioniert nur mit QR-IBAN. SCOR/NON nur mit normaler IBAN.

2. QRR-Referenz (27 Stellen)

26 Ziffern + 1 Prüfziffer = 27 Stellen

Prüfziffer-Berechnung (Modulo 10 rekursiv):
- Tabelle: [0, 9, 4, 6, 8, 2, 7, 1, 3, 5]
- Für jede Ziffer: carry = tabelle[(carry + ziffer) % 10]
- Prüfziffer = (10 - carry) % 10

JavaScript-Beispiel:

function calculateMod10Recursive(ref) {
  const table = [0, 9, 4, 6, 8, 2, 7, 1, 3, 5];
  let carry = 0;

  for (const char of ref) {
    const digit = parseInt(char, 10);
    if (!isNaN(digit)) {
      carry = table[(carry + digit) % 10];
    }
  }

  return (10 - carry) % 10;
}

// Verwendung:
const reference26 = '00000000000020240115123456';  // 26 Stellen
const checkDigit = calculateMod10Recursive(reference26);
const fullReference = reference26 + checkDigit;    // 27 Stellen

3. Strukturierte Adresse (seit Nov. 2025 Pflicht)

{
  "name": "Max 70 Zeichen",
  "street": "Strassenname (max 70)",
  "buildingNumber": "Hausnummer (max 16)",
  "postalCode": "PLZ (max 16)",
  "city": "Ort (max 35)",
  "country": "CH"
}
Feld Max. Länge Pflicht
name 70 Ja
street 70 Nein
buildingNumber 16 Nein
postalCode 16 Ja
city 35 Ja
country 2 Ja (ISO 3166-1 alpha-2)

4. Betrag

  • Minimum: 0.01
  • Maximum: 999'999'999.99
  • Maximal 2 Dezimalstellen
  • Optional (kann leer sein für Spenden etc.)

5. Währung

Nur CHF oder EUR erlaubt.


Empfohlene Bibliothek

swissqrbill (Node.js/TypeScript) https://github.com/schoero/swissqrbill

npm install swissqrbill pdfkit

Diese Bibliothek:

  • Generiert korrektes PDF-Layout nach SIX-Vorgaben
  • Erstellt den QR-Code
  • Unterstützt alle Sprachen (DE, FR, IT, EN)
  • Bietet Optionen für Perforierung/Schere

Beispiel-Payload

{
  "creditor": {
    "iban": "CH4431999123000889012",
    "address": {
      "name": "Meine Firma AG",
      "street": "Hauptstrasse",
      "buildingNumber": "1",
      "postalCode": "8000",
      "city": "Zürich",
      "country": "CH"
    }
  },
  "amount": 1500.00,
  "currency": "CHF",
  "reference": {
    "type": "QRR",
    "value": "000000000000000000000000000"
  },
  "debtor": {
    "address": {
      "name": "Kunde GmbH",
      "street": "Kundenweg",
      "buildingNumber": "42",
      "postalCode": "3000",
      "city": "Bern",
      "country": "CH"
    }
  },
  "additionalInformation": {
    "message": "Rechnung 2024-001"
  },
  "options": {
    "language": "de",
    "separate": true
  }
}

Validierung implementieren

Checkliste

  1. IBAN-Format prüfen

    • CH/LI + 19 alphanumerische Zeichen
    • Leerzeichen entfernen, Grossbuchstaben
  2. IBAN-Typ erkennen

    • IID extrahieren (Stelle 5-9)
    • 30000-31999 = QR-IBAN
    • Andere = normale IBAN
  3. Referenz-IBAN-Kompatibilität prüfen

    • QRR erfordert QR-IBAN
    • SCOR/NON erfordert normale IBAN
  4. QRR-Prüfziffer validieren

    • Modulo 10 rekursiv (siehe oben)
  5. SCOR-Prüfziffer validieren

    • Format: RF + 2 Prüfziffern + max. 21 alphanumerisch
    • Modulo 97 nach ISO 11649
  6. Adressfelder auf Länge prüfen

    • Alle Maximal-Längen einhalten

IBAN-Validierung (JavaScript)

function isQrIban(iban) {
  const cleanIban = iban.replace(/\s/g, '').toUpperCase();
  const iid = parseInt(cleanIban.substring(4, 9), 10);
  return iid >= 30000 && iid <= 31999;
}

function validateIbanReferenceCombo(iban, referenceType) {
  const isQr = isQrIban(iban);

  if (referenceType === 'QRR' && !isQr) {
    return 'QRR erfordert eine QR-IBAN (IID 30000-31999)';
  }

  if ((referenceType === 'SCOR' || referenceType === 'NON') && isQr) {
    return 'SCOR/NON erfordert eine normale IBAN';
  }

  return null; // OK
}

Sprachen

Code Sprache
de Deutsch
fr Französisch
it Italienisch
en Englisch

Die Sprache beeinflusst:

  • Überschriften ("Zahlteil" / "Récépissé" / etc.)
  • Feldbezeichnungen
  • Zusatztexte

PDF-Layout

Der QR-Zahlteil besteht aus zwei Teilen:

  1. Empfangsschein (links, 62mm breit)
  2. Zahlteil (rechts, 148mm breit)

Gesamtgrösse: 210mm x 105mm (A6 quer, unten auf A4)

Die swissqrbill Bibliothek generiert das Layout automatisch korrekt.


Testressourcen


Häufige Fehler

Fehler Ursache Lösung
Ungültige Referenz Falsche Prüfziffer Modulo 10 rekursiv korrekt berechnen
IBAN/Referenz Mismatch QRR mit normaler IBAN QR-IBAN verwenden oder auf SCOR/NON wechseln
Adresse zu lang Feldlänge überschritten Auf max. Länge kürzen
Ungültige Währung USD oder andere Nur CHF oder EUR verwenden

API-Endpunkt (dieses Projekt)

POST /api/v1/invoice/qr-bill
Content-Type: application/json

Response: application/pdf (Binary)

Health Check:

GET /health
GET /health/ready