fix: unblock onboarding after sync failure (#346)

## Summary
- mark failed banking sync jobs as error so onboarding can continue
- add EnableBanking HTTP timeouts to avoid worker hard timeouts
- add regression coverage for failed active sync jobs

## Tests
- vendor/bin/pint --dirty --format agent
- php artisan test --compact
tests/Feature/OpenBanking/SyncBankingConnectionJobTest.php
--filter='failed sync job marks active connection as error'
- php artisan test --compact
tests/Feature/Onboarding/OnboardingSyncStatusTest.php --filter='returns
pending false when unsynced connection has an error status'
This commit is contained in:
Víctor Falcón 2026-05-04 12:52:21 +01:00 committed by GitHub
parent 592fb20059
commit 70f3897b55
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 40 additions and 0 deletions

View File

@ -33,6 +33,8 @@ class SyncBankingConnectionJob implements ShouldBeUnique, ShouldQueue
public int $backoff = 30;
public int $timeout = 120;
/**
* Maximum number of scheduled sync cycles that will auto-retry
* a connection in Error state before requiring manual intervention.
@ -143,6 +145,25 @@ class SyncBankingConnectionJob implements ShouldBeUnique, ShouldQueue
/**
* Handle permanent errors (auth failures) that should not be retried.
*/
public function failed(?\Throwable $e): void
{
$connection = $this->bankingConnection->fresh();
if (! $connection || $connection->status === BankingConnectionStatus::Error) {
return;
}
if (! $this->isSyncableStatus($connection)) {
return;
}
$connection->update([
'status' => BankingConnectionStatus::Error,
'error_message' => $e ? $this->friendlyErrorMessage($e) : __('An unexpected error occurred during sync. Please try again later.'),
'consecutive_sync_failures' => $connection->consecutive_sync_failures + 1,
]);
}
private function handlePermanentError(BankingConnection $connection, \Throwable $e): void
{
$connection->update([

View File

@ -138,6 +138,8 @@ class EnableBankingProvider implements BankingProviderInterface
private function client(): PendingRequest
{
return Http::baseUrl(self::BASE_URL)
->timeout(20)
->connectTimeout(5)
->withToken($this->generateJwt())
->acceptJson()
->throw(function ($response, $exception) {

View File

@ -1260,6 +1260,23 @@ test('sends auth failed email immediately for binance 403 error', function () {
});
});
test('failed sync job marks active connection as error so onboarding can continue', function () {
$connection = BankingConnection::factory()->create([
'status' => BankingConnectionStatus::Active,
'last_synced_at' => null,
'consecutive_sync_failures' => 0,
]);
$job = new SyncBankingConnectionJob($connection);
$job->failed(new RuntimeException('Provider request timed out.'));
$connection->refresh();
expect($connection->status)->toBe(BankingConnectionStatus::Error);
expect($connection->last_synced_at)->toBeNull();
expect($connection->error_message)->toBe('An unexpected error occurred during sync. Please try again later.');
expect($connection->consecutive_sync_failures)->toBe(1);
});
test('rate limit error does not set connection status to error', function () {
$user = User::factory()->onboarded()->create();
$connection = BankingConnection::factory()->create([