# actual-gocardless-proxy A drop-in [GoCardless Bank Account Data API](https://developer.gocardless.com/bank-account-data/quick-start-guide/) proxy server that lets [Actual Budget](https://actualbudget.org/) sync with French (and other European) bank accounts via [Enable Banking](https://enablebanking.com/) — a **free** PSD2 aggregator. **Zero modifications to actual-server.** The only change is setting one environment variable: ``` GOCARDLESS_BASE_URL=http://gocardless-proxy:3456 ``` ## How it works ``` Actual Budget (browser) │ GoCardless API calls ▼ actual-server │ GOCARDLESS_BASE_URL → http://gocardless-proxy:3456 ▼ gocardless-proxy ◄── this repo │ Enable Banking PSD2 API ▼ Enable Banking → French banks (BNP, SG, CA, LCL, BP, …) ``` The proxy translates GoCardless API shapes ↔ Enable Banking shapes on the fly and persists all state (tokens, requisitions, account mappings) in `./data/store.json` so Docker restarts don't break existing account links. --- ## Setup ### 1. Sign up for Enable Banking Go to [enablebanking.com](https://enablebanking.com) and create a **free personal account**. Under your app settings, note your **Application ID**. ### 2. Generate an RSA key pair Enable Banking uses RS256 JWT auth. You need to provide your **public key** in the Enable Banking dashboard and keep the **private key** on your server. ```bash # Generate a 2048-bit RSA private key in PKCS8 format openssl genpkey -algorithm RSA -pkcs8 -out private_key.pem -pkeyopt rsa_keygen_bits:2048 # Extract the public key (upload this to Enable Banking) openssl pkey -in private_key.pem -pubout -out public_key.pem ``` Upload `public_key.pem` in the Enable Banking dashboard under your application's API settings. Keep `private_key.pem` somewhere safe — you'll need it in the next step. ### 3. Run the setup script Clone this repo and install dependencies: ```bash git clone https://github.com/yourname/actual-gocardless-proxy cd actual-gocardless-proxy npm install ``` Run the interactive setup: ```bash npm run setup ``` The script will ask for: - Your Enable Banking **Application ID** - Path to your **private key PEM** file - The **proxy base URL** (the URL that Enable Banking will redirect to after OAuth — must be reachable from your browser; e.g. `http://localhost:3456` for local dev or `https://yourserver.example.com` for production) It will generate a random `secret_id` and `secret_key` pair and print them: ``` ────────────────────────────────────────────────────────── Enter these values in Actual Budget → Settings → GoCardless: Secret ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx Secret Key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx And set this environment variable on actual-server: GOCARDLESS_BASE_URL=http://gocardless-proxy:3456 ────────────────────────────────────────────────────────── ``` All credentials are stored in `./data/store.json`. ### 4. Add the proxy to your docker-compose.yml In your existing `actual-server` `docker-compose.yml`, add the proxy service and wire it to actual-server: ```yaml services: actual-server: image: actualbudget/actual-server:latest # ... your existing config ... environment: GOCARDLESS_BASE_URL: http://gocardless-proxy:3456 # ... your other env vars ... depends_on: - gocardless-proxy gocardless-proxy: build: /path/to/actual-gocardless-proxy # or: image: ghcr.io/yourname/actual-gocardless-proxy:latest container_name: gocardless-proxy restart: unless-stopped volumes: - /path/to/actual-gocardless-proxy/data:/app/data environment: PORT: 3456 ``` > **Note on PROXY_BASE_URL:** The proxy base URL is saved in `data/store.json` during setup. If you need to change it later (e.g. you added a reverse proxy), re-run `npm run setup` or edit `data/store.json` directly and update `config.proxy_base_url`. ### 5. Start the services ```bash docker compose up -d ``` Verify the proxy is running: ```bash curl http://localhost:3456/health # → {"status":"ok","configured":true} ``` ### 6. Configure Actual Budget In Actual Budget, go to **Settings → GoCardless** and enter the `secret_id` and `secret_key` printed in step 3. You can now go to **Accounts → Link account** and search for your French bank. --- ## OAuth redirect flow ``` 1. actual-server POST /api/v2/requisitions/ { redirect: "http://actual:5006/..." } ↓ 2. proxy POST https://api.enablebanking.com/auth { aspsp: { name, country }, redirect_url: "http://proxy/callback", state: } ← { url: "https://tilisy.enablebanking.com/..." } ↓ 3. proxy returns requisition with link = EB auth URL ↓ 4. User visits the link, authenticates at their bank ↓ 5. Enable Banking redirects → http://proxy/callback?code=xxx&state= ↓ 6. proxy POST https://api.enablebanking.com/sessions { code } ← { session_id, accounts: [uid, ...] } → fetches account details, stores GC account IDs → marks requisition status "LN" (linked) → redirects → http://actual:5006/...?requisition_id= ↓ 7. actual-server GET /api/v2/requisitions// ← { status: "LN", accounts: ["gc-acc-id-1", ...] } ↓ 8. actual-server GET /api/v2/accounts//details/ GET /api/v2/accounts//balances/ GET /api/v2/accounts//transactions/ ``` --- ## Supported banks All French banks supported by Enable Banking, including: | Bank | BIC | |------|-----| | BNP Paribas | BNPAFRPP | | Société Générale | SOGEFRPP | | Crédit Agricole | AGRIFRPP | | LCL | CRLYFRPP | | Banque Populaire | various | | Caisse d'Épargne | various | | La Banque Postale | PSSTFRPPPAR | | Crédit Mutuel | CMCIFRPP | | CIC | CMCIFRPP | | Boursorama | BOUSFRPPXXX | | Fortuneo | FTNOFRP1 | | Hello bank! | BNPAFRPP | See [enablebanking.com/docs/markets/fr/](https://enablebanking.com/docs/markets/fr/) for the full list. --- ## Troubleshooting ### "Proxy not configured" error Run `npm run setup` and restart the container. ### "Unknown institution_id" when creating a requisition actual-server caches institutions. Go to **Settings → GoCardless → Reset** and re-fetch banks. ### OAuth callback fails with "Enable Banking error" - Make sure `proxy_base_url` in `data/store.json` is reachable from your browser (not just from Docker). - Check that the redirect URL is whitelisted in your Enable Banking application settings. ### Transactions not appearing Enable Banking may return an empty `transactions` array for accounts with no activity in the requested date range. Try widening the date range. ### Private key errors Make sure the key is **PKCS8** format. To convert from traditional RSA format: ```bash openssl pkcs8 -topk8 -nocrypt -in old_key.pem -out private_key.pem ``` --- ## Data & privacy All bank credentials and tokens stay on your server inside `./data/store.json`. Nothing is sent to any third party except Enable Banking (which is the PSD2 provider) and your bank. The `data/` directory is git-ignored and should not be committed. --- ## License MIT