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 see

Intro

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

ActorRole
Individual end user (Payer)initiates the payment, enters the payee’s alias
Payer’s pilot-pspvalidates, authenticates, debits payer’s digital-euro balance
despresolves alias → DEAN, routes the instruction, runs settlement
Payee’s pilot-pspreceives 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.10 are 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 a TransactionStatus (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:

MethodPathoperationIdUsed for
POST/v1/payment-instructionspaymentPayee’s PSP receiving the payment instruction from DESP
POST/v1/paymentspaymentsPayer’s PSP receiving the corresponding payment request from DESP
GET/callback/v1/payment-instructions/{payment-instruction-id}/statuspaymentinstructionStatusAsync status push for a payment instruction
GET/callback/v1/payments/{payment-id}/statuspaymentsStatusUpdateAsync status push for a payment
GET/v1/payments/{payment-id}/status-inquiriespaymentsStatusInquiryPoll status if a callback is missed
POST/v1/fundingsfundingsRequestDESP-driven funding of the digital-euro account (reverse waterfall)
POST/v1/defundingsdefundingsRequestDESP-driven defunding to commercial bank account (waterfall)
GET/callback/v1/fundings/{funding-id}/statusfundingsStatusStatus of a funding
GET/callback/v1/defundings/{defunding-id}/statusdefundingsStatusStatus 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: currency ISO 4217 + amount string, up to 14 significant digits)
    • encryptedPaymentInstructionObject — JWE-encrypted EncryptedPaymentInstructionObject containing creditorAccount/debtorAccount (AccountReference = DEAN or IBAN), creditor/debtor party 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 (TransactionStatus enum: ACTC / ACCP / ACSC / RJCT), statusReason, creditorEntry (EntryId), optional paymentDefundingObject (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:

MethodPathoperationIdUsed for
POST/v1/payment-instructionspaymentinstructionsPayer’s PSP sends the instruction here first
POST/v1/paymentspaymentsDESP forwards the corresponding request onward
GET/v1/payments/{payment-id}/status-inquiriespaymentsStatusInquiryPoll payment status
GET/v1/payment-instructions/{payment-instruction-id}/statuspaymentInstructionStatusPoll instruction/payment/refund status
POST/v1/fundingsfundingsRequestReverse-waterfall funding, DESP side
POST/v1/defundingsdefundingsRequestWaterfall defunding, DESP side

Plus, for the alias resolution step specifically — per spec-alias (Alias.yaml):

MethodPathoperationIdPurpose
POST/v1/lookups/aliasesgetAliasResolve alias DEAN + payee’s PSP
POST/v1/lookups/deansgetIntermediaryResolve 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. TELE for 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.03 decrease-on-payer-side / sTM-32.03/.04 increase-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.07 are 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 that sTM-32 exists 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.2 reads 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)

TM 3.C wireframe

  1. 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).
  2. 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.
  3. 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)

TM 8.A wireframe

  1. 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.
  2. Transaction history screen — grouped by Today / This week / Future payments (scheduled recurring charges), each row showing counterparty + signed amount; Search and Export actions.
  3. 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.