whisper-money/tests/Feature/OpenBanking
Víctor Falcón f800847591
feat(banking): back off scheduler when EnableBanking returns 429 (#352)
## Problem

Production logs show repeated EnableBanking 429s on the same
connections, every cron cycle:

```
[2026-05-04 18:00:55] EnableBanking API error status:429
  body: [HUB046] Allowed number of accesses exceeded for consent
[2026-05-04 21:47:12] EnableBanking API error status:429
  body: Daily PSU not present consultation limit has been exceeded
[2026-05-05 00:01:41] same connection, same error
[2026-05-05 06:01:41] same connection, same error
```

Root cause: `SyncBankingConnectionJob` returned early on 429s without
persisting any backoff state. The scheduler kept re-dispatching the same
connection on every run, hammering the provider and burning the daily
quota.

## Fix

Persist a per-connection backoff window so the scheduler stops
re-dispatching until the provider quota resets.

- New `rate_limited_until` column on `banking_connections`.
- On 429: derive the window from
  1. `Retry-After` header if present,
2. "Daily ..." message → next UTC midnight (matches PSU daily limit
semantics),
  3. default 1 hour (consent / generic).
- Job short-circuits with a `Skipped` sync log if the window is still
active.
- Successful sync clears the window.
- `SyncAllBankingConnectionsJob` + `SyncBankingConnections` command
filter out connections still inside their backoff.

## Tests

- Existing rate-limit test updated (now also asserts the backoff is
set).
- New: daily message → next UTC midnight.
- New: `Retry-After` header honoured (1800s).
- New: rate-limited connection skipped without calling provider.
- New: successful sync clears `rate_limited_until`.
- New: scheduler excludes connections whose backoff has not expired.

`php artisan test --compact
--filter="SyncBankingConnectionJobTest|SyncRetryAndLoggingTest|SyncBankingConnectionsCommandTest"`
→ 70 passed.
2026-05-05 09:39:32 +02:00
..
AccountMappingTest.php feat(open-banking): remove feature flag gating (#297) 2026-04-17 10:20:05 +02:00
AuthorizationControllerTest.php feat(open-banking): remove feature flag gating (#297) 2026-04-17 10:20:05 +02:00
BalanceSyncServiceTest.php feat: Integrate EnableBanking as open banking provider (#106) 2026-02-12 09:09:28 +01:00
BinanceBalanceSyncTest.php feat(settings): centralize currency options and split profile/account support (#256) 2026-04-02 19:23:10 +02:00
BinanceControllerTest.php feat(open-banking): remove feature flag gating (#297) 2026-04-17 10:20:05 +02:00
BitpandaBalanceSyncTest.php feat(settings): centralize currency options and split profile/account support (#256) 2026-04-02 19:23:10 +02:00
BitpandaControllerTest.php feat(open-banking): remove feature flag gating (#297) 2026-04-17 10:20:05 +02:00
ConnectionControllerTest.php feat(open-banking): remove feature flag gating (#297) 2026-04-17 10:20:05 +02:00
IndexaCapitalBalanceSyncTest.php feat: store invested_amount in user currency instead of account currency (#262) 2026-04-06 12:48:13 +02:00
IndexaCapitalControllerTest.php feat(open-banking): remove feature flag gating (#297) 2026-04-17 10:20:05 +02:00
InstitutionControllerTest.php feat(open-banking): remove feature flag gating (#297) 2026-04-17 10:20:05 +02:00
OpenBankingFeatureFlagTest.php fix: route new PWA guests to signup (#313) 2026-04-21 10:53:05 +01:00
SyncBankingConnectionJobTest.php feat(banking): back off scheduler when EnableBanking returns 429 (#352) 2026-05-05 09:39:32 +02:00
SyncBinanceHistoricalBalancesJobTest.php chore: upgrade Laravel 12 to 13 (#242) 2026-03-25 12:56:33 +00:00
SyncRetryAndLoggingTest.php feat(banking): back off scheduler when EnableBanking returns 429 (#352) 2026-05-05 09:39:32 +02:00
TransactionSyncServiceTest.php feat: Add per-bank description formatter for bank-synced transactions (#120) 2026-02-13 09:54:58 +01:00