supplycart/xero
A Laravel integration for the Xero accounting API by Supplycart, providing a simple way to connect your application to Xero for syncing invoices, contacts, payments, and other accounting data through an expressive, framework-friendly interface.
This package manages Laravel-side "connections" that hold OAuth credentials and refresh tokens on your behalf. The 0.3.23 release is a small but behaviorally meaningful patch: when a token refresh fails, the connection's enabled status is now reset, so the connection stops being treated as usable instead of silently continuing with a stale/expired token.
Minimal steps to get productive:
enabled column (and any last_refreshed_at/error columns). This is the state the release note changes — everything else in the package orbits it.enabled = false (or equivalent) before rethrowing/logging. Confirm whether the exception still propagates or is swallowed.enabled before attempting work; after 0.3.23 that filter becomes load-bearing rather than cosmetic.Gate work on enabled, not on token presence. The pre-0.3.23 pattern of "try the call, catch the 401, move on" now produces different state than it used to. Prefer:
Connection::query()->where('enabled', true)->each(fn ($c) => $c->refreshIfNeeded());
so refresh failures are handled once, at the boundary, rather than inside every consumer.
Refresh-then-use, with a single retry. Wrap outbound API calls so a refresh failure disables the connection and does not get retried in a loop. The reset is a signal, not a transient error — retrying an already-disabled connection wastes quota and can trigger provider lockouts.
Re-enable only through re-authorization. Build a "reconnect" flow (redirect to the provider's consent screen) and, on success, explicitly set enabled = true. Treat enabled as user-visible: surface disabled connections in your settings UI with a "Reconnect" button and a reason string if the package stores one.
Isolate connection health from feature code. A small ConnectionHealth service that returns usable / needs_reauth / disabled keeps the enabled flag out of controllers and jobs, and gives you one place to change when the package's semantics shift again (they just did).
Test with a fake token endpoint. Use Http::fake() to return a 400 invalid_grant for the refresh call and assert two things: the connection is now disabled, and your job recorded a skip rather than a hard failure. This is the exact regression surface of 0.3.23.
Multi-tenant care. If connections are per-user or per-team, make sure the disable happens on the specific connection row, not globally, and that a tenant's disabled connection doesn't silently drop them from a sync fan-out — emit an event or notification so it's visible.
enabled will just... do nothing. Add logging/alerting when a connection is skipped so a wave of disabled connections doesn't masquerade as "no new records."enabled = false, or clear a previously-set enabled = true default. Read the diff against your migration; if the column is nullable, false vs null may behave differently in your scopes.enabled = true in memory. Re-query, or check wasChanged(), before acting.TokenRefreshFailed event), listen to it to notify users proactively. That's a better hook than overriding the connection class.How can I help you explore Laravel packages today?