fix(banking): treat EnableBanking upstream 5xx as transient, not reportable
getTransactions/getBalances classified 401/400/422/ASPSP errors but let any
other RequestException fall through to a raw re-throw, so an upstream 500
('Internal server error') from EnableBanking or the ASPSP propagated and was
report()ed as an app error (Sentry PHP-LARAVEL-3J: 36 events / 3 users).
A 5xx is a transient server-side failure — the same class as a
ConnectionException, which is already wrapped as TransientBankingProviderException
(logged at warning, retried, self-healing). Classify any upstream 5xx the same
way in both getTransactions and getBalances via a new isTransientServerError()
helper, so provider outages retry/self-heal instead of paging Sentry.
Non-5xx paths (401/400/422/ASPSP + genuine 4xx client errors) are unchanged and
stay reportable.
This commit is contained in:
parent
9e237802f6
commit
d35bc07c05
|
|
@ -127,6 +127,15 @@ class EnableBankingProvider implements BankingProviderInterface
|
|||
);
|
||||
}
|
||||
|
||||
if ($this->isTransientServerError($e)) {
|
||||
throw new TransientBankingProviderException(
|
||||
'EnableBanking returned a server error while fetching account transactions.',
|
||||
provider: 'enablebanking',
|
||||
statusCode: $e->response->status(),
|
||||
previous: $e,
|
||||
);
|
||||
}
|
||||
|
||||
if (! $this->isAspspError($e)) {
|
||||
throw $e;
|
||||
}
|
||||
|
|
@ -178,6 +187,15 @@ class EnableBankingProvider implements BankingProviderInterface
|
|||
);
|
||||
}
|
||||
|
||||
if ($this->isTransientServerError($e)) {
|
||||
throw new TransientBankingProviderException(
|
||||
'EnableBanking returned a server error while fetching account balances.',
|
||||
provider: 'enablebanking',
|
||||
statusCode: $e->response->status(),
|
||||
previous: $e,
|
||||
);
|
||||
}
|
||||
|
||||
if (! $this->isAspspError($e)) {
|
||||
throw $e;
|
||||
}
|
||||
|
|
@ -230,6 +248,14 @@ class EnableBankingProvider implements BankingProviderInterface
|
|||
&& ($body['error'] ?? null) === 'ASPSP_ERROR';
|
||||
}
|
||||
|
||||
private function isTransientServerError(RequestException $e): bool
|
||||
{
|
||||
// Any upstream 5xx (EnableBanking itself or the ASPSP behind it) is a
|
||||
// transient server-side failure — same class as a ConnectionException,
|
||||
// so retry/self-heal rather than report it as an app error.
|
||||
return $e->response->status() >= 500;
|
||||
}
|
||||
|
||||
private function isExpiredSession(RequestException $e): bool
|
||||
{
|
||||
$body = $this->errorBody($e);
|
||||
|
|
|
|||
|
|
@ -59,6 +59,54 @@ test('getTransactions wraps connection failures as non-reportable transient erro
|
|||
test()->fail('Expected transient banking provider exception.');
|
||||
});
|
||||
|
||||
test('getTransactions wraps an upstream 500 as a non-reportable transient error', function () {
|
||||
Http::fake([
|
||||
'api.enablebanking.com/accounts/ext-123/transactions*' => Http::response([
|
||||
'code' => 500,
|
||||
'message' => 'Internal server error',
|
||||
], 500),
|
||||
]);
|
||||
|
||||
$provider = enableBankingProviderForTest();
|
||||
|
||||
try {
|
||||
$provider->getTransactions('ext-123', now()->toDateString(), now()->toDateString());
|
||||
} catch (TransientBankingProviderException $e) {
|
||||
expect($e)->toBeInstanceOf(ShouldntReport::class)
|
||||
->and($e->provider)->toBe('enablebanking')
|
||||
->and($e->statusCode)->toBe(500)
|
||||
->and($e->getPrevious())->toBeInstanceOf(RequestException::class);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
test()->fail('Expected transient banking provider exception.');
|
||||
});
|
||||
|
||||
test('getBalances wraps an upstream 500 as a non-reportable transient error', function () {
|
||||
Http::fake([
|
||||
'api.enablebanking.com/accounts/ext-123/balances*' => Http::response([
|
||||
'code' => 500,
|
||||
'message' => 'Internal server error',
|
||||
], 500),
|
||||
]);
|
||||
|
||||
$provider = enableBankingProviderForTest();
|
||||
|
||||
try {
|
||||
$provider->getBalances('ext-123');
|
||||
} catch (TransientBankingProviderException $e) {
|
||||
expect($e)->toBeInstanceOf(ShouldntReport::class)
|
||||
->and($e->provider)->toBe('enablebanking')
|
||||
->and($e->statusCode)->toBe(500)
|
||||
->and($e->getPrevious())->toBeInstanceOf(RequestException::class);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
test()->fail('Expected transient banking provider exception.');
|
||||
});
|
||||
|
||||
test('getTransactions wraps an expired session 401 as a non-reportable expired session error', function () {
|
||||
Http::fake([
|
||||
'api.enablebanking.com/accounts/ext-123/transactions*' => Http::response([
|
||||
|
|
|
|||
Loading…
Reference in New Issue