Sandbox: no real money moves.
Pétrusse BankLuxembourg

Pétrusse Bank – PSD2 Exercise

Finance & Technology – University of Luxembourg

Deadline for submission: Friday, 11 December 2026
In-class review: Monday, 14 December 2026
Questions: georges@berscheid.lu

1. Your mission

You run a small fintech that helps people manage their money. Under PSD2 your company is a Third Party Provider (TPP):

Your client banks at Pétrusse Bank, a fictional bank that offers a PSD2 interface based on the Berlin Group NextGenPSD2 standard. Many European banks use this standard, including most Luxembourg banks.

Your client is between jobs. They ask you to make sure their current account will not go into the red next month. You will:

  1. read their accounts and transactions,
  2. work out exactly how much money must be moved from their savings account to their current account, and
  3. initiate that transfer for them.

Everything happens in a sandbox. No real money moves. The bank's data is frozen at Thursday, 5 November 2026: treat that date as "today" for the bank's accounts.

2. What you received

You received a personal sheet, on paper in class or as a PDF by email. It contains:

Your sheet is personal and every action is logged against it. Do not share it, and never type your PIN, TAN codes, activation code or client secret into an AI tool, a chat or a shared document. Under PSD2 (Article 69), a payment service user must keep their personalised security credentials safe. Giving them to a third party, including an AI tool, is exactly what PSD2 is designed to prevent.

3. Rules of the game

4. Set up Google Colab

  1. Go to colab.research.google.com, sign in with a Google account and create a new notebook. Name it psd2-<your-matriculation-number>.
  2. Open the Secrets panel: the key icon in the left sidebar. This is where you store passwords instead of writing them in your code. Your notebook, and any AI assistant reading it, then never contains them.
  3. You will add two secrets there: CLIENT_ID now (from your sheet) and CLIENT_SECRET in step 5.1. Enable "Notebook access" for both.

Useful documentation, keep it open:

5. The steps

Each step ends with a checkpoint, so you know when you are done. Keep each step in its own notebook cell, with a short comment in your own words explaining what it does.

5.1 Activate your developer access

Real banks have a developer portal where fintechs get their credentials. Open https://petrussebank.berscheid.lu/portal in your browser. Enter your client_id and the activation code from your sheet, and choose a portal password.

The portal shows your client secret once. Copy it straight into Colab Secrets as CLIENT_SECRET. If you lose it, you can generate a new one in the portal, and the old one stops working.

In the portal you can also see My activity: the bank's log of your requests. It is very useful when something goes wrong.

Checkpoint: the portal says your access is active, and CLIENT_SECRET is stored in Colab Secrets.

5.2 Install your client's banking app

Scan the QR code (section C of your sheet) with your phone. Log in with the PSU ID and PIN from section B. Your phone is now your client's Pétrusse Bank app. You will need it to approve the payment in step 5.7.

Only one phone can be activated. Use the phone you will have with you while working on the exercise.

Checkpoint: the app on your phone shows "Device activated".

5.3 Authenticate your fintech

Every request your fintech sends must carry an access token. Your fintech gets one by presenting its credentials to the bank. This starter cell does it for you:

import requests, uuid
from google.colab import userdata

BASE_URL = "https://petrussebank.berscheid.lu"
CLIENT_ID = userdata.get("CLIENT_ID")
CLIENT_SECRET = userdata.get("CLIENT_SECRET")

r = requests.post(f"{BASE_URL}/oauth/token",
                  data={"grant_type": "client_credentials"},
                  auth=(CLIENT_ID, CLIENT_SECRET))
print(r.status_code)
print(r.json())

Tokens expire after 15 minutes. When you get an error about an expired token, run this cell again. In real PSD2 a fintech proves its identity with a qualified eIDAS certificate. The token is the sandbox's simplified stand-in.

Checkpoint: you get status 200 and an access_token.

Your fintech may only read the accounts after your client has given consent. Find the right endpoint in the API documentation and create a consent that lets you read the accounts, balances and transactions your calculation needs.

Things to find out from the documentation:

The bank's answer contains a link for Strong Customer Authentication (SCA). Open it in your browser. You now act as the client: log in with the PSU ID and PIN, check what you are agreeing to, and enter the code from your TAN card at the position the bank asks for.

