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)¶
- 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. - 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.
- 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.
- Der Agent hat eine eigene Identität (Legal-Person-Credential des Betreibers, European Business Wallet) und haftet für Mandatstreue im Übrigen.
- Keine Authentifizierungsfaktoren beim Agenten. Der Agent hat nie Zugriff auf Wallet-Keys, PIN oder Biometrie.
- 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)¶
- Nutzer wählt im BYOAI-Bereich „Shopping-Agent“.
- App lädt/erzeugt Mock-EBW-Credential des Agenten-Betreibers, zeigt Name, Betreiber, Credential-Hash.
- Agent bekommt eigenes Schlüsselpaar (Secure Enclave), getrennt von Wallet-Keys.
Flow B – Mandat autorisieren (der einzige SCA-Moment)¶
- Nutzer sagt/tippt z. B. „Lass den Agenten bis 300 € im Monat Lebensmittel bei Lidl kaufen, max. 60 € pro Einkauf, kein Alkohol.“
- Agent erzeugt
MandateRequest→ Mandate Service (Bank) → erzeugtmandate_request_idund OpenID4VP-Request mittransaction_data. - Consent-Screen (neu): zeigt vollständig und unveränderlich: Agent (Name, Betreiber), Payment-Scope, Fach-Scope, Laufzeit. Button „Mit Sparkassen-Credential autorisieren“.
- Wallet präsentiert Sparkassen-Credential +
transaction_data(Biometrie/PIN wie bei bestehender Wero-Freigabe). Kein eigenes Credential wird erzeugt. - Bank speichert Mandat
active, gibtmandate_idzurück. Wallet speichert nur einen Spiegel (read-only, mit Verweis auf Bank).
Flow C – Agent kauft ein¶
- Agent sucht im Merchant Service (Katalog-API, UCP-ähnlich), stellt Warenkorb zusammen.
- Merchant Service prüft Warenkorb gegen
functional_scope(bekommt den Scope vom Agenten als Teil des Mandats-Auszugs), verifiziert Agenten-Identität, erstelltMerchantAttestation. - Agent sendet Zahlungsauftrag an Mandate Service.
- 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öhtspent_total. - Wallet erhält Push/Event, Aktivitätslog zeigt: Händler, Betrag, verbleibendes Limit, Attestation ✔.
Flow D – Widerruf und Dispute¶
- „Mandat beenden“ in der Wallet → Bank setzt
revoked; weitere Zahlungen werden abgelehnt. - Jede Ablehnung mit Grund im Log (
limit_exceeded,merchant_not_allowed,attestation_missing,mandate_revoked). - Demo-Fall: Agent versucht Kauf außerhalb Fach-Scope → Händler verweigert Attestation → kein Zahlungsauftrag. Und: Agent versucht Kauf über Limit → Bank lehnt ab.
6. Konkrete Arbeitspakete (in dieser Reihenfolge)¶
- Types & Interfaces:
Mandate,MandateRequest,MerchantAttestation,AgentPaymentRequest; ProtokolleMandateServiceProtocol,MerchantServiceProtocol,AgentIdentityProtocol. - Mock Mandate Service (in-app, persistiert lokal): issue/verify/revoke, Limit-Buchhaltung, Ablehnungsgründe.
- Mock Merchant Service: ca. 20 Katalogartikel mit Kategorien, Fach-Scope-Prüfung, Attestation-Signatur (Mock-Key).
- Agent Identity: Mock-EBW-Credential + Agenten-Keypair, getrennt von Wallet-Keys.
- Consent-Screen im bestehenden Wallet-Design (Flow B, Schritt 3); Presentation über den vorhandenen Wero/EUDI-Pay-Freigabepfad mit zusätzlichem
transaction_data. - Mandats-Spiegel als neue Karte im Kartenstapel („Agenten-Mandate“) mit Status, Limits, Verbrauch, Widerruf.
- Agent-Flow im BYOAI-Layer: Voice-Intent → MandateRequest; Voice-Intent „kauf ein“ → Flow C; Aktivitätslog.
- 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¶
- Bestehende Architektur, Naming und UI-Patterns von EUDIPal übernehmen; keine neuen Frameworks.
- Mandats-Logik strikt in den Mandate Service – die Wallet darf nie entscheiden, ob gezahlt wird.
- Agenten-Keys und Wallet-Keys technisch trennen (zwei Keychain-Scopes).
- Jede Ablehnung muss im UI erklärbar sein (Grund + welche Instanz abgelehnt hat: Bank oder Händler).
- Alle Beträge in Cent als Integer intern, Anzeige formatiert.
- Vor größeren Umbauten kurz rückfragen; kleine Entscheidungen selbst treffen und im Commit begründen.