fix(banking): treat 429 rate limit as transient, skip error status on sync (#224)
## Summary - A `429 ASPSP_RATE_LIMIT_EXCEEDED` response from the bank's API was incorrectly marking connections as `status=error`, blocking all future syncs. - Rate limit errors are transient — the connection is still valid and should be retried on the next scheduled sync. - Added `isRateLimitError()` check in the `catch` block of `SyncBankingConnectionJob`: on 429, the job returns early without updating the connection status or error message.
This commit is contained in:
parent
d5735b59c7
commit
5b9ae2a525
|
|
@ -80,6 +80,10 @@ class SyncBankingConnectionJob implements ShouldBeUnique, ShouldQueue
|
|||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
if ($this->isRateLimitError($e)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$connection->update([
|
||||
'status' => BankingConnectionStatus::Error,
|
||||
'error_message' => $this->friendlyErrorMessage($e),
|
||||
|
|
@ -203,6 +207,11 @@ class SyncBankingConnectionJob implements ShouldBeUnique, ShouldQueue
|
|||
return __('An unexpected error occurred during sync. Please try again later.');
|
||||
}
|
||||
|
||||
private function isRateLimitError(\Throwable $e): bool
|
||||
{
|
||||
return $e instanceof RequestException && $e->response->status() === 429;
|
||||
}
|
||||
|
||||
private function isAuthError(\Throwable $e): bool
|
||||
{
|
||||
return $e instanceof RequestException
|
||||
|
|
|
|||
|
|
@ -828,3 +828,34 @@ test('sends auth failed email for binance 403 error on final attempt', function
|
|||
&& $mail->bankingConnection->id === $connection->id;
|
||||
});
|
||||
});
|
||||
|
||||
test('rate limit error does not set connection status to error', function () {
|
||||
$user = User::factory()->onboarded()->create();
|
||||
$connection = BankingConnection::factory()->create([
|
||||
'user_id' => $user->id,
|
||||
'last_synced_at' => now()->subDay(),
|
||||
]);
|
||||
Account::factory()->connected()->create([
|
||||
'user_id' => $user->id,
|
||||
'banking_connection_id' => $connection->id,
|
||||
'external_account_id' => 'ext-123',
|
||||
]);
|
||||
|
||||
$transactionSync = Mockery::mock(TransactionSyncService::class);
|
||||
$transactionSync->shouldReceive('sync')->andThrow(
|
||||
new \Illuminate\Http\Client\RequestException(
|
||||
new \Illuminate\Http\Client\Response(
|
||||
new \GuzzleHttp\Psr7\Response(429)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$balanceSync = Mockery::mock(BalanceSyncService::class);
|
||||
|
||||
$job = new SyncBankingConnectionJob($connection);
|
||||
$job->handle($transactionSync, $balanceSync);
|
||||
|
||||
$connection->refresh();
|
||||
expect($connection->status)->toBe(BankingConnectionStatus::Active);
|
||||
expect($connection->error_message)->toBeNull();
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue