Scoped Mandate – Feature-Brief für EUDIPal

Stand: v1.0, 07.10.2026 · Autor: Oliver Lauer (digitallabor.berlin) · Zweck: Handoff an Claude Code in VS Code. Lies zuerst dieses Dokument vollständig, dann den bestehenden Code. Nichts umbauen, was nicht in Abschnitt 6 steht.


1. Was gebaut werden soll (ein Absatz)

EUDIPal bekommt einen Agenten-Shopping-Flow mit begrenztem Mandat: Der Nutzer autorisiert einmalig per SCA aus der Wallet ein Mandat für einen identifizierten KI-Agenten (Höchstbetrag je Kauf, Gesamtlimit, Laufzeit, erlaubte Händler/Kategorien). Danach findet und kauft der Agent innerhalb dieser Grenzen eigenständig – ohne weitere Freigabe pro Transaktion. Das Mandat liegt immer bei der Bank, nie in der Wallet. EUDIPal ist Autorisierungskanal, Agenten-Oberfläche (BYOAI) und Spiegel der erteilten Mandate.

2. Grundsätze (nicht verhandelbar)

  1. Die Wallet stellt kein Mandats-Credential aus. Sie präsentiert das bank-ausgestellte Sparkassen-Credential und autorisiert dabei die Mandatsdaten als transaction_data (PaSO Generic Mandate). Diese eine Presentation ist die SCA mit Dynamic Linking auf Limits + Agenten-Identität.
  2. Die Bank hält, prüft und widerruft das Mandat. Sie prüft nur den Payment-Scope (Betrag, Limit, Laufzeit, Händler/Kategorie, Agent). Sie sieht keine Warenkorbpositionen.
  3. Der Händler prüft den Fach-Scope (Warengruppen, Ausschlüsse, Alter) und stellt eine signierte Konformitäts-Attestation aus, deren Hash in die Zahlung eingeht.
  4. Der Agent hat eine eigene Identität (Legal-Person-Credential des Betreibers, European Business Wallet) und haftet für Mandatstreue im Übrigen.
  5. Keine Authentifizierungsfaktoren beim Agenten. Der Agent hat nie Zugriff auf Wallet-Keys, PIN oder Biometrie.
  6. Payment-Rail: Wero/EUDI Pay (A2A) wie bereits im Kartenstapel; SEPA-Lastschrift optional als zweite Variante.

3. Rollen und Komponenten

Komponente Wo Aufgabe im Prototyp
EUDIPal Wallet iOS App (bestehend) Presentation des Sparkassen-Credentials mit transaction_data; Mandats-Spiegel; Agenten-Aktivitätslog
EUDIPal Agent iOS App, BYOAI-Layer (bestehend) Shopping-Agent mit eigener Identität; nutzt Voice/Chat-Interaktion wie heute; ruft Merchant- und Bank-APIs
Sparkasse Mandate Service neuer Mock-Backend-Dienst (oder in-app Simulation, Phase 1) Credential-Issuer, Mandatsspeicher, Payment-Scope-Prüfung, Widerruf, Ausführung über Wero-Mock
Merchant Service neuer Mock (Katalog + Checkout) Produktsuche, Prüfung Warenkorb gegen Fach-Scope, signierte Attestation; Schnittstelle UCP-ähnlich
Agent Identity Mock-EBW-Credential Legal-Person-Credential des Agenten-Betreibers, wird Händler und Bank präsentiert

Für Phase 1 dürfen Bank und Händler als lokale Module in der App simuliert werden, aber mit sauberen Interfaces, damit sie in Phase 2 gegen echte Endpunkte (PaSO-Referenzimplementierung) getauscht werden können.

4. Datenmodelle (JSON, als Vorschlag – an bestehende Typen anpassen)

// Mandat, wie es die Bank hält und die Wallet spiegelt
{
  "mandate_id": "mnd_…",
  "status": "active | revoked | expired | exhausted",
  "principal": { "credential_ref": "sparkasse_credential_id" },
  "agent": {
    "agent_id": "agt_…",
    "operator_legal_person": "…",        // aus EBW-Credential
    "ebw_credential_hash": "…"
  },
  "payment_scope": {
    "currency": "EUR",
    "max_per_transaction": 60.00,
    "max_total": 300.00,
    "valid_from": "2026-10-07T00:00:00Z",
    "valid_until": "2026-11-07T00:00:00Z",
    "merchant_whitelist": ["merchant_schwarz_demo"],
    "merchant_categories": ["5411"],      // MCC-Logik, optional
    "rail": "wero"
  },
  "functional_scope": {
    "allowed_categories": ["groceries", "household"],
    "excluded_categories": ["alcohol", "tobacco", "electronics"],
    "age_restricted_allowed": false
  },
  "spent_total": 0.00,
  "created_at": "…", "revoked_at": null
}
// transaction_data der Presentation (PaSO Generic Mandate) – das ist der SCA-Gegenstand
{
  "type": "generic_mandate",
  "mandate_request_id": "mrq_…",
  "payment_scope": { …wie oben… },
  "functional_scope": { …wie oben… },
  "agent": { "agent_id": "agt_…", "ebw_credential_hash": "…" },
  "creditor_display": "Sparkasse – Mandat für Agent ‹Name›"
}
// Händler-Attestation (vom Merchant Service signiert)
{
  "attestation_id": "att_…",
  "mandate_id": "mnd_…",
  "merchant_id": "merchant_schwarz_demo",
  "basket_hash": "sha256:…",
  "conforms_to_functional_scope": true,
  "checks": ["categories_ok", "no_exclusions", "no_age_restricted"],
  "amount": 42.80,
  "issued_at": "…",
  "signature": "…"
}
// Zahlungsauftrag des Agenten an die Bank
{
  "mandate_id": "mnd_…",
  "agent_id": "agt_…",
  "agent_proof": "…",                     // Signatur mit Agenten-Key
  "merchant_id": "merchant_schwarz_demo",
  "amount": 42.80,
  "attestation_hash": "sha256:…",
  "basket_hash": "sha256:…"
}

