actual-gocardless-proxy/README.md
jeanGaston 2487d8a2f9 docs: README with full setup guide
Covers: Enable Banking signup, RSA key generation, running setup script,
docker-compose wiring, OAuth redirect flow diagram, supported French banks,
and troubleshooting.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 10:34:52 +02:00

228 lines
7.5 KiB
Markdown

# 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: <requisition_id> }
← { 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=<req_id>
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=<req_id>
7. actual-server GET /api/v2/requisitions/<req_id>/
← { status: "LN", accounts: ["gc-acc-id-1", ...] }
8. actual-server GET /api/v2/accounts/<id>/details/
GET /api/v2/accounts/<id>/balances/
GET /api/v2/accounts/<id>/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