After that, check the consent status from your notebook.

Checkpoint: your consent has status valid.

5.5 Read the data

Use your consent to read:

  1. the list of your client's accounts,
  2. the balances of the current account, and
  3. all transactions of the current account for the last three full calendar months and the current month, both booked and pending.

Watch out:

Keep the data in Python variables or in a pandas DataFrame. pd.DataFrame(list_of_dicts) is a good start.

Checkpoint: you have every booked and pending transaction of the current account, and you can print how many there are per month.

5.6 Analyse: how much must be transferred?

Compute the amount using exactly these rules:

  1. A recurring debit is a SEPA direct debit (same mandate) or a standing order (same creditor account) that was debited in each of the last three full calendar months. A direct debit that was returned or refunded does not count as debited for that month. Card payments are never recurring.
  2. Each recurring debit will be taken next month at the amount of its most recent occurrence.
  3. Start from the current account's closing booked balance. Assume all pending transactions will be booked at their amount in EUR. The overdraft must not be used.
  4. No salary or other incoming payment will arrive next month.
  5. Apply what your client told you on the phone (section D of your sheet).
  6. Transfer = (total of recurring debits + anything from the phone call) − (closing booked balance + total of pending transactions), rounded up to the next whole euro. If the result is negative, nothing needs to be transferred.

Tips:

Checkpoint: you have one amount in EUR and a table that justifies it.

5.7 Initiate the payment (PIS)

Initiate a SEPA credit transfer from your client's savings account to their current account for the amount you computed.

Things to find out from the documentation:

This time there is no link to open. Pétrusse Bank uses decoupled SCA for payments: the approval request appears in the app on your phone. Check the amount, the payee and the "name check" result shown in the app, then approve with your PIN.

Back in your notebook, follow the payment's authorisation status and then its payment status. The payment does not settle instantly. Poll the status until it is final, waiting a few seconds between requests (time.sleep(5)).

Checkpoint: the payment status is final. ACSC means your amount was right. RJCT means it was rejected; the answer tells you why. If it was the amount, you have 3 attempts in total.

5.8 Submit

Before the deadline, log in to the developer portal and open Submit:

  1. Upload your notebook (in Colab: File → Download → Download .ipynb).
  2. Answer the three reflection questions:
    • Describe in your own words the journey from your client's consent to your fintech reading the data.
    • Which pitfalls did you run into, and how did you notice them?
    • How did you use AI? Where was it helpful, and where was it wrong or missing information?

You may resubmit until the deadline; the latest version counts.

6. In-class review

On Monday, 14 December 2026 you answer three personal questions in writing (about 10 minutes). The questions are generated from your log: your requests, your errors, your consents, your payment attempts. If you understood what you did at each step, you will be fine. If you can't attend, contact georges@berscheid.lu to arrange a short video call.

7. When something goes wrong

What you seeWhat it usually means
400Your request is malformed: a missing header, wrong format, wrong parameter. Read tppMessages.
401Your token is missing or expired, or your consent doesn't allow this request.
403 / 404You asked for something that doesn't exist or isn't yours. Check IDs and paths.
429Too many requests. Slow down, or read about the PSD2 access limit (step 5.5).
SCA page refuses your PINAfter 5 wrong PINs the client is locked for 15 minutes.
TAN rejected 3 timesThat authorisation failed. Start again with a new consent or payment.
App shows nothing to approveCheck that the payment request succeeded and that you told the bank who the client is.

Your My activity page in the portal shows every request the bank received from you, which makes it the fastest way to find a mistake.

8. Glossary

TermMeaning
ASPSPThe bank that holds the account (here: Pétrusse Bank)
TPPThird Party Provider: your fintech
AISP / PISPA TPP that reads account information / initiates payments
PSUPayment Service User: the bank's client
ConsentThe client's permission for a TPP to access specific data, for a limited time
SCAStrong Customer Authentication: at least two of knowledge (PIN), possession (TAN card, phone) and inherence (fingerprint)
Redirect / decoupled SCAThe client authenticates on the bank's web page / in the bank's app
Booked / pendingFinal transactions / transactions not yet final
IBANInternational Bank Account Number
VoPVerification of Payee: the bank checks that the payee's name matches the account
ACSC / RJCTISO 20022 payment statuses: settled / rejected