5. Flows

Flow A – Agent registrieren (einmalig, Setup-Screen)

  1. Nutzer wählt im BYOAI-Bereich „Shopping-Agent“.
  2. App lädt/erzeugt Mock-EBW-Credential des Agenten-Betreibers, zeigt Name, Betreiber, Credential-Hash.
  3. Agent bekommt eigenes Schlüsselpaar (Secure Enclave), getrennt von Wallet-Keys.

Flow B – Mandat autorisieren (der einzige SCA-Moment)

  1. Nutzer sagt/tippt z. B. „Lass den Agenten bis 300 € im Monat Lebensmittel bei Lidl kaufen, max. 60 € pro Einkauf, kein Alkohol.“
  2. Agent erzeugt MandateRequest → Mandate Service (Bank) → erzeugt mandate_request_id und OpenID4VP-Request mit transaction_data.
  3. Consent-Screen (neu): zeigt vollständig und unveränderlich: Agent (Name, Betreiber), Payment-Scope, Fach-Scope, Laufzeit. Button „Mit Sparkassen-Credential autorisieren“.
  4. Wallet präsentiert Sparkassen-Credential + transaction_data (Biometrie/PIN wie bei bestehender Wero-Freigabe). Kein eigenes Credential wird erzeugt.
  5. Bank speichert Mandat active, gibt mandate_id zurück. Wallet speichert nur einen Spiegel (read-only, mit Verweis auf Bank).

Flow C – Agent kauft ein

  1. Agent sucht im Merchant Service (Katalog-API, UCP-ähnlich), stellt Warenkorb zusammen.
  2. Merchant Service prüft Warenkorb gegen functional_scope (bekommt den Scope vom Agenten als Teil des Mandats-Auszugs), verifiziert Agenten-Identität, erstellt MerchantAttestation.
  3. Agent sendet Zahlungsauftrag an Mandate Service.
  4. Bank prüft nur: Mandat aktiv, Agent passt, Betrag ≤ max_per_transaction, spent_total + amount ≤ max_total, Händler/Kategorie erlaubt, Laufzeit, Attestation-Hash vorhanden. Führt Wero-Mock aus, erhöht spent_total.
  5. Wallet erhält Push/Event, Aktivitätslog zeigt: Händler, Betrag, verbleibendes Limit, Attestation ✔.

Flow D – Widerruf und Dispute

6. Konkrete Arbeitspakete (in dieser Reihenfolge)

  1. Types & Interfaces: Mandate, MandateRequest, MerchantAttestation, AgentPaymentRequest; Protokolle MandateServiceProtocol, MerchantServiceProtocol, AgentIdentityProtocol.
  2. Mock Mandate Service (in-app, persistiert lokal): issue/verify/revoke, Limit-Buchhaltung, Ablehnungsgründe.
  3. Mock Merchant Service: ca. 20 Katalogartikel mit Kategorien, Fach-Scope-Prüfung, Attestation-Signatur (Mock-Key).
  4. Agent Identity: Mock-EBW-Credential + Agenten-Keypair, getrennt von Wallet-Keys.
  5. Consent-Screen im bestehenden Wallet-Design (Flow B, Schritt 3); Presentation über den vorhandenen Wero/EUDI-Pay-Freigabepfad mit zusätzlichem transaction_data.
  6. Mandats-Spiegel als neue Karte im Kartenstapel („Agenten-Mandate“) mit Status, Limits, Verbrauch, Widerruf.
  7. Agent-Flow im BYOAI-Layer: Voice-Intent → MandateRequest; Voice-Intent „kauf ein“ → Flow C; Aktivitätslog.
  8. Demo-Script (demo/scoped_mandate_demo.md): Happy Path, Limit-Ablehnung, Fach-Scope-Ablehnung, Widerruf.

Phase 2 (später, nicht jetzt): Mandate Service gegen die PaSO-Referenzimplementierung tauschen (https://github.com/digitallabor-berlin/payment-banking-demo bzw. https://digitallabor-berlin.github.io/payment-banking-demo/), transaction_data nach PaSO-Spec (https://aptitude-consortium.github.io/payments-and-sca-for-openid/latest/), Merchant Service gegen echtes UCP-Endpoint.

7. Leitplanken für Claude Code