Zum Hauptinhalt springen

Öffentliche Bezahlseite

Zweck

Die öffentliche Bezahlseite gibt Ihren Kunden eine Web-Adresse, unter der sie eine bestimmte Rechnung ohne Login bezahlen können — per Banküberweisung (mit GiroCode-QR fürs Banking-App-Scannen) oder per PayPal. Nach einer erfolgreichen PayPal-Zahlung gleicht SpeamCore den zugehörigen offenen Posten automatisch aus; die genaue Mechanik beschreibt das Konzept PayPal-Zahlungsannahme.

Die Seite läuft unter der Route /pay/:ref und wird ohne den üblichen App-Rahmen angezeigt (kein Menü, keine Anmeldung).

Voraussetzungen

- Die öffentliche Bezahlseite ist in den [Rechnungsausgang-Einstellungen](/settings/rechnungsausgang) aktiviert (`publicPaymentPageEnabled`). - Mindestens eine Zahlmethode ist freigegeben (`bank` und/oder `paypal`). - Für **PayPal** als Methode: eine aktive [PayPal-Anbindung](/transaction-accounts) mit `paymentsEnabled`. - Für **Banküberweisung**: eine hinterlegte Bankverbindung an der Niederlassung der Rechnung. - Zur Rechnung existiert ein **Bezahllink** (`InvoicePaymentLink`); er wird beim Drucken/Versenden mit aktiviertem „Jetzt bezahlen"-QR automatisch angelegt.

Berechtigungen (CASL)

Die Seite ist öffentlich — es gibt keine CASL-Prüfung und keine Anmeldung. An ihre Stelle tritt eine fachliche Zugangskontrolle:

  • Der ref in der URL ist die zufällige, nicht erratbare UUID des Bezahllinks (InvoicePaymentLink.id).
  • Zusätzlich muss der Kunde Rechnungsnummer und Kundennummer angeben (beide aus dem jeweiligen Nummernkreis). Stimmt eines nicht, antwortet der Server mit einem generischen Fehler.
  • Ist die Bezahlseite in den Einstellungen ausgeschaltet, ist jeder Link „nicht gefunden".

Beträge und der Rechnungsbezug werden immer serverseitig aus dem Link abgeleitet — nie aus der Anfrage des Browsers übernommen.

So gelangt der Kunde auf die Seite

  1. Auf der Rechnung (PDF/Druck) steht ein QR-Code. Welcher QR gedruckt wird, steuert der QR-Modus in den Rechnungsausgang-Einstellungen:
    • „Jetzt bezahlen"-QR (payLinkQr) → führt direkt auf /pay/:ref; Rechnungs- und Kundennummer werden aus dem Link als Parameter mitgegeben.
    • Bank-QR (bankQr) → klassischer GiroCode für die Banking-App (keine Bezahlseite).
  2. Alternativ können Sie den Link manuell aus dem Bezahllink der Rechnung weitergeben.
  3. Öffnet der Kunde den Link ohne mitgelieferte Parameter, fragt die Seite Rechnungs- und Kundennummer ab.

