-
Notifications
You must be signed in to change notification settings - Fork 3
Hotfix 4.4.4 - FinTS bank import review fixes #332
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 13 commits
892ce81
42e2d8c
779c505
9e78219
e83f024
3eb77ed
927f0c1
1918ff9
fb3085e
803d31d
84698c8
fb45df5
08f68be
cf38557
5d81891
0e90269
e7c6025
78521dc
fdb7769
d202f7a
f7e868b
dfaa1d3
d3f0345
75a66b8
ad6e7c6
49c3128
57fad9c
6f0bea4
fde7da9
2c63b67
01e9bfa
35b9c94
2015b68
fc5e0e5
e929d0f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -135,9 +135,12 @@ public function logout(): bool | |
| $this->finTs->close(); // logout @ server | ||
| $this->forgetCachedCredentials($this->credentialId); | ||
| HTMLPageRenderer::addFlash(BT::TYPE_SUCCESS, 'Erfolgreich ausgeloggt'); | ||
| } catch (ServerException $e) { | ||
| } catch (CurlException|ServerException|UnexpectedResponseException $e) { | ||
| // A logout that cannot reach the bank is not worth an error page - the local | ||
| // session data is dropped either way below. | ||
| $this->logger->error('Logout failed', ['exception' => $e]); | ||
| HTMLPageRenderer::addFlash(BT::TYPE_DANGER, 'Logout fehlgeschlagen', $e->getMessage()); | ||
| $this->forgetCachedCredentials($this->credentialId); | ||
|
|
||
| return false; | ||
| } | ||
|
|
@@ -156,7 +159,7 @@ public function getUserTanModes(): array | |
| try { | ||
| $this->logger->info('Fetch TAN Modes', ['credId' => $this->credentialId]); | ||
| $tanModes = $this->finTs->getTanModes(); | ||
| } catch (CurlException|ServerException $e) { | ||
| } catch (CurlException|ServerException|UnexpectedResponseException $e) { | ||
| $this->logger->info('Fetch TAN Modes failed', ['exception' => $e]); | ||
| ErrorHandler::handleException($e, 'TAN Modi können nicht empfangen werden - Verbringung zur Bank gestört'); | ||
| } | ||
|
|
@@ -185,7 +188,7 @@ public function getTanMedias(int $tanModeId): array | |
| } | ||
|
|
||
| return $tanMediumNames; | ||
| } catch (CurlException|ServerException $e) { | ||
| } catch (CurlException|ServerException|UnexpectedResponseException $e) { | ||
| $this->logger->error('Tan kann nicht empfangen werden - Verbindung zur Bank gestört', ['exception' => $e]); | ||
| ErrorHandler::handleException($e, 'TAN Modi können nicht empfangen werden - Verbindung zur Bank gestört'); | ||
| } | ||
|
|
@@ -284,6 +287,7 @@ private function saveAction(?BaseAction $action = null): void | |
| } else { | ||
| // delete it from cache otherwise | ||
| $this->setCache('action', null); | ||
| $this->setCache('action-scope', null); | ||
| } | ||
| // save persist in cache | ||
| $this->setCache('persist', $this->finTs->persist()); | ||
|
|
@@ -342,11 +346,24 @@ public static function load(int $credentialId): self | |
|
|
||
| $credentials = Credentials::create($username, self::getPassword($credentialId)); | ||
|
|
||
| if (trim((string) FINTS_REGNR) === '') { | ||
| // FinTsOptions::validate() would raise "Product name required!" as an | ||
| // uncaught InvalidArgumentException, i.e. an error page with no clue. | ||
| throw new LegacyDieException( | ||
| 500, | ||
| 'Für den Bankzugang fehlt die FinTS-Registrierungsnummer (FINTS_REG_NR in der Konfiguration). '. | ||
| 'Bitte wende dich an die Administration.' | ||
| ); | ||
| } | ||
|
|
||
| $options = new FinTsOptions; | ||
| $options->url = $res['bank.url']; | ||
| $options->bankCode = $res['bank.blz']; | ||
| $options->productName = FINTS_REGNR; | ||
| $options->productVersion = InstalledVersions::getRootPackage()['version'].DEV ? '-dev' : ''; | ||
| // The concatenation binds tighter than ?:, so this used to evaluate as | ||
| // (('4.4.3'.DEV) ? '-dev' : '') - an always-truthy string, which reported the | ||
| // version to the bank as literally "-dev" regardless of what is installed. | ||
| $options->productVersion = InstalledVersions::getRootPackage()['version'].(DEV ? '-dev' : ''); | ||
|
|
||
| $tanModeInt = null; | ||
| if ($res['tan_mode'] !== 'null' && ! is_null($res['tan_mode'])) { | ||
|
|
@@ -374,7 +391,7 @@ private function execute(BaseAction $action): void | |
| // TODO decoupled tan stuff here | ||
| throw new NeedsTanException($action); | ||
| } | ||
| } catch (CurlException|ServerException $e) { | ||
| } catch (CurlException|ServerException|UnexpectedResponseException $e) { | ||
| $this->logger->error('Aktion nicht ausgeführt', ['exception' => $e]); | ||
| ErrorHandler::handleException($e, 'Verbindung zur Bank gestört - Aktion nicht ausgeführt'); | ||
| } | ||
|
|
@@ -423,10 +440,27 @@ public function submitTan(string $tan): bool | |
| HTMLPageRenderer::addFlash(BT::TYPE_DANGER, 'Konnte keine Verbindung zum Server aufbauen', $e->getMessage()); | ||
|
|
||
| return false; | ||
| } catch (ServerException $e) { | ||
| } catch (ServerException|UnexpectedResponseException $e) { | ||
| // A rejected TAN arrives as UnexpectedResponseException("Bank has not accepted | ||
| // TAN: ...") from FinTs::submitTan(). That extends RuntimeException, while | ||
| // ServerException extends Exception - two unrelated hierarchies, so catching | ||
| // only the latter turned a mistyped TAN into an error page. | ||
| $this->logger->error('Wrong Tan', ['exception' => $e]); | ||
| HTMLPageRenderer::addFlash(BT::TYPE_DANGER, 'TAN nicht akzeptiert', $e->getMessage()); | ||
|
|
||
| return false; | ||
| } catch (InvalidArgumentException $e) { | ||
| // The library refuses to take a TAN for a decoupled TAN mode (confirmation | ||
| // happens in the banking app instead). Supporting that properly is its own | ||
| // work package; until then, say so rather than showing an error page. | ||
| $this->logger->error('TAN submission rejected by the library', ['exception' => $e]); | ||
| HTMLPageRenderer::addFlash( | ||
| BT::TYPE_DANGER, | ||
| 'Dieses TAN-Verfahren kann StuFiS derzeit nicht abschließen', | ||
| 'Bei Freigabe-Verfahren ohne TAN-Eingabe (z. B. pushTAN-Freigabe in der Banking-App) '. | ||
| 'fehlt die Unterstützung noch. Bitte wähle ein TAN-Verfahren mit TAN-Eingabe.' | ||
| ); | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
|
|
@@ -447,7 +481,7 @@ public function setTanMode(int $tanModeId, ?string $tanMediumName = null): bool | |
| } | ||
| $this->saveAction(); | ||
| $this->logger->info('Set TAN Mode', ['credId' => $this->credentialId, 'tanMode' => $tanModeId, 'tanMedium' => $tanMediumName]); | ||
| } catch (CurlException|ServerException $e) { | ||
| } catch (CurlException|ServerException|UnexpectedResponseException $e) { | ||
| $this->logger->error('BPB fetch failed', ['exception' => $e]); | ||
| ErrorHandler::handleException($e, 'Kann keine Verbindung zum Bank Server aufbauen', 'BPB fetch failed'); | ||
| } | ||
|
|
@@ -466,25 +500,45 @@ public function setTanMode(int $tanModeId, ?string $tanMediumName = null): bool | |
|
|
||
| public function getStatements(string $iban, DateTime $start, DateTime $end): StatementOfAccount | ||
| { | ||
| // What a pending statement request was created for. While it waits for a TAN the | ||
| // action sits in the session, and it used to be resumed on nothing but its type: | ||
| // asking for account A, then opening account B's import URL and entering the TAN | ||
| // there returned A's statements, which the caller then stored under B's konto_id. | ||
| $scope = $this->statementScope($iban, $start, $end); | ||
| $action = $this->resumableAction(); | ||
| if ($action instanceof GetStatementOfAccount) { | ||
| if ($action->isDone()) { | ||
| $this->saveAction(); | ||
| if ($this->getCache('action-scope') === $scope) { | ||
| if ($action->isDone()) { | ||
| $this->saveAction(); | ||
|
|
||
| return $action->getStatement(); | ||
| return $action->getStatement(); | ||
| } | ||
| throw new NeedsTanException($action); | ||
| } | ||
| throw new NeedsTanException($action); | ||
| $this->logger->warning('Discarding a pending statement request made for something else', [ | ||
| 'credId' => $this->credentialId, | ||
| 'requested' => $scope, | ||
| ]); | ||
| $this->saveAction(); // drops the stale action and its scope | ||
| } | ||
| $this->logger->info('Start Get SEPA Statements', ['credId' => $this->credentialId, $iban]); | ||
| $account = $this->getSepaAccount($iban); | ||
| $account = clone $account; // weird fix, without the clone the session var is changed to DateTime object | ||
| // might be a bug in fints TODO: see if minimal example with the same bug can be found | ||
| $action = GetStatementOfAccount::create($account, $start, $end); | ||
| // Has to be recorded before execute(), which caches the action and then throws | ||
| // NeedsTanException, ending this request. | ||
| $this->setCache('action-scope', $scope); | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is this ever read?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🤖 Claude Code (posting via @lukas-staab's account) Written, yes — the logger level is So in
Separate question your comment raises: that whole |
||
| $this->execute($action); | ||
|
|
||
| return $action->getStatement(); | ||
| } | ||
|
|
||
| private function statementScope(string $iban, DateTime $start, DateTime $end): string | ||
| { | ||
| return $iban.'|'.$start->format('Y-m-d').'|'.$end->format('Y-m-d'); | ||
| } | ||
|
|
||
| public function getLogger(): LoggerInterface | ||
| { | ||
| return $this->finTs->getLogger(); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use the one out of the config instead
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤖 Claude Code (posting via @lukas-staab's account)
Done —
config('stufis.version')now, incf38557f.Worth noting it was wrong twice over:
getRootPackage()['version']returns the normalised version, so even with the precedence fixed the bank would have been told4.4.4.0. The config value goes throughgetPrettyVersion()and gives4.4.4. Verified locally:The
Composer\InstalledVersionsimport is gone with it.