Files
kvitt/docs/spikes/k0
jacobh2 2ce317f72c K0 spike: local Enable Banking test app
Stdlib Python app (docs/spikes/k0/app.py) on localhost:8765: lists Swedish ASPSPs, starts BankID authorisation with the registered https redirect and a paste box for the landed address, creates the session, fetches transactions with and without PSU headers (continuation_key paging), balances, diff between fetches and an aggregate analysis block. Key and state in the git-ignored docs/spikes/k0/.local/.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 11:36:12 +02:00
..
2026-10-24 11:36:12 +02:00

K0 spike: local Enable Banking test app

A one-file local web app that connects a card account (SJ Prio Mastercard, SEB Kort Bank AB) through Enable Banking with BankID and shows what the API returns. Python 3 standard library only; the RS256 JWT is signed with openssl.

Run

python3 docs/spikes/k0/app.py        # serves http://localhost:8765/ and opens the browser

Options: --port, --no-browser, --local-dir (config and state folder), --api-base (a fake API for tests).

Where the key goes

docs/spikes/k0/.local/ (git-ignored, created on first run):

  • <application-id>.pem: the private key the Enable Banking control panel downloaded at registration (the file name is the application id).
  • config.json (optional): {"application_id": "…", "key_file": "….pem"} when the file name is not the id or there are several keys.
  • state.json: written by the app. Holds the settings, the bank list, the session id, the account, the last two fetches and an aggregate fetch log. When you run it again it reuses the session, so you do not need BankID again until valid_until.

What each button tests

Section Button Call What it shows
1 Status none none Application id, key, session with valid_until and days left, chosen account
2 Banker Lista banker / Välj bank GET /aspsps?country=SE All Swedish ASPSPs. SJ, SEB Kort and Eurocard are highlighted and sorted first. Per row: beta, maximum_consent_validity, psu_types, auth_methods, required_psu_headers. The chosen ASPSP's full JSON is shown below. The default is the one named "SJ Prio"
3 Anslut Anslut med BankID POST /auth Sets valid_until to now plus the ASPSP's maximum, capped at 180 days, less 5 min, with psu_type: personal and a random state. Opens the bank in a new tab, where you scan the QR with BankID
4 Klistra in Använd adressen POST /sessions Takes code and state from any pasted address and checks state. This is the main path: the redirect goes to https://kvitt.suorra.duckdns.org/cards/callback, which does not load, but the address bar still carries the code
5 Konton Kontrollera session / Välj konto GET /sessions/{id} Accounts with uid, name, product, currency and type. Identifiers are masked to the last 4 characters. If there is only one account (or one CARD account), it is chosen by default
6 Hämta Uppdatera GET /accounts/{uid}/transactions Sends PSU headers (user present): always Psu-Ip-Address and Psu-User-Agent, plus every header the ASPSP lists in required_psu_headers. Uses date_from = today − history days, optionally strategy=longest, and follows continuation_key
6 Hämta Bakgrundshämtning same, without PSU headers Tests the background path. A refusal is shown with its HTTP status and error JSON
6 Hämta Saldo GET /accounts/{uid}/balances Balances, fetched with PSU headers
7 Köp none none Purchases with both dates, text, signed amount, currency, direction, status, entry_reference and a "rå" toggle for the raw JSON. Compared with the previous fetch: new rows, changed rows (PDNG→BOOK) and rows that disappeared
8 Analys Kopiera analys none Aggregates only: counts, dates reached vs requested, field presence, non-SEK rows, entry_reference presence and stability, PDNG→BOOK matched by entry_reference vs by amount+date (±7 days), pages per fetch, and the fetch log
9 Koppla från Koppla från DELETE /sessions/{id} Clears the local session, accounts and transactions. Keeps the settings, the bank list and the aggregate log

The PSU IP address field is filled from api.ipify.org on the first Uppdatera if you leave it empty; you can also type an address. http://localhost:8765/callback is handled as well, but Enable Banking's control panel refuses http redirect URLs, so it is not the default.

Privacy

stdout shows only method, path template and HTTP status. It never shows bodies, merchant texts, amounts or ids. Personal data stays in .local/. Copy only the Analys block into reports.