dolibarr.bankimport/vendor/nemiah/php-fints/lib/Fhp/Protocol/ServerException.php
data 014a943f78 feat: HKEKA-Implementierung, PDF-Bugfixes, Sortierung, Umsatz-Umbenennung
- HKEKA v3/v4/v5 Segmente fuer phpFinTS implementiert (VR Bank unterstuetzt kein HKEKP)
- GetElectronicStatement Action mit Base64-Erkennung und Quittungscode
- PDF-Deduplizierung per MD5 (Bank sendet identische Saldenmitteilungen)
- Saldenmitteilungen ohne Auszugsnummer werden uebersprungen
- Datums-Validierung: 30.02. (Bank-Konvention) wird auf 28.02. korrigiert
- Numerische Sortierung fuer statement_number (CAST statt String-Sort)
- Jahr-Filter: statement_year=0 ausgeschlossen
- Menue/Button: "Kontoauszuege" -> "Umsaetze" (statements.php zeigt MT940, nicht PDFs)
- Redirect nach FinTS-Abruf auf aktuelles Jahr statt year=0

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 07:10:59 +01:00

181 lines
6.2 KiB
PHP
Executable file

<?php
/** @noinspection PhpUnused */
namespace Fhp\Protocol;
use Fhp\Segment\HIRMG\HIRMGv2;
use Fhp\Segment\HIRMS\HIRMSv2;
use Fhp\Segment\HIRMS\Rueckmeldung;
use Fhp\Segment\HIRMS\Rueckmeldungscode;
/**
* Thrown when the server response with a response code that indicates an error when executing the request.
*/
class ServerException extends \Exception
{
/** @var Rueckmeldung[] */
private $errors;
/** @var Rueckmeldung[] */
private $warnings;
/** @var string[] Selected segments from the request that can be useful in debugging. */
private $requestSegments;
/** @var Message */
private $request;
/** @var Message */
private $response;
/**
* @param Rueckmeldung[] $errors
* @param Rueckmeldung[] $warnings
* @param string[] $requestSegments (already serialized and sanitized)
*/
public function __construct(array $errors, array $warnings, array $requestSegments, Message $request, Message $response)
{
$this->errors = $errors;
$this->warnings = $warnings;
$this->requestSegments = $requestSegments;
$this->request = $request;
$this->response = $response;
$errorsStr = count($errors) === 0 ? '' : "FinTS errors:\n" . implode("\n", $errors);
$warningsStr = count($warnings) === 0 ? '' : "FinTS warnings:\n" . implode("\n", $warnings);
$segmentsStr = count($requestSegments) === 0 ? '' : "Request segments:\n" . implode("\n", $requestSegments);
parent::__construct(implode("\n", array_filter([$errorsStr, $warningsStr, $segmentsStr])));
}
/**
* @return Rueckmeldung[]
*/
public function getErrors()
{
return $this->errors;
}
/**
* @return Rueckmeldung[]
*/
public function getWarnings()
{
return $this->warnings;
}
/**
* @return string[]
*/
public function getRequestSegments()
{
return $this->requestSegments;
}
/**
* @param int $code A Rueckmeldungscode to look for (should be an error code, i.e. 9xxx).
* @return bool Whether an error with this code is present.
*/
public function hasError(int $code): bool
{
foreach ($this->errors as $error) {
if ($error->rueckmeldungscode === $code) {
return true;
}
}
return false;
}
/**
* @param int $code A Rueckmeldungscode to look for (should be a warning code, i.e. 3xxx).
* @return bool Whether a warning with this code is present.
*/
public function hasWarning(int $code): bool
{
foreach ($this->warnings as $warning) {
if ($warning->rueckmeldungscode === $code) {
return true;
}
}
return false;
}
/**
* @return bool True if the {@link Credentials} used to make this request are wrong. If this returns true, the
* application should ask the user to re-enter the credentials before making any further requests to the bank.
*/
public function indicatesBadLoginData(): bool
{
return $this->hasError(Rueckmeldungscode::PIN_UNGUELTIG);
}
/**
* @return bool If the error indicates that the account (bank account and/or online banking access) has been locked,
* usually due to suspicious activity or failed login attempts. If this returns true, the application should
* refrain from logging in / using that account in any automated way before getting confirmation from the user
* that it has been unlocked.
*/
public function indicatesLocked(): bool
{
return $this->hasError(Rueckmeldungscode::PIN_GESPERRT)
|| $this->hasError(Rueckmeldungscode::TEILNEHMER_GESPERRT)
|| $this->hasWarning(Rueckmeldungscode::PIN_VORLAEUFIG_GESPERRT)
|| $this->hasWarning(Rueckmeldungscode::ZUGANG_VORLAEUFIG_GESPERRT)
|| $this->hasError(Rueckmeldungscode::ZUGANG_GESPERRT);
}
/**
* @return bool True if the provided TAN is invalid (including entirely wrong, used for another transaction already,
* or exceeded its expiration time).
*/
public function indicatesBadTan(): bool
{
return $this->hasError(Rueckmeldungscode::TAN_UNGUELTIG)
|| $this->hasError(Rueckmeldungscode::TAN_BEREITS_VERBRAUCHT)
|| $this->hasError(Rueckmeldungscode::ZEITUEBERSCHREITUNG_IM_ZWEI_SCHRITT_VERFAHREN);
}
/**
* @param Message $response A response received from the server.
* @param Message $request The original requests, from which this function pulls the segments that errors
* refer to, for ease of debugging.
* @throws ServerException In case the response indicates an error.
*/
public static function detectAndThrowErrors(Message $response, Message $request)
{
/** @var HIRMGv2[]|HIRMSv2[] $segments */
$segments = array_merge(
[$response->requireSegment(HIRMGv2::class)],
$response->findSegments(HIRMSv2::class)
);
$errors = [];
$warnings = [];
$requestSegments = [];
foreach ($segments as $segment) {
$referenceSegment = $segment->segmentkopf->bezugselement;
foreach ($segment->rueckmeldung as $rueckmeldung) {
$rueckmeldung->referenceSegment = $referenceSegment;
if (Rueckmeldungscode::isError($rueckmeldung->rueckmeldungscode)) {
$errors[] = $rueckmeldung;
if ($referenceSegment !== null) {
$requestSegment = $request->findSegmentByNumber($referenceSegment);
if ($requestSegment !== null) {
$requestSegments[] = $requestSegment;
}
}
} elseif (Rueckmeldungscode::isWarning($rueckmeldung->rueckmeldungscode)) {
$warnings[] = $rueckmeldung;
}
}
}
if (count($errors) > 0) {
throw new ServerException($errors, $warnings, $requestSegments, $request, $response);
}
}
/**
* The response that the bank sent, that contained the errors
*/
public function getResponse(): Message
{
return $this->response;
}
}