From 619ed0f1db44f004b8e4704e2a4e1bbef0e56a74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Falc=C3=B3n?= Date: Tue, 23 Jun 2026 11:54:31 +0200 Subject: [PATCH] feat(banking): let Wise credentials be updated (#588) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What Makes **Wise** connections support credential updates, like every other API-key provider. Wise was the only API-key provider not wired into the credential-update path: `ConnectionController::validateProviderCredentials()` had no Wise arm, so it fell to the `Unsupported provider` default, and the update dialog (driven by the provider registry's `updatable` flag) rendered nothing. A Wise connection in an auth-error state therefore showed an **Update Credentials** button that opened an empty, unusable dialog. ## Fix - Add the Wise validation arm in `validateProviderCredentials` (`WiseClient::getProfiles()`). The validation rules and the `api_token` → column mapping already come from `BankingProvider`'s credential registry, so nothing else on the backend changes. - Drop the now-unused `updatable` flag from the connect-providers registry — every connect provider is updatable — and simplify `update-credentials-dialog` to render fields for any registry provider. ## Tests - Updating Wise credentials with a valid token succeeds (status back to active, token stored, sync dispatched). - `BankingProviderTest`: every API-key provider declares credential fields, guarding against adding a provider without an update path again. Follow-up to #581 (this branch is off the updated `main`). --- .../OpenBanking/ConnectionController.php | 2 ++ .../update-credentials-dialog.tsx | 17 +++++------ resources/js/lib/connect-providers.tsx | 3 -- .../OpenBanking/ConnectionControllerTest.php | 28 +++++++++++++++++++ tests/Unit/Enums/BankingProviderTest.php | 13 +++++++++ 5 files changed, 50 insertions(+), 13 deletions(-) diff --git a/app/Http/Controllers/OpenBanking/ConnectionController.php b/app/Http/Controllers/OpenBanking/ConnectionController.php index 2c83d0be..11d2997c 100644 --- a/app/Http/Controllers/OpenBanking/ConnectionController.php +++ b/app/Http/Controllers/OpenBanking/ConnectionController.php @@ -17,6 +17,7 @@ use App\Services\Banking\BitpandaClient; use App\Services\Banking\CoinbaseClient; use App\Services\Banking\IndexaCapitalClient; use App\Services\Banking\InteractiveBrokersClient; +use App\Services\Banking\WiseClient; use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Illuminate\Http\RedirectResponse; use Illuminate\Support\Facades\Auth; @@ -119,6 +120,7 @@ class ConnectionController extends Controller BankingProvider::Bitpanda => (new BitpandaClient($validated['api_key']))->getCryptoWallets(), BankingProvider::Coinbase => (new CoinbaseClient($validated['api_key_name'], $validated['private_key']))->getAccounts(limit: 1), BankingProvider::InteractiveBrokers => (new InteractiveBrokersClient($validated['token'], $validated['query_id']))->fetchStatement(), + BankingProvider::Wise => (new WiseClient($validated['api_token']))->getProfiles(), default => throw new \InvalidArgumentException('Unsupported provider for credential update.'), }; } catch (\InvalidArgumentException $e) { diff --git a/resources/js/components/open-banking/update-credentials-dialog.tsx b/resources/js/components/open-banking/update-credentials-dialog.tsx index 5ca9166a..36db974f 100644 --- a/resources/js/components/open-banking/update-credentials-dialog.tsx +++ b/resources/js/components/open-banking/update-credentials-dialog.tsx @@ -34,9 +34,6 @@ export function UpdateCredentialsDialog({ const [error, setError] = useState(null); const provider = connectProviderByKey(connection.provider); - // Only providers the backend exposes a credential-update path for (Wise has none). - const updatableProvider = - provider && provider.updatable !== false ? provider : undefined; const setCredential = useCallback((key: string, value: string) => { setCredentials((current) => ({ ...current, [key]: value })); @@ -55,7 +52,7 @@ export function UpdateCredentialsDialog({ } function handleSubmit() { - if (!updatableProvider) { + if (!provider) { return; } @@ -64,14 +61,14 @@ export function UpdateCredentialsDialog({ router.patch( `/settings/connections/${connection.id}/credentials`, - credentialPayload(updatableProvider, credentials), + credentialPayload(provider, credentials), { onSuccess: () => { onOpenChange(false); resetState(); }, onError: (errors) => { - const fieldError = updatableProvider.fields + const fieldError = provider.fields .map((f) => errors[f.key]) .find(Boolean); @@ -90,8 +87,8 @@ export function UpdateCredentialsDialog({ ); } - const isValid = updatableProvider - ? isProviderComplete(updatableProvider, credentials) + const isValid = provider + ? isProviderComplete(provider, credentials) : false; return ( @@ -108,9 +105,9 @@ export function UpdateCredentialsDialog({ {error &&

{error}

} - {updatableProvider && ( + {provider && (