diff --git a/Changes.md b/Changes.md index 57683d9..b4bfa59 100644 --- a/Changes.md +++ b/Changes.md @@ -1,5 +1,25 @@ # Änderungen +## Echtzeitüberweisung im Zahlungsverkehr (25.09.2026) + +Der Überweisungsdialog bietet jetzt das Häkchen **Echtzeitüberweisung**: Das Geld ist binnen +Sekunden beim Empfänger. SEPA behandelt die Echtzeitüberweisung als eigenes Verfahren +(SCT Inst), der Finanzmanager deshalb auch. + +- Über FinTS geht der Auftrag als Geschäftsvorfall HKIPZ hinaus (`SendSEPARealtimeTransfer` + aus php-fints) statt als HKCCS. In der pain.001-Nachricht steht die Zahlungsart + `LclInstrm/Cd = INST`; das gibt es nur im ISO-Format `pain.001.001.03`. +- Eine Echtzeitüberweisung hat keinen Ausführungstermin: Der Dialog sperrt das Feld, ein + Termin in der Zukunft wird beim Speichern abgewiesen. +- Bietet die Bank keine Echtzeitüberweisung an, scheitert der Auftrag mit einer klaren + Meldung und bleibt stehen. Eine stille Umwandlung in eine gewöhnliche Überweisung durch + die Bank ist abgeschaltet. +- Die Übersicht kennzeichnet Echtzeitüberweisungen in der Spalte „Ausführung“, das + Übermittlungsprotokoll nennt sie ausdrücklich. +- Neue Spalte `zahlungsauftraege.echtzeit` (Migration `019`). +- Test 43 (`tests/43_EchtzeitueberweisungTest.php`) und ein E2E-Test für das Sperren des + Termins. + ## Übermittlungsprotokoll für Überweisungen und Bankkommunikation (21.09.2026) Als Nachweis gegenüber der Bank, dass eine Überweisung fristgerecht und korrekt übermittelt diff --git a/README.md b/README.md index e4bcab5..8450fb3 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ Haushalt wird beides parallel genutzt. eines wählbaren Zeitraums. * **Nebenmodule** – Buchungsvorlagen, regelmäßige Buchungen mit Fälligkeitslauf und Finanzkalender, Erinnerungen, Adressen, Verträge, Sparziele, Kredite mit Tilgungsplan. -* **Zahlungsverkehr** – Überweisungen und Terminüberweisungen über FinTS. +* **Zahlungsverkehr** – Überweisungen, Terminüberweisungen und Echtzeitüberweisungen über FinTS. * **Abgleich mit dem Bargeld-Tool** – wer Bargeldausgaben in einem zweiten Programm erfasst, sieht beim Zuweisen einer Kategorie sofort, ob die Buchung dort schon steht: grüner Haken, rotes X oder Ausrufezeichen in einer schmalen Spalte des Kontoblatts. diff --git a/docs/nebenmodule.md b/docs/nebenmodule.md index 92009e0..d402aef 100644 --- a/docs/nebenmodule.md +++ b/docs/nebenmodule.md @@ -131,6 +131,17 @@ Fehlgeschlagene tragen die Meldung der Bank daneben. Ohne Ausführungstermin geht die Überweisung sofort hinaus, mit Termin als Terminüberweisung; sie erscheint dann im Finanzkalender. +**Echtzeitüberweisung.** Das Häkchen „Echtzeitüberweisung“ im Überweisungsdialog +(Spalte `zahlungsauftraege.echtzeit`) macht aus dem Auftrag eine SEPA-Echtzeitüberweisung +(SCT Inst): Das Geld ist binnen Sekunden beim Empfänger. SEPA behandelt sie als eigenes +Verfahren – über FinTS geht sie als Geschäftsvorfall **HKIPZ** hinaus statt als HKCCS, in +der pain.001-Nachricht steht zusätzlich die Zahlungsart `PmtTpInf/LclInstrm/Cd = INST`. +Einen Ausführungstermin gibt es dafür nicht; der Dialog sperrt das Feld, das Modell weist +einen Termin in der Zukunft ab. Bietet die Bank keine Echtzeitüberweisung an (kein +Segment HIIPZS in den Bankparametern), scheitert der Auftrag mit einer klaren Meldung. +Eine Umwandlung in eine gewöhnliche Überweisung erlaubt der Finanzmanager der Bank +ausdrücklich nicht – wer Echtzeit wählt, soll erfahren, wenn es nicht klappt. + **Welches Konto bei der Bank gemeint ist**, entscheidet dieselbe Zuordnung wie beim Umsatzabruf: die Zeile in `bank_konten`, die dem Konto zugewiesen ist (IBAN, ersatzweise Kontonummer). Die IBAN am Konto selbst ist optional und wird nur herangezogen, wenn keine @@ -147,6 +158,8 @@ anderen nur das ältere DK-Format `pain.001.003.03` – und weisen alles andere Einreichen ab. Welche Formate eine Bank führt, steht in den Bankparametern (Segment HISPAS); `SepaXml::schemaWaehlen()` sucht daraus das passende aus, ISO bevorzugt. Für die Einzelüberweisung sind beide Fassungen im Aufbau gleich, es genügt der andere Namensraum. +Die Echtzeitüberweisung gibt es nur im ISO-Format: Das DK-Format kennt die Zahlungsart +`LclInstrm` nicht. Ihre Formate nennt die Bank in HIIPZS, ersatzweise gelten die aus HISPAS. Der **SEPA-Zeichenvorrat** ist eng: `a–z A–Z 0–9 / - ? : ( ) . , ' +` und Leerzeichen. Umlaute werden umgeschrieben (ä → ae), alles Übrige fällt weg. Ohne diese Umschrift weist diff --git a/migrations/019_echtzeitueberweisung.sql b/migrations/019_echtzeitueberweisung.sql new file mode 100644 index 0000000..d252d7f --- /dev/null +++ b/migrations/019_echtzeitueberweisung.sql @@ -0,0 +1,11 @@ +-- Echtzeitüberweisung (SEPA Instant Credit Transfer). +-- +-- Eine Echtzeitüberweisung ist im SEPA-Verfahren ein eigener Geschäftsvorfall: +-- Über FinTS geht sie als HKIPZ statt als HKCCS hinaus, und in der +-- pain.001-Nachricht steht die Zahlungsart INST. Sie wird sofort ausgeführt, +-- einen Ausführungstermin gibt es dafür nicht. +-- +-- Idempotent über IF NOT EXISTS. + +ALTER TABLE zahlungsauftraege + ADD COLUMN IF NOT EXISTS echtzeit TINYINT(1) NOT NULL DEFAULT 0 AFTER ausfuehrung_am; diff --git a/public/static/app.js b/public/static/app.js index 1fa92ea..7df8d62 100644 --- a/public/static/app.js +++ b/public/static/app.js @@ -192,6 +192,22 @@ }); } + // Echtzeitüberweisung: Sie geht sofort hinaus, ein Ausführungstermin hat + // daneben keinen Sinn. Das Feld wird gesperrt, solange sie gewählt ist – + // die Prüfung beim Speichern bleibt trotzdem die maßgebliche. + const echtzeit = document.querySelector('[data-echtzeit]'); + const termin = document.getElementById('ausfuehrung_am'); + if (echtzeit && termin) { + const abgleichen = function () { + termin.disabled = echtzeit.checked; + if (echtzeit.checked) { + termin.value = ''; + } + }; + echtzeit.addEventListener('change', abgleichen); + abgleichen(); + } + // Kontenzuordnung einer Bankverbindung: Die Felder für ein neues Konto // stehen in einer eigenen Tabellenzeile und werden erst sichtbar, wenn // „neues Konto anlegen“ gewählt ist. Ausgeblendet wird hier und nicht im diff --git a/src/Controllers/ZahlungsverkehrController.php b/src/Controllers/ZahlungsverkehrController.php index 98c18d4..4aac4f2 100644 --- a/src/Controllers/ZahlungsverkehrController.php +++ b/src/Controllers/ZahlungsverkehrController.php @@ -303,6 +303,7 @@ class ZahlungsverkehrController extends BaseController 'waehrung' => $this->post('waehrung', 'EUR'), 'verwendungszweck' => $this->post('verwendungszweck'), 'ausfuehrung_am' => $this->post('ausfuehrung_am'), + 'echtzeit' => isset($_POST['echtzeit']), 'kategorie_id' => $this->post('kategorie_id'), 'klasse_id' => $this->post('klasse_id'), ]; diff --git a/src/Models/Zahlungsauftrag.php b/src/Models/Zahlungsauftrag.php index 8a5253f..5726dd6 100644 --- a/src/Models/Zahlungsauftrag.php +++ b/src/Models/Zahlungsauftrag.php @@ -11,7 +11,9 @@ use Finanzmanager\Mandant; * Überweisungen: erfasst, geprüft, über FinTS abgesendet. * * Der Auftrag bleibt als Beleg stehen, auch wenn die Bank ihn ablehnt – mit der - * Meldung daneben. Erst wenn die Bank ihn angenommen hat, entsteht die + * Meldung daneben. Eine Überweisung geht als gewöhnliche Überweisung, + * Terminüberweisung oder Echtzeitüberweisung hinaus; die Echtzeitüberweisung + * behandelt SEPA als eigenen Geschäftsvorfall und kennt keinen Termin. Erst wenn die Bank ihn angenommen hat, entsteht die * zugehörige Buchung auf dem Auftragskonto; vorher wäre sie eine Behauptung. */ class Zahlungsauftrag @@ -63,12 +65,12 @@ class Zahlungsauftrag Database::execute( 'INSERT INTO zahlungsauftraege (benutzer_id, konto_id, zugang_id, empfaenger, iban, bic, betrag, waehrung, - verwendungszweck, ausfuehrung_am, kategorie_id, klasse_id, status) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', + verwendungszweck, ausfuehrung_am, echtzeit, kategorie_id, klasse_id, status) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', [ Mandant::id(), $f['konto_id'], $f['zugang_id'], $f['empfaenger'], $f['iban'], $f['bic'], $f['betrag'], $f['waehrung'], $f['verwendungszweck'], - $f['ausfuehrung_am'], $f['kategorie_id'], $f['klasse_id'], 'entwurf', + $f['ausfuehrung_am'], $f['echtzeit'], $f['kategorie_id'], $f['klasse_id'], 'entwurf', ] ); @@ -91,12 +93,12 @@ class Zahlungsauftrag Database::execute( 'UPDATE zahlungsauftraege SET konto_id = ?, zugang_id = ?, empfaenger = ?, iban = ?, bic = ?, betrag = ?, waehrung = ?, verwendungszweck = ?, ausfuehrung_am = ?, - kategorie_id = ?, klasse_id = ?, status = \'entwurf\', meldung = NULL + echtzeit = ?, kategorie_id = ?, klasse_id = ?, status = \'entwurf\', meldung = NULL WHERE id = ? AND benutzer_id = ?', [ $f['konto_id'], $f['zugang_id'], $f['empfaenger'], $f['iban'], $f['bic'], $f['betrag'], $f['waehrung'], $f['verwendungszweck'], $f['ausfuehrung_am'], - $f['kategorie_id'], $f['klasse_id'], $id, Mandant::id(), + $f['echtzeit'], $f['kategorie_id'], $f['klasse_id'], $id, Mandant::id(), ] ); } @@ -209,6 +211,15 @@ class Zahlungsauftrag throw new \InvalidArgumentException('Der Ausführungstermin liegt in der Vergangenheit.'); } + // Eine Echtzeitüberweisung ist binnen Sekunden beim Empfänger – einen + // Termin kennt sie nicht. Der heutige Tag ist ohnehin „sofort“. + $echtzeit = !empty($daten['echtzeit']); + if ($echtzeit && $termin !== '' && $termin > date('Y-m-d')) { + throw new \InvalidArgumentException( + 'Eine Echtzeitüberweisung wird sofort ausgeführt und kann keinen Ausführungstermin haben.' + ); + } + $id = static fn(mixed $wert): ?int => ($wert === null || $wert === '' || (int)$wert === 0) ? null : (int)$wert; @@ -223,7 +234,8 @@ class Zahlungsauftrag // 140 Zeichen sind das SEPA-Maß für den Verwendungszweck; was // darüber steht, würde die Bank ohnehin abschneiden. 'verwendungszweck' => mb_substr(trim((string)($daten['verwendungszweck'] ?? '')), 0, 140), - 'ausfuehrung_am' => $termin !== '' ? $termin : null, + 'ausfuehrung_am' => $termin !== '' && !$echtzeit ? $termin : null, + 'echtzeit' => $echtzeit ? 1 : 0, 'kategorie_id' => $id($daten['kategorie_id'] ?? null), 'klasse_id' => $id($daten['klasse_id'] ?? null), ]; diff --git a/src/Services/FinTsUeberweisung.php b/src/Services/FinTsUeberweisung.php index 091cb3a..416a728 100644 --- a/src/Services/FinTsUeberweisung.php +++ b/src/Services/FinTsUeberweisung.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace Finanzmanager\Services; use Fhp\Action\GetSEPAAccounts; +use Fhp\Action\SendSEPARealtimeTransfer; use Fhp\Action\SendSEPATransfer; use Fhp\BaseAction; use Fhp\FinTs; @@ -14,6 +15,8 @@ use Fhp\Model\VopConfirmationRequest; use Fhp\Model\VopVerificationResult; use Fhp\Options\Credentials; use Fhp\Options\FinTsOptions; +use Fhp\Segment\IPZ\HIIPZSv1; +use Fhp\Segment\IPZ\HIIPZSv2; use Fhp\Segment\SPA\HISPAS; use Finanzmanager\Auth; use Finanzmanager\Models\Bankprotokoll; @@ -40,6 +43,13 @@ use Finanzmanager\Models\Zahlungsauftrag; * überein, geht es ohne Rückfrage weiter zur TAN. Weicht der Name ab oder konnte * die Bank nicht prüfen, wird zuerst gefragt – mit dem Text, den die Bank dazu * mitliefert. + * + * **Echtzeitüberweisung.** Ist sie am Auftrag gewählt, geht der Auftrag als + * eigener Geschäftsvorfall HKIPZ hinaus statt als HKCCS – so will es das + * SEPA-Verfahren. Bietet die Bank das nicht an, scheitert der Auftrag mit einer + * klaren Meldung und bleibt stehen. Eine stille Umwandlung in eine gewöhnliche + * Überweisung erlauben wir der Bank nicht: Wer Echtzeit wählt, braucht das Geld + * sofort beim Empfänger und soll erfahren, wenn das nicht klappt. */ class FinTsUeberweisung { @@ -185,7 +195,8 @@ class FinTsUeberweisung $auftrag = Zahlungsauftrag::find($this->auftragId); if ($auftrag !== null) { $this->mitschnitt->vermerke(sprintf( - 'Überweisung %d: %s, IBAN %s, %s %s, Ausführung %s', + '%s %d: %s, IBAN %s, %s %s, Ausführung %s', + self::istEchtzeit($auftrag) ? 'Echtzeitüberweisung' : 'Überweisung', $this->auftragId, $auftrag['empfaenger'], $auftrag['iban'], @@ -307,17 +318,29 @@ class FinTsUeberweisung return $this->fehler('Das Auftragskonto ist nicht mehr lesbar. Bitte erneut senden.'); } - $formate = $this->sepaFormate($fints); - $schema = SepaXml::schemaWaehlen($formate); + $echtzeit = self::istEchtzeit($auftrag); + if ($echtzeit && $this->echtzeitParameter($fints) === null) { + return $this->fehler( + 'Die Bank bietet über FinTS keine Echtzeitüberweisung an. ' + . 'Bitte die Echtzeitüberweisung am Auftrag abwählen und erneut senden.' + ); + } + + $formate = $echtzeit ? $this->echtzeitFormate($fints) : $this->sepaFormate($fints); + $schema = SepaXml::schemaWaehlen($formate, $echtzeit); if ($schema === null) { return $this->fehler( - 'Die Bank nimmt nur SEPA-Formate an, die der Finanzmanager nicht erzeugt: ' + ($echtzeit ? 'Für Echtzeitüberweisungen nimmt die Bank' : 'Die Bank nimmt') + . ' nur SEPA-Formate an, die der Finanzmanager nicht erzeugt: ' . implode(', ', $formate) ); } $xml = SepaXml::ueberweisung($this->auftragsdaten($auftrag, $sepa), $schema); - $aktion = SendSEPATransfer::create($sepa, $xml); + $aktion = $echtzeit + // Keine Umwandlung in eine gewöhnliche Überweisung – siehe oben. + ? SendSEPARealtimeTransfer::create($sepa, $xml, false) + : SendSEPATransfer::create($sepa, $xml); $fints->execute($aktion); return $this->nachDemSenden($fints, $aktion); @@ -352,6 +375,42 @@ class FinTsUeberweisung : []; } + /** + * Die Bankparameter der Echtzeitüberweisung (Segment HIIPZS) – oder `null`, + * wenn die Bank sie über FinTS nicht anbietet. + */ + private function echtzeitParameter(FinTs $fints): HIIPZSv1|HIIPZSv2|null + { + try { + $hiipzs = $fints->getBpd()->getLatestSupportedParameters('HIIPZS'); + } catch (\Throwable) { + return null; + } + + return $hiipzs instanceof HIIPZSv1 || $hiipzs instanceof HIIPZSv2 ? $hiipzs : null; + } + + /** + * Die SEPA-Datenformate für Echtzeitüberweisungen. Nennt die Bank dafür + * keine eigenen, gelten die allgemeinen aus HISPAS. + * + * @return list + */ + private function echtzeitFormate(FinTs $fints): array + { + $formate = $this->echtzeitParameter($fints)?->parameter->getUnterstuetzteSEPADatenformate(); + + return $formate === null || $formate === [] + ? $this->sepaFormate($fints) + : array_values($formate); + } + + /** @param array $auftrag */ + private static function istEchtzeit(array $auftrag): bool + { + return (int)($auftrag['echtzeit'] ?? 0) === 1; + } + /** * Was nach dem Einreichen des Auftrags noch zu tun ist: auf den * Namensabgleich warten, ihn bestätigen lassen, die TAN einholen. @@ -428,6 +487,7 @@ class FinTsUeberweisung 'waehrung' => (string)$auftrag['waehrung'], 'verwendungszweck' => (string)$auftrag['verwendungszweck'], 'ausfuehrung_am' => $auftrag['ausfuehrung_am'], + 'echtzeit' => self::istEchtzeit($auftrag), ]; } @@ -488,7 +548,13 @@ class FinTsUeberweisung /** @return array */ private function angenommen(FinTs $fints): array { - Zahlungsauftrag::alsGesendetVermerken($this->auftragId, 'Von der Bank angenommen.'); + $auftrag = Zahlungsauftrag::find($this->auftragId); + $echtzeit = $auftrag !== null && self::istEchtzeit($auftrag); + + Zahlungsauftrag::alsGesendetVermerken( + $this->auftragId, + $echtzeit ? 'Von der Bank als Echtzeitüberweisung angenommen.' : 'Von der Bank angenommen.' + ); self::vergessen(); try { @@ -497,7 +563,12 @@ class FinTsUeberweisung // Der Dialog ist zu Ende; ein Fehler beim Abmelden ändert nichts. } - return ['status' => 'erfolg', 'meldung' => 'Die Überweisung wurde an die Bank übergeben.']; + return [ + 'status' => 'erfolg', + 'meldung' => $echtzeit + ? 'Die Echtzeitüberweisung wurde an die Bank übergeben.' + : 'Die Überweisung wurde an die Bank übergeben.', + ]; } /** diff --git a/src/Services/SepaXml.php b/src/Services/SepaXml.php index 1b3dae8..deb5906 100644 --- a/src/Services/SepaXml.php +++ b/src/Services/SepaXml.php @@ -18,6 +18,12 @@ namespace Finanzmanager\Services; * Fassungen im Aufbau gleich (Reihenfolge und Pflichtfelder stimmen überein), * sie unterscheiden sich nur im Namensraum. Welches Format die Bank annimmt, * steht im FinTS-Segment HISPAS – siehe `Services\FinTsUeberweisung`. + * + * **Echtzeitüberweisung.** SEPA behandelt sie als eigenes Verfahren (SCT Inst): + * In der Nachricht steht zusätzlich die Zahlungsart `LclInstrm/Cd = INST`, und + * die Bank bekommt sie über einen eigenen Geschäftsvorfall (HKIPZ). Das ältere + * DK-Format pain.001.003.03 kennt die Zahlungsart nicht – eine + * Echtzeitüberweisung gibt es deshalb nur im ISO-Format. */ class SepaXml { @@ -27,6 +33,12 @@ class SepaXml /** Die Formate, die hier erzeugt werden – in der Reihenfolge des Vorzugs. */ private const SCHEMATA = [self::SCHEMA_PAIN_001_001_03, self::SCHEMA_PAIN_001_003_03]; + /** Die Formate, in denen sich eine Echtzeitüberweisung ausdrücken lässt. */ + private const SCHEMATA_ECHTZEIT = [self::SCHEMA_PAIN_001_001_03]; + + /** Zahlungsart (Local Instrument) der Echtzeitüberweisung. */ + public const ECHTZEIT = 'INST'; + /** * Das Ausführungsdatum für „sofort“. * @@ -44,7 +56,7 @@ class SepaXml * absender_name: string, absender_iban: string, absender_bic?: string, * empfaenger_name: string, empfaenger_iban: string, empfaenger_bic?: string, * betrag: float, waehrung?: string, verwendungszweck?: string, - * ausfuehrung_am?: ?string + * ausfuehrung_am?: ?string, echtzeit?: bool * } $auftrag * @param string $schema Namensraum der Nachricht, siehe `schemaWaehlen()`. */ @@ -55,12 +67,20 @@ class SepaXml throw new \InvalidArgumentException('Der Betrag muss größer als null sein.'); } + $echtzeit = !empty($auftrag['echtzeit']); + if ($echtzeit && !in_array($schema, self::SCHEMATA_ECHTZEIT, true)) { + throw new \InvalidArgumentException('Eine Echtzeitüberweisung gibt es nur im Format pain.001.001.03.'); + } + // Eindeutige Kennungen: Die Bank lehnt eine Nachricht ab, deren Kennung // sie schon kennt. $kennung = 'FM' . date('YmdHis') . substr(bin2hex(random_bytes(4)), 0, 6); $zeitpunkt = date('Y-m-d\TH:i:s'); - $ausfuehrung = self::ausfuehrungstag((string)($auftrag['ausfuehrung_am'] ?? '')); + // Eine Echtzeitüberweisung geht immer sofort hinaus. + $ausfuehrung = $echtzeit + ? self::SOFORT + : self::ausfuehrungstag((string)($auftrag['ausfuehrung_am'] ?? '')); $waehrung = strtoupper((string)($auftrag['waehrung'] ?? 'EUR')); $zweck = self::text((string)($auftrag['verwendungszweck'] ?? ''), 140); @@ -100,6 +120,11 @@ class SepaXml $ebene = $dokument->createElement('SvcLvl'); $art->appendChild($ebene); self::kind($dokument, $ebene, 'Cd', 'SEPA'); + if ($echtzeit) { + $zahlungsart = $dokument->createElement('LclInstrm'); + $art->appendChild($zahlungsart); + self::kind($dokument, $zahlungsart, 'Cd', self::ECHTZEIT); + } self::kind($dokument, $sammler, 'ReqdExctnDt', $ausfuehrung); @@ -193,15 +218,18 @@ class SepaXml * Vorzugsformat; meldet sie nur Formate, die hier nicht erzeugt werden, * kommt `null` zurück. * + * Für eine Echtzeitüberweisung kommt nur das ISO-Format infrage; führt die + * Bank ausschließlich das DK-Format, ist das Ergebnis ebenfalls `null`. + * * @param list $unterstuetzt */ - public static function schemaWaehlen(array $unterstuetzt): ?string + public static function schemaWaehlen(array $unterstuetzt, bool $echtzeit = false): ?string { if ($unterstuetzt === []) { return self::SCHEMA_PAIN_001_001_03; } - foreach (self::SCHEMATA as $schema) { + foreach ($echtzeit ? self::SCHEMATA_ECHTZEIT : self::SCHEMATA as $schema) { foreach ($unterstuetzt as $format) { if (str_starts_with(trim($format), $schema)) { return $schema; diff --git a/src/Views/zahlungsverkehr/formular.php b/src/Views/zahlungsverkehr/formular.php index d90950a..69291c6 100644 --- a/src/Views/zahlungsverkehr/formular.php +++ b/src/Views/zahlungsverkehr/formular.php @@ -119,6 +119,17 @@ include __DIR__ . '/../layout/kopf.php'; + +

+ Das Geld ist binnen Sekunden beim Empfänger. Eine Echtzeitüberweisung wird sofort + ausgeführt und hat keinen Ausführungstermin. Bietet die Bank sie nicht an, wird der + Auftrag abgewiesen und bleibt als Entwurf stehen. +

+