Fix n.n.: Echtzeitüberweisung – offener Status (3045) ist kein Fehler
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
38ac132752
commit
0a8b1f506c
4 changed files with 101 additions and 5 deletions
|
|
@ -1,5 +1,12 @@
|
|||
# Änderungen
|
||||
|
||||
## Echtzeitüberweisung: offener Status ist kein Fehler (01.10.2026)
|
||||
|
||||
- Quittiert die Bank die TAN mit 3045 („noch keine Rückmeldung zum Status“), meldete php-fints „Bank did not confirm SEPATransfer execution“, obwohl der Auftrag bei der Bank liegt. Jetzt gilt der Auftrag als gesendet (gebucht), die Meldung weist darauf hin, den Kontoumsatz zu prüfen und nicht erneut zu senden.
|
||||
- `Services/FinTsEchtzeitUeberweisung` erweitert `SendSEPARealtimeTransfer` um dieses Merkmal.
|
||||
- Eine Statusabfrage bei der Bank (Nachfragen) ist noch offen: php-fints kennt dafür weder `HKIPS` noch `HKTAN` S für Nicht-Decoupled-Verfahren.
|
||||
- Test: `tests/54_EchtzeitStatusOffenTest.php` (Antwort aus einem echten Lauf).
|
||||
|
||||
## Überweisung erfassen: QR-Code einlesen (01.10.2026)
|
||||
|
||||
- Neben „Empfänger“ steht ein Bereich, auf den sich ein Bild (z. B. Rechnungs-Screenshot) ziehen lässt. Ein Klick öffnet einen Dialog zum Einfügen aus der Zwischenablage (Strg+V) oder zur Dateiauswahl.
|
||||
|
|
|
|||
42
src/Services/FinTsEchtzeitUeberweisung.php
Normal file
42
src/Services/FinTsEchtzeitUeberweisung.php
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Finanzmanager\Services;
|
||||
|
||||
use Fhp\Action\SendSEPARealtimeTransfer;
|
||||
use Fhp\Protocol\Message;
|
||||
use Fhp\Protocol\UnexpectedResponseException;
|
||||
|
||||
/**
|
||||
* Echtzeitüberweisung, die eine noch offene Statusmeldung der Bank übersteht.
|
||||
*
|
||||
* Antwortet die Gegenstelle nicht rechtzeitig, quittiert die Bank die TAN mit
|
||||
* 3045 („noch keine Rückmeldung zum Status“) statt mit „entgegengenommen“ oder
|
||||
* „ausgeführt“. php-fints wirft dann eine Ausnahme, obwohl der Auftrag bei der
|
||||
* Bank liegt. Hier bleibt nur das Merkmal `statusOffen` zurück.
|
||||
*/
|
||||
class FinTsEchtzeitUeberweisung extends SendSEPARealtimeTransfer
|
||||
{
|
||||
/** Rückmeldecode der Bank: Status der Echtzeitüberweisung noch unbekannt. */
|
||||
public const STATUS_OFFEN = 3045;
|
||||
|
||||
private bool $statusOffen = false;
|
||||
|
||||
public function istStatusOffen(): bool
|
||||
{
|
||||
return $this->statusOffen;
|
||||
}
|
||||
|
||||
public function processResponse(Message $response)
|
||||
{
|
||||
try {
|
||||
parent::processResponse($response);
|
||||
} catch (UnexpectedResponseException $e) {
|
||||
if ($response->findRueckmeldungen(self::STATUS_OFFEN) === []) {
|
||||
throw $e;
|
||||
}
|
||||
$this->statusOffen = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,6 @@ 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;
|
||||
|
|
@ -343,7 +342,7 @@ class FinTsUeberweisung
|
|||
$xml = SepaXml::ueberweisung($this->auftragsdaten($auftrag, $sepa), $schema);
|
||||
$aktion = $echtzeit
|
||||
// Keine Umwandlung in eine gewöhnliche Überweisung – siehe oben.
|
||||
? SendSEPARealtimeTransfer::create($sepa, $xml, false)
|
||||
? FinTsEchtzeitUeberweisung::create($sepa, $xml, false)
|
||||
: SendSEPATransfer::create($sepa, $xml);
|
||||
|
||||
try {
|
||||
|
|
@ -468,7 +467,7 @@ class FinTsUeberweisung
|
|||
return $this->tanNoetig($fints, $aktion, 'Überweisung', 'senden');
|
||||
}
|
||||
|
||||
return $this->angenommen($fints);
|
||||
return $this->angenommen($fints, $aktion);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -575,14 +574,21 @@ class FinTsUeberweisung
|
|||
}
|
||||
|
||||
/** @return array<string,mixed> */
|
||||
private function angenommen(FinTs $fints): array
|
||||
private function angenommen(FinTs $fints, ?BaseAction $aktion = null): array
|
||||
{
|
||||
$auftrag = Zahlungsauftrag::find($this->auftragId);
|
||||
$echtzeit = $auftrag !== null && self::istEchtzeit($auftrag);
|
||||
|
||||
// Die Bank hat die TAN angenommen, den Ausgang aber noch nicht gemeldet.
|
||||
// Der Auftrag gilt als gesendet – ein erneutes Senden wäre eine
|
||||
// Doppelzahlung –, der Nutzer prüft den Umsatz.
|
||||
$statusOffen = $aktion instanceof FinTsEchtzeitUeberweisung && $aktion->istStatusOffen();
|
||||
|
||||
Zahlungsauftrag::alsGesendetVermerken(
|
||||
$this->auftragId,
|
||||
$echtzeit ? 'Von der Bank als Echtzeitüberweisung angenommen.' : 'Von der Bank angenommen.'
|
||||
$statusOffen
|
||||
? 'Die Bank hat die Echtzeitüberweisung entgegengenommen, den Ausgang aber noch nicht bestätigt.'
|
||||
: ($echtzeit ? 'Von der Bank als Echtzeitüberweisung angenommen.' : 'Von der Bank angenommen.')
|
||||
);
|
||||
self::vergessen();
|
||||
|
||||
|
|
@ -592,6 +598,14 @@ class FinTsUeberweisung
|
|||
// Der Dialog ist zu Ende; ein Fehler beim Abmelden ändert nichts.
|
||||
}
|
||||
|
||||
if ($statusOffen) {
|
||||
return [
|
||||
'status' => 'erfolg',
|
||||
'meldung' => 'Die Bank hat die Echtzeitüberweisung entgegengenommen, den Ausgang aber noch nicht bestätigt. '
|
||||
. 'Bitte im Kontoumsatz prüfen, ob sie ausgeführt wurde, und nicht erneut senden.',
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'status' => 'erfolg',
|
||||
'meldung' => $echtzeit
|
||||
|
|
|
|||
33
tests/54_EchtzeitStatusOffenTest.php
Normal file
33
tests/54_EchtzeitStatusOffenTest.php
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Fhp\Protocol\Message;
|
||||
use Finanzmanager\Services\FinTsEchtzeitUeberweisung;
|
||||
|
||||
/**
|
||||
* Echtzeitüberweisung mit offenem Status (Rückmeldung 3045).
|
||||
*
|
||||
* Die Antwort stammt aus einem echten Lauf: Die Bank nimmt die TAN an, meldet
|
||||
* aber nur „noch keine Rückmeldung zum Status“. Das ist kein Fehler – der
|
||||
* Auftrag liegt bei der Bank.
|
||||
*/
|
||||
class EchtzeitStatusOffenTest extends TestCase
|
||||
{
|
||||
protected string $testName = 'Test 54: Echtzeitüberweisung – Status offen';
|
||||
|
||||
private const ANTWORT = <<<'FINTS'
|
||||
HNHBK:1:3+000000000620+300+YWPfOjdl96ABAACrZvYFQwSCCgQA+5+YWPfOjdl96ABAACrZvYFQwSCCgQA:5'HNVSK:998:3+PIN:2+998+1+2::6lrfOjdl96ABAACrZvYFQwSCCgQA+1:20261001:143605+2:2:13:@8@00000000:5:1+280:20041111:PRIVATE_:V:0:0+0'HNVSD:999:1+@374@HNSHK:2:4+PIN:2+902+7079006+1+1+2::6lrfOjdl96ABAACrZvYFQwSCCgQA+1+1:20261001:143605+1:999:1+6:10:19+280:20041111:PRIVATE_:S:0:0'HIRMG:3:2+3060::Bitte beachten Sie die enthaltenen Warnungen/Hinweise.'HIRMS:4:2:3+3045::Wir haben leider noch keine Rückmeldung zum Status ihrer Überweisung.'HITAN:5:6:3+2++47017475'HIIPZ:6:1:3+C6592DB8C24247C493F1B27377EC63DA'HNSHA:7:2+7079006''HNHBS:8:1+5'
|
||||
FINTS;
|
||||
|
||||
public function run(): void
|
||||
{
|
||||
$antwort = Message::parse(mb_convert_encoding(self::ANTWORT, 'ISO-8859-1', 'UTF-8'));
|
||||
$aktion = new FinTsEchtzeitUeberweisung();
|
||||
|
||||
$this->assert(!$aktion->istStatusOffen(), 'Ohne Antwort ist der Status nicht offen');
|
||||
|
||||
$aktion->processResponse($antwort);
|
||||
$this->assert($aktion->istStatusOffen(), 'Rückmeldung 3045 wird als „Status offen“ erkannt, nicht als Fehler');
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue