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>
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 untilvalid_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.