Cross-PSP alias payment — how it works end to end
prompt
create Cross-PSP alias payment — how it works end to end.md and explain in detail how payment using alias to user on another psp works. Include diagram including
waterfall and reverse waterfall. we will also need details aborut API to be provided by PSP and DESP and how to store to account
balance and history. provide also screens user shoud seeIntro
Scope: an individual end user pays another individual end user identified by alias
(phone number) whose beta digital euro account is held at a different pilot PSP. This
is flow TM-3.5 — “P2P payment with alias (payer initiated)” in
src-end-to-end-process-flows §3.1.2, using the common online-payment backbone
described in transaction-management-flows. (The DEAN variant, TM-3.10, is identical
except it skips alias resolution and does a pilot-PSP-ID lookup instead — noted inline
where it differs.)
1. Actors
| Actor | Role |
|---|---|
| Individual end user (Payer) | initiates the payment, enters the payee’s alias |
| Payer’s pilot-psp | validates, authenticates, debits payer’s digital-euro balance |
| desp | resolves alias → DEAN, routes the instruction, runs settlement |
| Payee’s pilot-psp | receives the instruction, credits payee’s digital-euro balance |
| Individual end user (Payee) | receives the funds, gets notified |
Payer and payee each have an account at their own PSP; DESP is the only party both PSPs talk to — PSPs never call each other directly (see desp, pilot-psp).
2. End-to-end sequence
sequenceDiagram participant Payer as Individual end user (Payer) participant PPSP as Payer's Pilot PSP participant DESP participant QPSP as Payee's Pilot PSP participant Payee as Individual end user (Payee) Payer->>PPSP: Select "pay with beta digital euro" -> alias, amount, consent (TM-3.5.01-.04) PPSP->>PPSP: Authenticate payer [sAM-17.1] (.05) Payer-->>PPSP: Confirms PPSP->>PPSP: Validate instruction (.07-.10, reject path) Note over PPSP,DESP: Alias resolution [sAM-20] PPSP->>DESP: getAlias (POST /v1/lookups/aliases) - alias + typeCode TELE (.11-.14) DESP-->>PPSP: AliasLookupResponse: account (DEAN) + digitalEuroAgent (payee's PSP BIC) Note over PPSP,QPSP: Backbone: pre-settlement [sTM-34] PPSP->>PPSP: Payer PSP processing [sTM-50]: fraud/risk check, balance pre-check (payer) [sTM-31] PPSP->>DESP: payment-instructions (POST /v1/payment-instructions) DESP->>DESP: Validate payment request (.02-.04, reject -> unlock payer funds) DESP->>QPSP: Forward payment instruction QPSP->>QPSP: Payee PSP processing [sTM-51]: balance pre-check (payee) [sTM-33] QPSP-->>DESP: Response (incl. defunding amount if waterfall required) Note over DESP: Settlement [sTM-55] (DESP-hosted, never itself detailed in source) DESP->>DESP: Settle: debit payer's digital-euro account, credit payee's digital-euro account DESP-->>PPSP: Settlement confirmation DESP-->>QPSP: Settlement confirmation PPSP->>Payer: Payment confirmed (.22-.23) opt Reverse waterfall was needed on payer side PPSP->>PPSP: Debit payer's linked commercial bank account (optional debit, .24-.26) end QPSP->>Payee: Payment received, confirmation shown Note over QPSP,DESP: Post-settlement holding-limit check (waterfall) [sTM-32] (.27) QPSP->>QPSP: Credit payee's online balance up to holding limit opt Balance would exceed holding limit QPSP->>DESP: Defunding instruction (excess amount) DESP-->>QPSP: Defunding confirmation QPSP->>QPSP: Credit payee's linked commercial bank account with excess end
Key source notes that matter for implementation:
- Steps
.01–.10are entirely internal to the payer’s PSP — no DESP call yet. - Alias resolution (
sAM-20) always happens before the payment instruction is sent — the payer’s PSP never sends an alias to DESP as part of the payment itself; it resolves the alias to a(DEAN, digitalEuroAgent)pair first, then proceeds exactly like a DEAN-addressed payment (TM-3.10) from that point on. - Settlement itself (
sTM-55) is DESP-hosted and is the one subprocess never given its own step table anywhere in src-end-to-end-process-flows — treat it as an atomic black box: you send a payment/payment-instruction, you get back aTransactionStatus(ACTC/ACCP/ACSC/RJCT). - The payer’s commercial-bank debit is conditional (only fires if reverse waterfall
was needed to cover a shortfall); the payee-side waterfall crediting is not part of
inline settlement for P2P — it’s a separate, always-run subprocess (
sTM-32) that specifically exists to catch the case below.
3. Reverse waterfall (payer side — topping up to pay)
Triggered when the payer’s online digital-euro balance is insufficient to cover the
payment. Runs inside balance pre-check (payer) sTM-31, called from sTM-50
(payer PSP processing), before the instruction is even sent to DESP:
flowchart TD A["sTM-31.01 Check online digital-euro balance"] --> B{"sTM-31.02 Sufficient balance?"} B -- yes --> C["sTM-31.03 Decrease online balance by full amount"] --> H["Continue to sTM-50 / pre-settlement"] B -- no --> D["sTM-31.03 Decrease online balance by available amount only"] D --> E{"sTM-31.04 Reverse waterfall activated for this user?"} E -- no --> F["Reject transaction"] E -- yes --> G{"sTM-31.05 Pre-authorisation request?"} G -- yes --> G1["sTM-31.06/.07 Check pre-auth amount <= holding limit, else reject"] G -- no --> I["sTM-31.08 [optional, same-PSP-linked account only]\nCheck linked commercial bank account status + balance"] G1 --> I I --> J{"sTM-31.09 Sufficient commercial bank funds?"} J -- no --> F J -- yes --> K["sTM-31.10 Block funding amount on linked commercial bank account"] K --> H
Practical read: the digital-euro shortfall (amount requested − available online balance) is what gets blocked on the linked commercial bank account and later becomes
the conditional debit in the main sequence above (PaymentDefundingObject /
Funding on the settlement side). The linked-account check is skipped entirely if
the commercial bank account is held at a different PSP than the digital-euro account —
in that case the PSP can only trust that reverse waterfall is “activated” and let DESP’s
own funding flow handle it.
4. Waterfall (payee side — holding-limit overflow on receipt)
Runs after settlement, as sTM-32, specifically to catch two concurrent incoming
payments that each individually looked fine against the holding limit but together
breach it — this is why it can’t be folded into settlement itself:
flowchart TD A["sTM-32.01 Check payee's digital-euro balance"] --> B{"sTM-32.02 Would this credit exceed the holding limit?"} B -- no --> C["sTM-32.03 Increase online balance by full credited amount"] --> END["Done"] B -- yes --> D["sTM-32.04 Increase online balance only up to the holding limit"] D --> E{"sTM-32.05 Was this part of a concurrent transaction set?"} E -- no --> F["sTM-32.07 Credit linked commercial bank account\n(waterfall already handled during settlement)"] --> END E -- yes --> G["sTM-32.08 Determine excess amount to defund\n(amount above holding limit)"] G --> H["sTM-32.09 Send defunding instruction to DESP"] H --> I["sTM-32.10 Settlement defunding online [sLM-31]"] I --> J["sTM-32.11-.12 Defunding confirmation forwarded back to PSP"] J --> K["sTM-32.13-.14 Credit payee's linked commercial bank account with the excess"] K --> END
⚠ Source inconsistency (flagged, not resolved): step sTM-32.10 tags the subprocess it
calls as [sLM-31] (“Settlement defunding online”), but every other reference to
“Settlement defunding online” in the liquidity-management flows is tagged [sTM-57] —
either a same-named-but-distinct subprocess, or a source typo. Don’t assume they’re
interchangeable without checking the raw doc again if you hit this in practice.
Important asymmetry to build for: waterfall for a business end user payee has
no reverse (a merchant can be topped up but never auto-defunded below what it
received) — see funding-and-defunding. Also, for the merchant-payee flows
(TM-2.2/TM-2.4/TM-1.6), the commercial-bank credit is inlined as the flow’s own
final step rather than routed through sTM-32 — that routing difference only applies to
this P2P/individual-payee case and to TM-7.2 refunds.
5. APIs — what the PSP must implement (PSP-hosted, DESP calls in)
Per spec-settlementpsp (SettlementPSP_Pilot.yaml), the PSP exposes these on its own
backend for the flow above:
| Method | Path | operationId | Used for |
|---|---|---|---|
| POST | /v1/payment-instructions | payment | Payee’s PSP receiving the payment instruction from DESP |
| POST | /v1/payments | payments | Payer’s PSP receiving the corresponding payment request from DESP |
| GET/callback | /v1/payment-instructions/{payment-instruction-id}/status | paymentinstructionStatus | Async status push for a payment instruction |
| GET/callback | /v1/payments/{payment-id}/status | paymentsStatusUpdate | Async status push for a payment |
| GET | /v1/payments/{payment-id}/status-inquiries | paymentsStatusInquiry | Poll status if a callback is missed |
| POST | /v1/fundings | fundingsRequest | DESP-driven funding of the digital-euro account (reverse waterfall) |
| POST | /v1/defundings | defundingsRequest | DESP-driven defunding to commercial bank account (waterfall) |
| GET/callback | /v1/fundings/{funding-id}/status | fundingsStatus | Status of a funding |
| GET/callback | /v1/defundings/{defunding-id}/status | defundingsStatus | Status of a defunding |
Key request/response fields (from the raw YAML, components/schemas)
PaymentInstruction (body of POST /v1/payment-instructions):
acceptanceDateTime(IsoDateTime)paymentIdentification(PaymentIdentification2— the DESP-assigned end-to-end ref)paymentInstructionObject:paymentInstructionId(Uuid7)debtorAgent/creditorAgent(FinancialInstitutionIdentification1.bicfi)amount(Amount:currencyISO 4217 +amountstring, up to 14 significant digits)encryptedPaymentInstructionObject— JWE-encryptedEncryptedPaymentInstructionObjectcontainingcreditorAccount/debtorAccount(AccountReference= DEAN or IBAN),creditor/debtorparty descriptions,remittanceInformationUnstructured/Structured,purposeCode,transactionAmount. Only the encrypted form may be sent — the PSP is never allowed to send this payload in cleartext.
- Response (
PaymentInstructionResponse):paymentTransactionStatus(TransactionStatusenum:ACTC/ACCP/ACSC/RJCT),statusReason,creditorEntry(EntryId), optionalpaymentDefundingObject(instructedAgent+amount— the waterfall/reverse-waterfall leg riding on this payment).
Funding/Defunding (body of POST /v1/fundings and /v1/defundings — this is the
waterfall/reverse-waterfall wire format): fundingId/defundingId (Uuid7),
instructingAgent/instructedAgent (BICFI), amount (Amount). Response carries a
fundingTransactionStatus/defundingTransactionStatus (same TransactionStatus enum).
Common building blocks: AccountReference is oneOf Dean (pattern
[A-Z]{1,2}[0-9]{1,16}, e.g. EU10914567890123456) or Iban; Amount.amount is a
string, dot decimal separator, e.g. "299.99"; Bicfi is the 11-char SWIFT/BIC pattern.
6. APIs — what DESP provides
Per spec-settlementdesp (SettlementDESP_Pilot.yaml) — the mirror-image contract the
PSP calls into:
| Method | Path | operationId | Used for |
|---|---|---|---|
| POST | /v1/payment-instructions | paymentinstructions | Payer’s PSP sends the instruction here first |
| POST | /v1/payments | payments | DESP forwards the corresponding request onward |
| GET | /v1/payments/{payment-id}/status-inquiries | paymentsStatusInquiry | Poll payment status |
| GET | /v1/payment-instructions/{payment-instruction-id}/status | paymentInstructionStatus | Poll instruction/payment/refund status |
| POST | /v1/fundings | fundingsRequest | Reverse-waterfall funding, DESP side |
| POST | /v1/defundings | defundingsRequest | Waterfall defunding, DESP side |
Plus, for the alias resolution step specifically — per spec-alias
(Alias.yaml):
| Method | Path | operationId | Purpose |
|---|---|---|---|
| POST | /v1/lookups/aliases | getAlias | Resolve alias → DEAN + payee’s PSP |
| POST | /v1/lookups/deans | getIntermediary | Resolve DEAN → hosting PSP (used by the DEAN variant, TM-3.10) |
(Note: spec-alias’s own summary table lists getAlias/getIntermediary as GET;
the raw Alias.yaml operations for /v1/lookups/aliases and /v1/lookups/deans are
defined as POST with a request body — this doc follows the raw YAML as the source of
truth per this wiki’s spec-precision rule. Worth reconciling on the spec-alias page
itself at some point.)
AliasLookupResponse (what the payer’s PSP gets back from getAlias):
alias(ProxyAccountIdentification:typeCode— enum incl.TELEfor phone number —identification)account(AccountReference— the payee’s DEAN)digitalEuroAgent(FinancialInstitutionIdentification1.bicfi— the payee’s PSP)
This (account, digitalEuroAgent) pair is exactly what gets slotted into
creditorAccount/creditorAgent on the subsequent PaymentInstruction call — alias
resolution is a pure lookup, it never carries money or triggers settlement itself.
7. Storing account balance and transaction history
The specs above define the wire contract only — DESP never dictates how a PSP stores
its ledger internally. What the specs do fix, and what any internal design must satisfy,
comes from the balance/history-facing flows TM-8.1/TM-8.2.2/TM-8.3 and the
sTM-31/.32/.33 balance sub-flows in transaction-management-flows:
Balance record — must support, per user, per digital-euro account (DEAN):
- current online digital-euro balance (mutated synchronously by
sTM-31.03decrease-on-payer-side /sTM-32.03/.04increase-on-payee-side — this is why it must be an atomically updatable counter, not something recomputed from history on read) - the user’s configured holding limit, waterfall, and reverse-waterfall
flags (checked at
sTM-31.04,sTM-32.02,sTM-33.03/.06) — business end users never get a reverse-waterfall flag (funding-and-defunding) - a reference to the linked commercial bank account (may be at a different PSP, in
which case the optional linked-account checks in
sTM-31.08/sTM-33.07are skipped) - an open concurrent-transactions counter or set (
sTM-32.05/sTM-33.04) — needed specifically to detect the two-simultaneous-incoming-payments-each-individually-OK case thatsTM-32exists to catch. This means balance updates and the concurrency check must be part of the same atomic operation (e.g. row-level lock or compare-and-swap on the balance row), not a separate read-then-write. - offline balance is a separate figure held in the device’s secure application, not
the PSP backend (
TM-8.2.2reads it locally, no PSP round-trip) — don’t conflate it with the online balance field above.
Transaction history record — one row per completed/rejected transaction, must carry
enough to answer TM-8.3 and the TM 8.A transaction-details screen (§8 below):
transactionId/paymentInstructionId (Uuid7), direction (debit/credit), counterparty
display name, counterparty DEAN/alias (masked appropriately), amount + currency,
settlementDateTime, paymentType (P2P/POI/B2P/funding/defunding), status
(TransactionStatus), merchant category (MerchantCategoryCode, if payee is a business),
free-text message/remittance info, and payment method used (NFC/app/etc, per the
wireframe below). Reject reasons (StatusReason) should be retained too, since generic
UX requirements (generic-ux-requirements) mandate showing a functional rejection
reason to the user.
Reconciliation vs. end-user history — don’t conflate these two: DESP’s
spec-reports GET /v1/reports/transactions (Transactions schema) returns an
aggregated, per-agent settlement report (originatingDigitalEuroAgent, amount,
paymentInstructionId, settlementDateTime) for back-office reconciliation — it is
not a substitute for a PSP’s own per-end-user transaction history. The PSP must
maintain its own ledger/history table from the payment instructions it processes;
spec-reports is only useful for reconciling that ledger against DESP’s settlement
record, not for populating the app’s transaction history screen.
Suggested minimal schema (not spec-mandated, a design recommendation only):
digital_euro_account
dean (PK), owner_ref, currency, online_balance, holding_limit,
waterfall_enabled, reverse_waterfall_enabled, linked_commercial_account_ref,
concurrent_tx_lock_version
transaction_history
transaction_id (PK), account_dean (FK), direction, counterparty_dean,
counterparty_display_name, amount, currency, payment_type, status,
status_reason, settlement_datetime, merchant_category, payment_method,
remittance_info
8. Screens the end user sees
Wireframes below are the ECB pilot’s own UX-requirements mockups (TM 3.C and TM 8.A,
pages 63 and 69 of src-user-journeys-ux-requirements), reproduced faithfully. Full
gallery of all pilot wireframes: wireframe-gallery.
Sending a payment via alias (TM 3.C)
- Setup & Initiation
- Screen 1: account overview — shows “John Doe’s account” (IBAN-style DEAN display) with online balance, plus “John’s Phone” offline balance, and Receive/Send actions.
- Screen 2: payment entry form — fields for Alias / DEAN, Payee name (auto-filled once alias resolves), Amount, optional Message; a QR/contact picker icon and a manual-entry icon; Cancel available throughout (per generic-ux-requirements’s “payer can cancel before authentication starts” rule).
- Consent & Authentication
- Screen 3: confirmation summary — source account, amount (“10.00 EUR”), and payee, with Confirm/Cancel.
- Screen 4: authentication step (biometric/PIN unlock icon) — per
authentication, this is
[sAM-17.2]if redirect/consent is needed, or the seamless in-app variant.
- Confirmation
- Screen 5: green checkmark, amount and payee redisplayed — success confirmation.
- Screen 6 (payee’s device, out of band): a lock-screen push notification — “Payment received!” — matching the generic UX requirement that notification equivalents to other payment means be shown (generic-ux-requirements).
Balance & transaction history (TM 8.A)
- Beta digital euro accounts screen — lists each account (online DEAN-based account plus the on-device offline wallet) with balance, a hide-balances toggle, and per-account actions: Manage, Switch, Close, Default; an + add account action at the bottom.
- Transaction history screen — grouped by Today / This week / Future payments (scheduled recurring charges), each row showing counterparty + signed amount; Search and Export actions.
- Transaction details screen (drill-down from a history row) — counterparty + amount header, then Date & Time, Transaction ID, Merchant category, Message, Payment method (e.g. “NFC via Mobile app”), with Share and Dispute actions.
All three screens inherit the cross-cutting rules in generic-ux-requirements: no balance/history data shown pre-authentication, amounts always shown with currency, holding-limit breach warnings before authentication if relevant, and plain-language error states (≤ CEFR B2, no internal error codes) on any rejection path from the flows in §2–§4 above.
Sources
spec-settlementpsp · spec-settlementdesp · spec-alias · spec-reports ·
transaction-management-flows · funding-and-defunding · authentication ·
generic-ux-requirements — ultimately src-end-to-end-process-flows §3.1.2/§3.6,
src-user-journeys-ux-requirements (pages 63, 69), and
raw/ecb-pilot/specs/{SettlementPSP_Pilot,SettlementDESP_Pilot,Alias,Reports_Pilot}.yaml.

