Webshop: Recherche eingearbeitet (kein PayPal bei Connect, Gewährleistungshinweis)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-10-05 08:30:56 +00:00
1 parent e9ab15bdb5
commit 308268e901
2 files changed
+17 -8

No files matched your search

+9 -3
View File
@@ -30,6 +30,11 @@ Website und zahlen über Stripe Checkout direkt an den Kunden; Bestellungen, Ver
- Appwrite-Felder: kein Feld namens `data`; Dokument-IDs ≤ 36 Zeichen `[A-Za-z0-9][A-Za-z0-9._-]*`. - Appwrite-Felder: kein Feld namens `data`; Dokument-IDs ≤ 36 Zeichen `[A-Za-z0-9][A-Za-z0-9._-]*`.
- Käufer-PII nie in Logs; Fehlertexte an Browser nur eigene deutsche Texte. - Käufer-PII nie in Logs; Fehlertexte an Browser nur eigene deutsche Texte.
- **Recht aus `recherche-recht-stripe.md` (verbindlich):** Stripe Accounts **v1** mit `controller`-Eigenschaften (v2 nicht nötig); Webhook prüft `livemode` (Live-Schlüssel ⇒ nur `livemode:true`);
kein PayPal; SEPA asynchron (Abschluss erst bei `async_payment_succeeded`). Harmonisierter **Hinweis auf die gesetzliche Gewährleistung** (VO (EU) 2025/1960 Anhang I, Art. 246a § 1 Abs. 1 Nr. 11 EGBGB, seit 27.09.2026)
hervorgehoben in Kasse (über dem Knopf) und in G09 – Text in `shop/rechtstexte.js` `gewaehrleistungsHinweis()` (Wortlaut nach Anhang I, Kanzlei-Frage, Grafik später).
Ausgeschlossene Waren (LB + Anfrage): Tabak/E-Zigaretten, Alkohol, Arzneimittel, Waffen/Feuerwerk, Erotik, lebende Tiere, digitale Inhalte, Gutscheine.
## Schnittstellen über Blockgrenzen (verbindlich) ## Schnittstellen über Blockgrenzen (verbindlich)
- `config.shop` (A1): `{ mode: SHOP_ENABLED ('off'|'pilot'|'on', Standard 'off'), pilotEmails: SHOP_PILOT_EMAILS (Liste, '*' = alle), - `config.shop` (A1): `{ mode: SHOP_ENABLED ('off'|'pilot'|'on', Standard 'off'), pilotEmails: SHOP_PILOT_EMAILS (Liste, '*' = alle),
@@ -163,7 +168,7 @@ Tests: Registerwerte, `oeffentlicheAnsicht('shop')`, Haken, legalPaths('shop'),
### Task C2: Einstellungen (rein + Speichern) ### Task C2: Einstellungen (rein + Speichern)
`server/services/shop/einstellungen.js` + Tests. Felder: `shopname` (1–80), `anbieter { name, strasse, plz, ort, land:'DE'|'AT', email, telefon, ustId? }` (Pflicht außer ustId/telefon), `server/services/shop/einstellungen.js` + Tests. Felder: `shopname` (1–80), `anbieter { name, strasse, plz, ort, land:'DE'|'AT', email, telefon, ustId? }` (Pflicht außer ustId/telefon),
`kleinunternehmer` bool, `versand { aktiv, laender: ['DE'] | ['DE','AT'], kostenCent { DE, AT? }, freiAbCent|null }`, `abholung { aktiv, adresse, hinweis ≤ 300 }` `kleinunternehmer` bool, `versand { aktiv, laender: ['DE'] | ['DE','AT'], kostenCent { DE, AT? }, freiAbCent|null }`, `abholung { aktiv, adresse, hinweis ≤ 300 }`
(mind. eins aktiv), `lieferzeit` (Pflicht, 3–80, z. B. „2–4 Werktage“), `zahlungsarten`: Teilmenge von `card|paypal|klarna` (card immer enthalten), (mind. eins aktiv), `lieferzeit` (Pflicht, 3–80, z. B. „2–4 Werktage“), `zahlungsarten`: Teilmenge von `card|klarna|sepa_debit` (card immer enthalten; PayPal geht bei Connect-Direct-Charges laut Stripe NICHT; SEPA nur mit Warnung „Zahlung erst nach Tagen sicher, Ware erst nach Zahlungseingang versenden“),
`akzent` (#rrggbb, Standard #111111), `mindestbestellCent|null`, `ruecksendekosten: 'kaeufer'|'haendler'`. `akzent` (#rrggbb, Standard #111111), `mindestbestellCent|null`, `ruecksendekosten: 'kaeufer'|'haendler'`.
`speichereEinstellungen(shop, einstellungen)` erhöht `version`, leert Kontext-Cache. Fehlerobjekt je Feld (deutsche Texte, Du-Form). `speichereEinstellungen(shop, einstellungen)` erhöht `version`, leert Kontext-Cache. Fehlerobjekt je Feld (deutsche Texte, Du-Form).
@@ -239,7 +244,7 @@ Dokument `offen` mit Schnappschuss `{ positionen, summen, lieferart, land, einst
dann Checkout auf dem Konto: `mode:'payment'`, `line_items` (je Position `price_data{ currency:'eur', unit_amount, product_data:{ name: 'Name – Variante', images? (öffentliche absolute Bild-URL) } }, quantity`), Versand als `shipping_options:[{ shipping_rate_data:{ type:'fixed_amount', fixed_amount:{amount, currency:'eur'}, display_name:'Versand' | 'Abholung', delivery_estimate? } }]`, dann Checkout auf dem Konto: `mode:'payment'`, `line_items` (je Position `price_data{ currency:'eur', unit_amount, product_data:{ name: 'Name – Variante', images? (öffentliche absolute Bild-URL) } }, quantity`), Versand als `shipping_options:[{ shipping_rate_data:{ type:'fixed_amount', fixed_amount:{amount, currency:'eur'}, display_name:'Versand' | 'Abholung', delivery_estimate? } }]`,
`shipping_address_collection:{ allowed_countries }` nur bei Versand, `billing_address_collection:'required'`, `phone_number_collection:{enabled:true}`, `payment_method_types` aus Einstellungen, `locale:'de'`, `expires_at` (30 min), `client_reference_id`, `metadata:{ wkShop:'1', shopId, orderId, bestellnummer }`, `payment_intent_data:{ metadata: gleich, description:'Bestellung <nr>' }`, `shipping_address_collection:{ allowed_countries }` nur bei Versand, `billing_address_collection:'required'`, `phone_number_collection:{enabled:true}`, `payment_method_types` aus Einstellungen, `locale:'de'`, `expires_at` (30 min), `client_reference_id`, `metadata:{ wkShop:'1', shopId, orderId, bestellnummer }`, `payment_intent_data:{ metadata: gleich, description:'Bestellung <nr>' }`,
`success_url` = geprüfte Rück-URL + `wk_bestellung=<id>&wk_t=<token>`, `cancel_url` = Rück-URL + `wk_abbruch=1`. Rück-URL nur, wenn Host ∈ erlaubte Hosts (`analyseDomains(project)` + Portal-Host), sonst `https://project.webklar.com/shop/<siteKey>`. `success_url` = geprüfte Rück-URL + `wk_bestellung=<id>&wk_t=<token>`, `cancel_url` = Rück-URL + `wk_abbruch=1`. Rück-URL nur, wenn Host ∈ erlaubte Hosts (`analyseDomains(project)` + Portal-Host), sonst `https://project.webklar.com/shop/<siteKey>`.
Scheitert Stripe ⇒ Bestellung `storniert` (Reservierung frei) und 503 mit Käufertext; PayPal/Klarna nicht freigeschaltet ⇒ einmal mit nur `card` wiederholen und dem Händler einen Hinweis in `stripeStatus.hinweis` speichern. Scheitert Stripe ⇒ Bestellung `storniert` (Reservierung frei) und 503 mit Käufertext; Klarna/SEPA nicht freigeschaltet ⇒ einmal mit nur `card` wiederholen und dem Händler einen Hinweis in `stripeStatus.hinweis` speichern.
### Task E2: Abschluss, Verwerfen, Ablauf ### Task E2: Abschluss, Verwerfen, Ablauf
`schliesseAb({ accountId, session, now })`: Sperre `shop-order:<orderId>`; Konto muss zum Shop passen; nur aus `offen` (oder `storniert` mit `grund:'abgelaufen'` und bezahlt ⇒ trotzdem bezahlt, Bestand ggf. negativ ⇒ Händler-Hinweis in K25 Block `ueberverkauft`); `amount_total` muss `summeCent` entsprechen (sonst trotzdem bezahlt, Verlauf-Vermerk + Team-Log); `schliesseAb({ accountId, session, now })`: Sperre `shop-order:<orderId>`; Konto muss zum Shop passen; nur aus `offen` (oder `storniert` mit `grund:'abgelaufen'` und bezahlt ⇒ trotzdem bezahlt, Bestand ggf. negativ ⇒ Händler-Hinweis in K25 Block `ueberverkauft`); `amount_total` muss `summeCent` entsprechen (sonst trotzdem bezahlt, Verlauf-Vermerk + Team-Log);
@@ -299,7 +304,7 @@ Funktioniert auch bei geschlossenem, aber `active`/`disabled`-Shop (Widerrufsrec
`public/shop-tab-bestellungen.js`: Filter, Liste, Detail, Aktionen (versendet mit Sendungsnummer, abholbereit, abgeschlossen, erstatten mit Betrag + Bestätigung), Widerrufe-Liste mit „erledigt“, CSV-Download (Zeitraum). Test. `public/shop-tab-bestellungen.js`: Filter, Liste, Detail, Aktionen (versendet mit Sendungsnummer, abholbereit, abgeschlossen, erstatten mit Betrag + Bestätigung), Widerrufe-Liste mit „erledigt“, CSV-Download (Zeitraum). Test.
### Task G6: Reiter Einstellungen + Rechtliches ### Task G6: Reiter Einstellungen + Rechtliches
`public/shop-tab-einstellungen.js` (alle Felder C2, Hinweis bei PayPal/Klarna „nur einschalten, wenn in Deinem Stripe-Konto aktiviert“), `public/shop-tab-rechtliches.js` (AGB/Widerrufsbelehrung/Formular bearbeiten, „Muster einsetzen“, Versand & Zahlung nur lesen, Impressum-/Datenschutz-URL, Bestätigungshaken + Knopf, Banner wenn Änderungen unbestätigt, Hinweis „Muster ohne Gewähr; lass Deine Texte prüfen, z. B. von einer Kanzlei oder einem Händlerbund“). Test. `public/shop-tab-einstellungen.js` (alle Felder C2, Hinweis bei Klarna/SEPA „nur einschalten, wenn in Deinem Stripe-Konto aktiviert“; SEPA-Warnung; Satz „PayPal ist über Stripe für Shops wie Deinen leider nicht verfügbar“), `public/shop-tab-rechtliches.js` (AGB/Widerrufsbelehrung/Formular bearbeiten, „Muster einsetzen“, Versand & Zahlung nur lesen, Impressum-/Datenschutz-URL, Bestätigungshaken + Knopf, Banner wenn Änderungen unbestätigt, Hinweis „Muster ohne Gewähr; lass Deine Texte prüfen, z. B. von einer Kanzlei oder einem Händlerbund“). Test.
--- ---
@@ -337,4 +342,5 @@ WOMS `src/lib/funktionenView.js` (`FUNKTIONEN` + `shop` mit Bestätigungstexten;
- [ ] K1 ganze Suite (Host, `--max-old-space-size`), Node-20-Lauf der Shop-Tests im Container. - [ ] K1 ganze Suite (Host, `--max-old-space-size`), Node-20-Lauf der Shop-Tests im Container.
- [ ] K2 Gesamt-Review (2 sonnet-Reviewer: Geld/Stripe/Bestellungen; Recht/UI/Widget), Fixrunde. - [ ] K2 Gesamt-Review (2 sonnet-Reviewer: Geld/Stripe/Bestellungen; Recht/UI/Widget), Fixrunde.
- [ ] K3 End-to-End mit Mocks: Server mit Appwrite-Mock + Fake-Stripe + SMTP-Senke, Playwright: Anfrage → Freigabe → Bestellung → Artikel → Öffnen → Kauf im Widget → Webhook → Mails → Versand → Erstattung → Widerruf; Screenshots nach `/root/reports/webshop-2026-10-05/ui/`. - [ ] K3 End-to-End mit Mocks: Server mit Appwrite-Mock + Fake-Stripe + SMTP-Senke, Playwright: Anfrage → Freigabe → Bestellung → Artikel → Öffnen → Kauf im Widget → Webhook → Mails → Versand → Erstattung → Widerruf; Screenshots nach `/root/reports/webshop-2026-10-05/ui/`.
- [ ] K3b Gewährleistungshinweis: siehe Global Constraints
- [ ] K4 Livegang-Anleitung `docs/funktionen/shop-livegang.md` (Spec 11) und Memory/Ledger aktualisieren. - [ ] K4 Livegang-Anleitung `docs/funktionen/shop-livegang.md` (Spec 11) und Memory/Ledger aktualisieren.
@@ -26,10 +26,10 @@ Stripe-Quittung und die Bestellbestätigung gehen an den Käufer, Rechnungen sch
eines schon bestehenden Stripe-Kontos per OAuth (später), alkoholische Getränke, Tabak, Arzneimittel, Waffen, eines schon bestehenden Stripe-Kontos per OAuth (später), alkoholische Getränke, Tabak, Arzneimittel, Waffen,
Erotik, Lebende Tiere und alles auf Stripes Liste verbotener Geschäfte (Bestellgate + Leistungsbeschreibung). Erotik, Lebende Tiere und alles auf Stripes Liste verbotener Geschäfte (Bestellgate + Leistungsbeschreibung).
**Warum nur Stripe (W1):** Stripe läuft schon für Abo und Funktionen. Stripe Checkout bietet PayPal, Klarna, Apple Pay **Warum nur Stripe (W1):** Stripe läuft schon für Abo und Funktionen. Stripe Checkout bietet Karte, Apple Pay, Google Pay,
und Google Pay als Zahlungsarten **innerhalb** desselben Checkouts an, wenn der Händler sie in seinem Stripe-Konto Klarna und SEPA-Lastschrift im selben Checkout an. **PayPal geht über Stripe bei Connect-Direct-Charges nicht** (Stripe-Doku,
einschaltet. Eine eigene PayPal-Anbindung hätte eine zweite Integration, einen zweiten Webhook und eine zweite Recherche A3); eine eigene PayPal-Anbindung wäre eine zweite Integration samt eigenem Webhook, eigener Erstattung und eigenem
Erstattungslogik bedeutet, ohne dem Käufer eine Zahlungsart mehr zu geben. Konto-Onboarding je Händler. Der Inhaber hat „nur Stripe“ ausdrücklich erlaubt; PayPal bleibt ein späterer Ausbau.
**Warum Stripe Connect mit eigenem Konto des Kunden (W2):** Das Geld fließt direkt vom Käufer an den Händler (Direct **Warum Stripe Connect mit eigenem Konto des Kunden (W2):** Das Geld fließt direkt vom Käufer an den Händler (Direct
Charges auf dem verbundenen Konto). WEBklar nimmt nie fremdes Geld an, behält keine Gebühr ein und haftet nicht für Charges auf dem verbundenen Konto). WEBklar nimmt nie fremdes Geld an, behält keine Gebühr ein und haftet nicht für
@@ -148,7 +148,8 @@ erscheinen an der Bestellung und als eigene Liste. CSV-Export (Bestellungen eine
Shopname, Anbieter (Firma/Name, Anschrift, E-Mail, Telefon, optional USt-IdNr.), Steuer (Kleinunternehmer ja/nein), Shopname, Anbieter (Firma/Name, Anschrift, E-Mail, Telefon, optional USt-IdNr.), Steuer (Kleinunternehmer ja/nein),
Versand (an/aus, Länder DE / DE+AT, Versandkosten je Land, versandkostenfrei ab), Abholung (an/aus, Adresse, Hinweis z. B. Versand (an/aus, Länder DE / DE+AT, Versandkosten je Land, versandkostenfrei ab), Abholung (an/aus, Adresse, Hinweis z. B.
Öffnungszeiten), Lieferzeit (Pflicht, z. B. „2–4 Werktage“), Zahlungsarten (Karte inkl. Apple Pay/Google Pay immer; Öffnungszeiten), Lieferzeit (Pflicht, z. B. „2–4 Werktage“), Zahlungsarten (Karte inkl. Apple Pay/Google Pay immer;
PayPal, Klarna wählbar – nur einschalten, wenn im eigenen Stripe-Konto aktiviert; der Shop zeigt genau diese Liste), Klarna und SEPA-Lastschrift wählbar – nur einschalten, wenn im eigenen Stripe-Konto aktiviert; SEPA mit Warnung, weil die
Zahlung erst nach Tagen sicher ist; der Shop zeigt genau diese Liste),
Akzentfarbe, Mindestbestellwert (optional). Akzentfarbe, Mindestbestellwert (optional).
### 4.6 Rechtliches (Händler-Rechtstexte) und „Shop geöffnet“ ### 4.6 Rechtliches (Händler-Rechtstexte) und „Shop geöffnet“
@@ -343,4 +344,6 @@ K15/K16/T16 bekommen ihre Texte aus dem Register (vorhanden).
| W9 | Vertragsschluss im Widget mit „Zahlungspflichtig bestellen“, Stripe nur Zahlung | § 312j Abs. 3 BGB sicher erfüllt | | W9 | Vertragsschluss im Widget mit „Zahlungspflichtig bestellen“, Stripe nur Zahlung | § 312j Abs. 3 BGB sicher erfüllt |
| W10 | Widerrufsfunktion (§ 356a BGB) im Widget + Standalone-Seite | seit 19.06.2026 Pflicht | | W10 | Widerrufsfunktion (§ 356a BGB) im Widget + Standalone-Seite | seit 19.06.2026 Pflicht |
| W11 | Käuferdaten 3 Jahre nach Bestelljahr anonymisieren, Shopdaten 90 Tage nach Abschalten löschen | Datensparsamkeit, Gewährleistung | | W11 | Käuferdaten 3 Jahre nach Bestelljahr anonymisieren, Shopdaten 90 Tage nach Abschalten löschen | Datensparsamkeit, Gewährleistung |
| W13 | Hinweis auf die gesetzliche Gewährleistung (VO (EU) 2025/1960) in Kasse und Bestätigung | Pflicht seit 27.09.2026 |
| W14 | Stripe Accounts v1 mit `controller` statt v2 | v1 weiter unterstützt, passt zu stripe-node 19, kein Plattform-Umbau |
| W12 | Versand nur DE/AT, keine digitalen Produkte, keine Gutscheine | Umfang V1, Recht (digitale Inhalte, Mehrzweckgutscheine) | | W12 | Versand nur DE/AT, keine digitalen Produkte, keine Gutscheine | Umfang V1, Recht (digitale Inhalte, Mehrzweckgutscheine) |