QR Code Payments in Europe — Initiation, Tracking, and Getting Notified

The ideal payment experience for a merchant: the customer scans a QR code, does something in their banking app, and the till beeps green. No card machine, no card fees, no card network. Just a bank transfer. The money is in your account in seconds.

This is technically achievable in the EU today — but the path there is more nuanced than it first appears. There are three meaningfully different approaches, each with different trade-offs around friction, tracking reliability, and infrastructure complexity. This post maps all of them.


The Fundamental Problem

EPC QR codes (covered here) solve one half of the problem beautifully: they pre-fill the banking app with the recipient's IBAN, name, amount, and payment reference. The user taps confirm. Done.

What they don't solve is the other half: how does the merchant know the payment happened?

Once the user presses confirm in their banking app, the bank executes a standard SEPA Credit Transfer. No webhook fires to your server. No API call goes out. The bank has no idea who generated the QR code or why — it just sees a credit transfer with a reference string. The EPC standard has no notification mechanism.

So you're left with:

  1. Trust — display "thank you" and hope they paid (bad for anything above a donation jar)
  2. Poll your own account — check for incoming transactions periodically via AISP
  3. Use a PISP — initiate the payment yourself, get notified when the bank executes it

Approach 1: EPC QR + Polling Your Own Account

How it works:

  1. Generate an EPC QR code with a unique payment reference per transaction (ISO 11649 structured creditor reference — more on this below)
  2. Display it to the customer
  3. Customer scans it, their banking app opens pre-filled, they confirm
  4. Your server polls your own bank account via an AISP API, looking for an incoming transaction matching the reference
EPC QR (pre-filled IBAN + amount + unique ref)
  ↓ customer scans + confirms
  ↓ SEPA Credit Transfer executes
  ↓ your server polls GoCardless API every N minutes
  ↓ transaction appears with matching reference
  ↓ payment confirmed, order fulfilled

Why this can work well:

The polling rate limit problem:

Under PSD2's RTS (Article 36), banks must allow at minimum 4 AIS (Account Information Service) requests per day per account without requiring additional customer authentication. This is the regulatory floor — some banks allow more on dedicated interfaces.

What this means in practice: if you're polling your merchant account via GoCardless four times a day, you'd detect a payment within a 6-hour window at worst. That's fine for invoices and asynchronous payments, completely wrong for point-of-sale.

Getting around the rate limit:

Several strategies help:

The reference matching piece:

For this approach to work, the payment reference must be unique and reliably transmitted. Use a structured creditor reference (ISO 11649) — a standardised format that surviving transit through most European banking systems:

RF + 2-digit checksum + up to 21 alphanumeric characters

Example: RF93 ORDER 42 2026 0225 — but in practice, most libraries generate this for you from an arbitrary identifier.

Put the structured reference on line 10 of the EPC payload (not line 11 — see the EPC QR post for the format). Banks preserve it with higher fidelity than free-text remittance info.

BCD
002
1
SCT

Acme Merchant GmbH
DE89370400440532013000
EUR24.99

RF93ORD420260225
            ↑ structured creditor reference

When the transfer lands in your account, your AISP response will include this reference in remittanceInformationStructured, letting you match it to the pending order.


Approach 2: PISP with QR Code Redirect

How it works:

Instead of an EPC QR code (which encodes IBAN + amount directly), the QR code contains a URL pointing to your server. The user scans it and is taken through a PISP-initiated payment flow:

QR code → https://yourapp.com/pay/order-id-42
  ↓ user scans on phone
  ↓ mobile browser opens
  ↓ your server creates payment initiation via Tink/Salt Edge/etc.
  ↓ user redirected to their bank's authorisation page
  ↓ user authenticates with SCA (Face ID / fingerprint / OTP)
  ↓ bank executes SCT or SCT Inst
  ↓ PISP provider sends webhook to your server
  ↓ merchant notified within seconds

The SCA step:

This is the unavoidable friction in the PISP approach. PSD2 mandates SCA on every payment initiation — the user must authenticate with their bank directly before any money moves. Banks cannot delegate this to the PISP.

What SCA looks like from the user's perspective:

For most users in countries where mobile banking is mature (Germany, Netherlands, Nordics), this is a ~10-15 second process via app-to-app redirect. Still more friction than confirming a pre-filled EPC payment, but not dramatically so.

Why PISP is worth it:

The webhook payload (Tink-style):

{
  "event": "payment.status.changed",
  "paymentId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "status": "SETTLED",
  "amount": { "value": 24.99, "currency": "EUR" },
  "reference": "ORDER-42",
  "creditorIban": "DE89370400440532013000",
  "settledAt": "2026-02-25T14:31:02Z"
}

SETTLED means the bank has confirmed execution. For SCT Inst, this arrives in under 10 seconds from authorisation.


Approach 3: Hybrid — EPC QR + Merchant-Side Neobank Webhook

