From e71a743a0a6e3a1a6bde5c1bc29df94555f74bd1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?V=C3=ADctor=20Falc=C3=B3n?=
Date: Wed, 13 May 2026 18:53:30 +0100
Subject: [PATCH] feat: Coinbase banking integration (#388)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## Summary
Adds **Coinbase** as a banking/investment provider, alongside existing
Binance, Bitpanda, and Indexa Capital integrations. Connection auth uses
Coinbase Developer Platform (CDP) JWT (ES256) API keys.
Sync model mirrors Bitpanda: no historical balance reconstruction
(Coinbase API has no daily snapshot endpoint). Balance tracking starts
from connection date.
## Account model
One single **Crypto Portfolio** Whisper Account in the user's fiat
currency, aggregating all Coinbase wallets (crypto + fiat).
## Backend
- `app/Services/Banking/CoinbaseClient.php` — JWT-signed Coinbase
Advanced Trade client (`firebase/php-jwt` ES256). Per-request JWT, 120s
TTL, `uri` claim `"METHOD host/path"`. Methods:
`getAccounts`/`getAllAccounts` (cursor pagination), `getProduct`,
`getBestBidAsk` (batched), with 429 retry/backoff.
- `app/Services/Banking/CoinbaseBalanceSyncService.php` — Partitions
wallets into fiat vs crypto. Batched `best_bid_ask` for prices, USD
stablecoin shortcut (USDT/USDC/DAI/PYUSD/GUSD),
`CurrencyConversionService` fallback. Aggregates everything into user
fiat.
- `app/Http/Controllers/OpenBanking/CoinbaseController.php` +
`ConnectCoinbaseRequest.php` — mirror Bitpanda flow, validates
`api_key_name` (`organizations/{org}/apiKeys/{id}`) + PEM `private_key`.
Stores key_name in `api_token`, PEM in `api_secret` (already encrypted
TEXT).
- `BankingConnection::isCoinbase()`,
`SyncBankingConnectionJob::syncCoinbase()`, credential-update flow.
- `routes/web.php`: `POST /open-banking/coinbase/connect`.
- Bank seeder entry + factory `coinbase()` state.
## Frontend
- `connect-account-dialog.tsx` / `connect-account-inline.tsx`: Coinbase
appears in the bank picker. Confirm step shows `` for API key
name and `
@@ -553,6 +594,59 @@ export function ConnectAccountDialog({
)}
+ {isCoinbase && (
+
+
+
+
+ setCoinbaseKeyName(e.target.value)
+ }
+ className="mt-1 font-mono text-xs"
+ placeholder="00000000-0000-0000-0000-000000000000"
+ />
+
+
+
+
+
+ {__(
+ 'Create a CDP API key (Ed25519 recommended) in the Coinbase Developer Platform under',
+ )}{' '}
+
+ {__('API Keys')}
+
+ . {__('Use a view-only key.')}
+
+
+ )}
+
@@ -513,6 +545,57 @@ export function ConnectAccountInline({
)}
+ {isCoinbase && (
+
+
+
+
+ setCoinbaseKeyName(e.target.value)
+ }
+ className="font-mono text-xs"
+ placeholder="00000000-0000-0000-0000-000000000000"
+ />
+
+
+
+
+
+ {__(
+ 'Create a CDP API key (Ed25519 recommended) in the Coinbase Developer Platform under',
+ )}{' '}
+
+ {__('API Keys')}
+
+ . {__('Use a view-only key.')}
+
+
+ )}
+