Schritt-für-Schritt — Kundensicht

  1. Kunde öffnet /pay/:ref (per QR oder Link).
  2. Falls nötig: Rechnungsnummer und Kundennummer eingeben → die Seite lädt die Rechnungsdaten (POST /api/public-pay/:ref/info).
  3. Die Seite zeigt Absender (Firma), Rechnungsnummer, Rechnungsdatum, Fälligkeit und den zu zahlenden Betrag. Bei Überfälligkeit erscheint ein entsprechender Hinweis; liegt ein gültiges Skonto-Fenster vor, wird der rabattierte Betrag angeboten.
  4. Kunde wählt einen Zahlungsweg:
    • Banküberweisung → GiroCode-QR zum Scannen in der Banking-App (IBAN, BIC, Betrag, Verwendungszweck „Rechnung …" sind hinterlegt).
    • PayPal → der PayPal-Button (über POST /api/public-pay/:ref/order wird die Order angelegt, nach Bestätigung folgt …/capture).
  5. Nach erfolgreicher PayPal-Zahlung zeigt die Seite die Bestätigung („Zahlung am … via PayPal").

<Screenshot status="todo" beschreibung="Öffentliche Bezahlseite: Firmenkopf, Rechnungsnummer und Betrag, Fälligkeits-Banner, darunter zwei Kacheln „Banküberweisung (GiroCode-QR)" und „PayPal" mit dem PayPal-Button." />

Was die Seite anzeigt

AnzeigeBedeutungVoraussetzung
FirmennameAbsender der Rechnung (aus companyName)
Rechnungsnummer / -datumidentifiziert den Belegkorrekte Eingabe der Nummern
Fälligkeit + Statusoverdue (überfällig), dueToday (heute fällig) oder plannedRechnung noch offen
Zu zahlender Betragoffener Betrag des Postens (ggf. Skonto-rabattiert)
Skonto-Hinweisrabattierter Betrag inkl. FristSkonto-Fenster aktuell offen
Geschätzte PayPal-GebührAufschlag, falls Gebühren-Weitergabe aktivpaypalSurchargeEnabled + Gebührenkonto
ZahlungswegeBanküberweisung und/oder PayPalin den Einstellungen freigegeben
Bezahlt-Ansicht„Zahlung am … via PayPal/Bank"offener Posten ausgeglichen
Die geschätzte PayPal-Gebühr erscheint auf der Bezahlseite **nur**, wenn in den [Rechnungsausgang-Einstellungen](/settings/rechnungsausgang) `paypalSurchargeEnabled` aktiv und ein Gebühren-Ertragskonto hinterlegt ist. Die endgültige Gebühren-Rechnung entsteht erst beim erfolgreichen Bezahlen (separater Beleg).

Nach der Zahlung — automatischer Ausgleich

Nach erfolgreicher PayPal-Capture bucht SpeamCore im Hintergrund eine Transaktion und eine Zuordnung gegen die Rechnung — der offene Posten ist damit ausgeglichen, und die Capture-ID wird als paypalTransactionCode auf der Rechnung hinterlegt. Trifft zusätzlich der PayPal-Webhook ein, wird nicht doppelt gebucht (Idempotenz über die externalId). Details: PayPal-Zahlungsannahme.

Eine Banküberweisung wird wie gewohnt über den Kontoumsatz und die Auto-Allocation zugeordnet — die Bezahlseite stellt hier nur den GiroCode bereit, löst aber selbst keine Buchung aus.

API/Schnittstellen

Alle Endpunkte sind öffentlich (kein Keycloak/CASL); die Autorisierung erfolgt über ref + Rechnungs-/Kundennummer.

MethodeEndpointZweck
POST/api/public-pay/:ref/infoRechnungsdaten und freigegebene Zahlwege laden
POST/api/public-pay/:ref/orderPayPal-Order über den offenen Betrag anlegen
POST/api/public-pay/:ref/capturebestätigte Order abbuchen (Capture) → Settlement
POST/api/public-pay/:ref/sdk-tokenClient-Token für das PayPal-JS-SDK

Verknüpfungen zu anderen Modulen

Wiederverwendbare Konzepte

Häufige Fehler und Lösungen

FehlerLösung
„Nicht gefunden" trotz gültigem LinkBezahlseite ist in den Einstellungen deaktiviert oder der Bezahllink ist inactive
„Autorisierung fehlgeschlagen"Rechnungs- oder Kundennummer stimmt nicht (Format ignoriert Leerzeichen/Groß-/Kleinschreibung)
Kein PayPal-ButtonPayPal nicht als Methode freigegeben oder keine aktive Anbindung mit paymentsEnabled
Kein GiroCodeBanküberweisung nicht freigegeben oder keine Bankverbindung an der Niederlassung
Betrag wirkt zu niedriggültiges Skonto-Fenster — es wird der rabattierte Betrag eingezogen

Versionshinweise

  • 2026-06-22: Initiale Veröffentlichung — öffentliche Bezahlseite /pay/:ref mit Banküberweisung (GiroCode) und PayPal. Verifiziert an PublicPaymentPage.tsx, publicPayment.router.ts, publicPayment.service.ts und paypalOrders.service.ts.