Skip to content

Repository files navigation

bunq PSD2 Reference Implementation

A working example of the PSD2 OAuth2 flow using the bunq API, built with Python and FastAPI. Use this as a reference when integrating with bunq as a Payment Service Provider (PSP).

Note: You may need formal PSD2 certification to access user payment data in production. This code targets the bunq sandbox environment and is not production-ready as-is.


What is PSD2?

PSD2 (Payment Services Directive 2) is a European regulation that requires banks to open up their payment infrastructure to licensed third-party providers via secure APIs. As a PSD2 provider you can:

  • Read account information — balances, transactions, payment history (with user consent)
  • Initiate payments — create payments or payment requests on behalf of users

Architecture

Entity Role
bunq The bank. Holds financial records and exposes the API.
End user A bunq user who grants your app access to their account via OAuth.
This app (PSD2 provider) Acts as the OAuth client. Stores user access tokens and makes API calls on their behalf.

The app consists of:

  • A FastAPI server exposing endpoints that proxy bunq's API
  • A SQLite database storing OAuth access tokens for each connected user
  • An RSA key pair used for request signing (generated automatically on first run)
  • A PSD2 certificate registered with bunq (generated by create_psd2_user.sh)

Setup

1. Generate your PSD2 user and API key

chmod +x create_psd2_user.sh
./create_psd2_user.sh

This script:

  • Generates an RSA installation key pair and a self-signed PSD2 certificate
  • Registers the certificate with bunq's sandbox API
  • Creates a device server and session
  • Prints your PSD2 API key (token_value) to the console

Copy that API key — you'll need it in the next step.

In production you would store the full credential response (including user ID) and manage certificate rotation. For this sandbox demo only the API key is needed.

2. Set your API key

Open dependencies.py and set YOUR_API_KEY to the value printed by the script:

YOUR_API_KEY = "your_api_key_here"

3. Install dependencies

pip install -r requirements.txt

4. Start the server

uvicorn main:app --reload

5. Run one-time setup

Open http://localhost:8000/setup_one_time in your browser. This only needs to be run once per API key.

It will:

  1. Initialize the local database
  2. Register an Installation with bunq (RSA public key exchange)
  3. Register a Device Server
  4. Create a Session
  5. Create an OAuth client
  6. Register the OAuth callback URL (http://localhost:8000/callback)
  7. Write a .env file with your OAuth credentials

After this completes, restart the server so it picks up the .env file.

6. Authorize a bunq user

Go to http://localhost:8000/auth. You'll be redirected to bunq's OAuth authorization page.

  • Scan the QR code with the bunq sandbox app (or paste it into an Android emulator)
  • Grant access to the bank accounts you want to share
  • You'll be redirected back to /callback

If running on localhost you may need to change https to http in the redirect URL after being sent back.

A successful callback returns:

{"message": "OAuth success", "new_user_id": 1}

The new_user_id is the local database ID for this connected user.

OAuth success response

7. Make API calls

Open http://localhost:8000/docs for the Swagger UI. All endpoints are listed there with example payloads.

Swagger UI

Every endpoint that acts on a user's behalf follows the same pattern internally:

  1. Look up the user's OAuth access token from the local database
  2. Exchange it for a session token via POST /session-server
  3. Use the session token to make the API call to bunq

Example — fetching a user's accounts:

GET /user/1/accounts

→ fetches the monetary accounts for the bunq user connected as local user 1.


Implemented Endpoints

OAuth

Method Endpoint Description
GET /auth Start OAuth authorization flow
GET /callback Exchange authorization code for access token
GET /oauth-clients List OAuth clients for the PSD2 user
GET /oauth-client/{id}/callback-url List registered callback URLs
POST /oauth-client/{id}/callback-url Add a new callback URL

Accounts

Method Endpoint Description
GET /user/{user_id}/ Get user profile
GET /user/{user_id}/accounts List monetary accounts

Payments

Method Endpoint Description
GET /user/{user_id}/payments/{account_id} List payments (paginated)
GET /user/{user_id}/monetary-account/{account_id}/payment/{payment_id}/ Get a single payment
POST /user/{user_id}/payment Create an immediate payment
GET /user/{user_id}/monetary-account/{account_id}/draft-payment/{payment_id}/ Get a draft payment
POST /user/{user_id}/draft-payment Create a draft payment
PUT /user/{user_id}/monetary-account/{account_id}/draft-payment/{payment_id}/ Accept a draft payment
POST /user/{user_id}/draft-payment-batch Create multiple draft payments

Requests

Method Endpoint Description
POST /user/{user_id}/request-inquiry Create a payment request

Cards

Method Endpoint Description
GET /user/{user_id}/cards List cards
GET /user/{user_id}/card/{card_id} Get a specific card
POST /user/{user_id}/credit-cards Order a new card
PUT /user/{user_id}/card/{card_id} Update card settings

bunqme Tabs

Method Endpoint Description
GET /user/{user_id}/monetary-account/{account_id}/bunqme-tab/ List bunqme tabs
POST /user/{user_id}/monetary-account/{account_id}/bunqme-tab/ Create a bunqme tab

Attachments & Notes

Method Endpoint Description
POST /user/{user_id}/monetary-account/{account_id}/attachment Upload a file attachment
POST /user/{user_id}/monetary-account/{account_id}/payment/{payment_id}/note-attachment Link attachment to a payment
GET /user/{user_id}/monetary-account/{account_id}/payment/{payment_id}/note-attachment Get attachments on a payment
POST /user/{user_id}/monetary-account/{account_id}/payment/{payment_id}/note-text Add a text note to a payment
GET /user/{user_id}/monetary-account/{account_id}/payment/{payment_id}/note-text Get text notes on a payment

Notification Filters

Method Endpoint Description
GET /user/{user_id}/notification-filter-url List URL notification filters
POST /user/{user_id}/notification-filter-url Set URL notification filters
PUT /user/{user_id}/notification-filter-url Update URL notification filters
GET /user/{user_id}/notification-filter-failure List notification delivery failures

Events & Mastercard Actions

Method Endpoint Description
GET /user/{user_id}/event List events
GET /user/{user_id}/event/{item_id} Get a specific event
GET /user/{user_id}/additional-transaction-information-category Get transaction info categories
GET /user/{user_id}/mastercard_action/ List Mastercard actions
GET /user/{user_id}/mastercard_action/{item_id} Get a specific Mastercard action

PSD2 Issuer Transactions

Method Endpoint Description
POST /psd2/payment-service-provider-issuer-transaction Create a PSD2 issuer transaction
GET /psd2/payment-service-provider-issuer-transaction/{transaction_id} Get a PSD2 issuer transaction
GET /psd2/payment-service-provider-issuer-transaction-public/{public_id} Get public transaction info
GET /psd2/redirect/{public_id} Redirect user to bunq PSP payment page

IP Whitelist

Method Endpoint Description
GET /credential-password-ip List credential-password-ip objects
GET /credential-password-ip/{ip_id} Get a specific one
GET /credential-password-ip/{credential_id}/ip List IP whitelist entries
POST /credential-password-ip/{credential_id}/ip Add IP to whitelist
GET /credential-password-ip/{credential_id}/ip/{item_id} Get a specific whitelist entry
PUT /credential-password-ip/{credential_id}/ip/{item_id} Update a whitelist entry

About

Sample Implementation of the bunq API for PSD2 partners

Resources

Stars

0 stars

Watchers

1 watching

Forks

Contributors

Languages