diff --git a/app/Jobs/SyncBankingConnectionJob.php b/app/Jobs/SyncBankingConnectionJob.php index cfd2b91c..dac9af20 100644 --- a/app/Jobs/SyncBankingConnectionJob.php +++ b/app/Jobs/SyncBankingConnectionJob.php @@ -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([ diff --git a/app/Services/Banking/EnableBankingProvider.php b/app/Services/Banking/EnableBankingProvider.php index 0cfc5a20..87359173 100644 --- a/app/Services/Banking/EnableBankingProvider.php +++ b/app/Services/Banking/EnableBankingProvider.php @@ -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) { diff --git a/tests/Feature/OpenBanking/SyncBankingConnectionJobTest.php b/tests/Feature/OpenBanking/SyncBankingConnectionJobTest.php index cbbc4135..c06349e1 100644 --- a/tests/Feature/OpenBanking/SyncBankingConnectionJobTest.php +++ b/tests/Feature/OpenBanking/SyncBankingConnectionJobTest.php @@ -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([