A middle ground that avoids both the polling frequency problem and the SCA redirect:

  1. Use an EPC QR code for zero-friction customer UX
  2. Open your merchant account at a provider with real-time incoming payment webhooks (Revolut Business, Wise Business, Bunq, Stripe)
  3. Configure the webhook to notify your server on every incoming credit
Customer scans EPC QR → confirms in their banking app
  ↓
SCT Inst lands in your Revolut Business account (< 10s)
  ↓
Revolut fires webhook to your server
  ↓
Your server matches reference → marks order paid
  ↓
Merchant UI updates

Why this works:

Why it doesn't always work:


SEPA Instant — The Game Changer

SCT Inst (SEPA Instant Credit Transfer) is what makes any of these approaches viable for point-of-sale:

Before the mandatory instant payments regulation, only ~60% of EU banks offered SCT Inst at all. The mandate changes this: by end of 2025, every EU bank must send and receive instant payments. This is the foundation that makes near-real-time reconciliation realistic.


Provider Comparison for PISP Flows

If you go the PISP route (approach 2), here's how the main providers compare for payment initiation:

Provider Webhook notification Countries SCA model Notes
Tink (Visa) ✅ Real-time 18 EU countries App-to-app / redirect Most enterprise-grade; requires commercial contract
TrueLayer ✅ Real-time UK + key EU markets App-to-app (biometric) "Pay by Bank" product; 40% of UK Pay by Bank volume; 20M consumer network; merchant account included; WooCommerce/Shopify plugins; hosted & embedded checkout options
Yapily ✅ Webhooks 19 countries (UK + EU, strong in UK & Germany) App-to-app / redirect Developer-API focused; both AISP + PISP from one integration; Variable Recurring Payments supported; used by Pleo, Crezco
Salt Edge ✅ Callbacks 50+ countries Redirect Good EU coverage; Partner Programme
Token.io ✅ Webhooks 18 EU countries App-to-app / redirect Payment-initiation focused; clean API
Banked ✅ Webhooks UK + limited EU Redirect UK-strong; EU coverage growing
GoCardless ❌ AISP only 31 countries N/A No PISP — read-only
Plaid ❌ AISP only (EU) 6 EU markets N/A Payment initiation EU not offered

For a European merchant wanting QR-initiated payments with webhook notification, Tink or Token.io are the strongest choices. Token.io is notably focused on the payment initiation use case specifically.


Putting It Together: A Practical Point-of-Sale Architecture

Here's a concrete design for a small merchant wanting QR-to-notification payments:

Option A (low friction, slightly slower): EPC QR + Neobank webhook

Order created
  ↓
Generate unique ISO 11649 reference for this order
Generate EPC QR:  IBAN + amount + reference
Display QR on POS screen / print on invoice
  ↓
Customer scans with banking app → confirms
SCT Inst settles in < 10s
  ↓
Revolut/Wise/Bunq webhook fires to your server
Match reference → mark paid
POS updates

Infrastructure: your server + webhook receiver + Revolut Business API. No PISP licence. Cost: ~0 (Revolut Business account is free for basic use).

Option B (full tracking): PISP redirect QR

Order created → your server generates a payment session
QR code → https://pay.yourapp.com/session/{id}
  ↓
Customer scans on phone
Mobile browser → your server redirects to PISP provider (e.g. Token.io)
PISP redirects to customer's bank for SCA
Customer authenticates, confirms payment
Bank executes SCT Inst
PISP webhook → your server
Order marked paid → POS updates

Infrastructure: server + PISP API integration + webhook receiver. Requires commercial agreement with PISP provider. Cost: per-transaction fee (typically 0.1–0.3% or fixed pence/cents per transaction — considerably cheaper than card).

Option C (simplest, async): EPC QR + AISP polling (for invoices)

For invoices and non-instant use cases (online orders, B2B):

Invoice sent with EPC QR embedded
Customer pays whenever
AISP polls your account 4x/day
Reference matched → invoice marked paid
Customer and you both notified

No real-time notification, but for payment terms of net-7 or net-30, this is completely fine and the simplest possible implementation.


A Note on Verification of Payee

From October 2025, all EU instant payments require VoP — the sending bank must verify that the beneficiary name in the payment matches the IBAN owner before proceeding. The EPC and PISP flows both go through this check.

What this means:


Summary: Which Approach for Which Use Case?

Use case Recommended approach Why
Physical POS (café, market stall) Option A — EPC QR + neobank webhook Low friction, fast with SCT Inst
Online checkout Option B — PISP redirect QR Full tracking, redirect acceptable on desktop
Invoicing / B2B Option C — EPC QR + AISP polling Async is fine; simplest build
Donations EPC QR, no tracking Users don't expect confirmation
High-value transactions Option B — PISP Verification and audit trail critical

The underlying SEPA infrastructure — especially SCT Inst becoming mandatory across all EU banks — means bank-transfer payments with near-real-time confirmation are no longer aspirational. The tooling (neobank webhooks, PISP providers with proper webhook support) is mature enough to build on today.

← All posts