27 KiB
Webshop (Funktion shop): Design
Stand 2026-10-05. Basis: Portal-Zweig feature/terminplaner-tp2 af63b69 (Register config.features, generischer Checkout,
Zustimmungsnachweis featureConsents, versionierte Rechtsunterlagen). Arbeitszweig feature/webshop, Worktree dev-webshop.
Pfade: P = Portal-Repo (kundenbereich.webklar.com), W = WOMS-Repo (ticket.webklar.com).
Fakten aus dem Code: /root/reports/webshop-2026-10-05/fakten-*.md; Recht und Stripe: recherche-recht-stripe.md.
Auftrag des Inhabers (2026-10-05, /goal): „Online-Webshop-Integration, auf Anfrage, einmalige Kosten, der Kunde
konfiguriert alle Artikel, Stripe-Anbindung (PayPal optional, nur Stripe ist ok), keine Rückfragen, Recht beachten.“
Alle Entscheidungen unten (W1–W30) hat Claude ohne Rückfrage getroffen; sie stehen gesammelt in Abschnitt 12.
1. Ziel und Abgrenzung
Der Webshop liefert: Ein Kunde fragt im Portal einen Webshop für eine seiner Websites an. Das Team prüft die Anfrage (was wird verkauft, ist es erlaubt, passt es) und gibt sie frei. Danach bestellt der Kunde die Einrichtung (einmalig 299 €, Stripe-Checkout wie bei den anderen Funktionen). Es entsteht ein Einrichtungs-Ticket, das Team baut den Shop in die Website ein. Der Kunde verbindet sein eigenes Stripe-Konto (Stripe Connect), pflegt Artikel, Versand, Abholung und seine Rechtstexte im Portal und öffnet den Shop. Endkunden kaufen im Widget auf seiner Website und bezahlen über Stripe Checkout direkt an den Kunden. Bestellungen, Versand, Erstattung und Widerrufe verwaltet der Kunde im Portal.
Nicht Teil (V1): digitale Produkte/Downloads, Abos/wiederkehrende Zahlungen, Gutscheine/Rabattcodes, Versand außerhalb Deutschlands und Österreichs, Lagerverwaltung über mehrere Lager, Rechnungserstellung durch WEBklar (die Stripe-Quittung und die Bestellbestätigung gehen an den Käufer, Rechnungen schreibt der Händler selbst), Anbindung 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).
Warum nur Stripe (W1): Stripe läuft schon für Abo und Funktionen. Stripe Checkout bietet Karte, Apple Pay, Google Pay, Klarna und SEPA-Lastschrift im selben Checkout an. PayPal geht über Stripe bei Connect-Direct-Charges nicht (Stripe-Doku, Recherche A3); eine eigene PayPal-Anbindung wäre eine zweite Integration samt eigenem Webhook, eigener Erstattung und eigenem 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
Charges auf dem verbundenen Konto). WEBklar nimmt nie fremdes Geld an, behält keine Gebühr ein und haftet nicht für
Rückbuchungen. Damit ist WEBklar kein Zahlungsdienst im Sinne des ZAG (Stripe ist das lizenzierte Institut), und der
Händler hat sein volles Stripe-Dashboard (Auszahlungen, Erstattungen, Belege). Konten werden mit den
controller-Eigenschaften angelegt: Stripe-Dashboard voll, Gebühren zahlt das Konto, Verluste trägt Stripe/Konto,
Anforderungen sammelt Stripe (Gegenstück zum früheren type: 'standard').
Pilot und Schalter (W3): SHOP_ENABLED=off|pilot|on wie TERMINE_ENABLED; pilot nur für SHOP_PILOT_EMAILS.
Pilotkunde ist das Testkonto (kenso.gri@gmail.com, Projekt test-website).
2. Ablauf aus Kundensicht
| Zustand | Kachel (Funktionen-Tab) | Ansicht #webshop |
|
|---|---|---|---|
| nichts angefragt | „Webshop anfragen“ | Anfrageformular | – |
| Anfrage offen | „Anfrage wird geprüft“ | Anfrage lesen, zurückziehen | T21 an Team |
| Anfrage abgelehnt | „Anfrage abgelehnt“ + Begründung, neue Anfrage möglich | Begründung | K24 |
freigegeben (off) |
„Webshop bestellen · 299 €“ | Bestellseite (generisch) | K24 |
ordered |
„Zahlung offen“ | Statuskarte, „Zahlung fortsetzen“ | – |
setup |
„In Einrichtung“ | Statuskarte + alle Reiter nutzbar (Artikel, Stripe, Rechtliches) | K15, T16 |
active |
„Webshop öffnen“ | Verwaltung | K16 |
disabled |
„Webshop ansehen“ | lesen, CSV-Export | – |
Auf Anfrage (W4): Der Inhaber will die Funktion „auf Anfrage“. Umsetzung als vorgeschaltete Freigabe: Die Kachel ist für jeden Kunden mit Website-Projekt sichtbar (wenn der Schalter es erlaubt), bestellbar wird sie erst nach Freigabe durch das Team. Die Anfrage fragt: Was wird verkauft (Freitext + Warenart: Waren/Lebensmittel/Kleidung/Kunst/Sonstiges), ungefähre Artikelzahl, Versand und/oder Abholung, Händler ist Unternehmer (Pflicht-Haken), keine der ausgeschlossenen Waren (Pflicht-Haken), Nachricht ans Team. Das Team sieht die Anfrage im WOMS (Kunde → Funktionen) und nimmt sie an oder lehnt sie mit Begründung ab. Der Preis bleibt fest (299 €); Sonderfälle (sehr viele Artikel, Sonderdesign) bespricht das Team im Chat und rechnet Mehraufwand nach Stunden ab, wie beim Terminplaner (Leistungsbeschreibung).
Einmalige Kosten (W5): 299 € einmalig (Kleinunternehmer, ohne USt gemäß § 19 UStG), kein Wartungs-Abo nötig, keine laufende Gebühr von WEBklar, keine Umsatzbeteiligung. Der Shop läuft, solange WEBklar die Website betreibt (Hosting oder Wartung). Stripe-Gebühren zahlt der Händler direkt an Stripe.
Ab setup nutzbar (W6): Anders als beim Terminplaner (Einstellung ab Bestellung gesperrt) pflegt der Kunde ab
setup Artikel, Stripe-Konto und Rechtstexte, damit der Shop zum Einbau fertig ist. Die Website zeigt den Shop erst,
wenn alles erfüllt ist (Abschnitt 4.6, „Shop geöffnet“).
3. Datenmodell (Appwrite, scripts/shop-setup.mjs, nur Server-Key)
Alle Collections ohne Client-Rechte (wie termineConfig); alles läuft über den Portal-Server.
shopConfig(Dokument-ID = ID desportalFeatures-Dokuments; eins je Projekt):customerId,projectId,siteKey(Kopie aus portalFeatures, Index),anfrage(JSON-Text ≤ 4000),anfrageStatus(''|offen|angenommen|abgelehnt|zurueckgezogen),anfrageAt,anfrageEntschiedenAt,anfrageEntschiedenVon,anfrageBegruendung(≤ 1000),stripeAccountId,stripeStatus(JSON ≤ 4000: charges_enabled, payouts_enabled, details_submitted, requirements.currently_due, disabled_reason, geprüft am),einstellungen(JSON ≤ 20000),rechtstexte(JSON ≤ 100000),rechtstexteBestaetigtAt,rechtstexteBestaetigtVon,rechtstexteFassung(Hash),geoeffnet(bool),geoeffnetAt.shopProducts:shopId(= shopConfig-ID, Index),customerId,aktiv(bool),sort(int),name(≤ 120),daten(JSON ≤ 30000: Kurz-/Langbeschreibung, Preis in Cent, Grundpreis-Menge+Einheit, Steuersatz, Bestand, Varianten, Bilder, Lieferart, Widerrufsausschluss, Produktsicherheit, Lebensmittelangaben, Kategorie).shopOrders:shopId(Index),customerId,bestellnummer(eindeutig je Shop, Index),status(Index),tokenHash,stripeSessionId(Index),paymentIntentId,summeCent,erstelltAt,ablaufAt,bezahltAt,kaeuferEmail(≤ 320),daten(JSON ≤ 60000: Positionen als Schnappschuss, Lieferart, Adressen, Versandkosten, Steuerausweis, Fassung der Rechtstexte, Verlauf),anonymisiertAt.shopWiderrufe:shopId(Index),customerId,bestellnummer,name,email,nachricht(≤ 2000),eingangAt,bestaetigungGesendetAt,bearbeitetAt.- Bilder auf der Platte unter
upload-data/shop/<siteKey>/<id>.<ext>(gitignored, wie News-Fotos), ausgeliefert über die öffentliche API.
Bestellnummer (W7): <JJ>-<6 Zeichen base32> (z. B. 26-K7QM2X), zufällig, bei Kollision im Shop neu ziehen. Keine
fortlaufende Zählung nötig (kein Rechnungsnummernkreis, Rechnungen schreibt der Händler).
4. Shop-Verwaltung im Portal (#webshop)
Reiter: Übersicht · Artikel · Bestellungen · Einstellungen · Rechtliches. Projektwahl wie beim Terminplaner (Shop je Website-Projekt).
4.1 Übersicht
Checkliste „Bereit zum Öffnen“ mit Haken: Einbau fertig (active), Stripe verbunden und Zahlungen freigeschaltet
(charges_enabled), Rechtstexte vollständig und bestätigt, mindestens ein aktiver Artikel, Versand oder Abholung
eingerichtet. Schalter „Shop geöffnet“ (nur wenn alle Haken grün; serverseitig erneut geprüft). Kennzahlen: offene
Bestellungen, Bestellungen 30 Tage, Umsatz 30 Tage (nur Anzeige). Knopf „Shop-Vorschau“ (Standalone-Seite, Abschnitt 5.4).
4.2 Stripe-Konto
Knopf „Stripe-Konto einrichten“: Server legt das verbundene Konto an (Land DE, E-Mail und Website des Kunden
vorbefüllt) und holt einen Onboarding-Link (Account Link), Weiterleitung zu Stripe; Rücksprung #webshop?stripe=zurueck
liest den Status neu. Danach: Status (Zahlungen aktiv / Auszahlungen aktiv / fehlende Angaben) und Knöpfe
„Angaben ergänzen“ (neuer Account Link) und „Stripe-Dashboard öffnen“ (dashboard.stripe.com). Status wird zusätzlich
per Connect-Webhook account.updated aktuell gehalten.
4.3 Artikel
Liste (sortierbar mit Hoch/Runter, aktiv/inaktiv, Bestand) und Editor:
- Name, Kategorie (frei, gruppiert im Shop), Kurzbeschreibung (≤ 300), Beschreibung (≤ 5000, Absätze, kein HTML).
- Preis brutto in € (PAngV: Gesamtpreis), Steuersatz (19 / 7 / 0 % – bei Kleinunternehmer ausgeblendet).
- Grundpreis (PAngV § 4): Menge + Einheit (g, kg, ml, l, m, m², Stück-los) → Anzeige „x € / 1 kg“ bzw. „/ 100 g“ nach § 5 PAngV (bei ≤ 250 g/ml darf 100 g/ml verwendet werden; V1 rechnet immer auf 1 kg / 1 l / 1 m / 1 m²).
- Bestand (leer = unbegrenzt) und „ausverkauft anzeigen“ statt ausblenden.
- Varianten (optional, eine Dimension, z. B. „Größe“): je Wert eigener Preis und Bestand (max 30).
- Bilder: bis 4, JPEG/PNG/WebP ≤ 8 MB, serverseitig geprüft (Signatur), erstes = Titelbild.
- Lieferart: Versand und Abholung / nur Abholung.
- Widerruf (§ 312g Abs. 2 BGB): „gesetzliches Widerrufsrecht“ (Standard) oder Ausschluss mit Grund: schnell verderblich, nach Kundenwunsch angefertigt, versiegelt aus Hygienegründen. Der Shop zeigt den Hinweis am Artikel und in der Bestellübersicht, die Bestellbestätigung nennt ihn.
- Produktsicherheit (GPSR Art. 19): Hersteller (Name, Anschrift, E-Mail/Website; Vorgabe „ich selbst“ = Anbieterdaten), bei Herstellern außerhalb der EU die verantwortliche Person in der EU, Warn- und Sicherheitshinweise (Freitext).
- Lebensmittel (LMIV Art. 14): Schalter „ist ein Lebensmittel“ → Pflichtfeld „Pflichtangaben“ (Bezeichnung, Zutaten, Allergene hervorgehoben, Menge, ggf. Hersteller), wird vor dem Kauf am Artikel gezeigt.
4.4 Bestellungen
Liste (Status-Filter, Suche nach Nummer/Name), Detail mit Positionen, Lieferart, Adresse, Zahlung, Verlauf. Aktionen: „Versendet“ (optional Sendungsnummer + Versanddienst → Mail G10), „Abholbereit“ (→ G11), „Abgeschlossen“, „Erstatten“ (voll oder Teilbetrag, Grund; Stripe-Refund auf dem verbundenen Konto → G13), „Stornieren“ (nur unbezahlt). Widerrufe erscheinen an der Bestellung und als eigene Liste. CSV-Export (Bestellungen eines Zeitraums).
4.5 Einstellungen
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. Öffnungszeiten), Lieferzeit (Pflicht, z. B. „2–4 Werktage“), Zahlungsarten (Karte inkl. Apple Pay/Google Pay immer; 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).
4.6 Rechtliches (Händler-Rechtstexte) und „Shop geöffnet“
Der Händler ist Verkäufer und verantwortlich. WEBklar liefert Muster, keine Rechtsberatung (W8, RDG):
- Widerrufsbelehrung: vorbefüllt mit dem gesetzlichen Muster (Anlage 1 zu Art. 246a § 1 Abs. 2 EGBGB) inkl. der Hinweise auf die elektronische Widerrufsfunktion, Name/Anschrift/E-Mail eingesetzt; Rücksendekosten trägt der Käufer (Standard, umschaltbar); Muster-Widerrufsformular (Anlage 2). Bearbeitbar; „Auf Muster zurücksetzen“.
- AGB: Muster-AGB für kleine Shops (Vertragsschluss, Preise, Versand, Zahlung über Stripe, Eigentumsvorbehalt, Gewährleistung gesetzlich, Streitbeilegung VSBG-Hinweis), deutlich als Muster gekennzeichnet; bearbeitbar.
- Versand- und Zahlungsinformationen: aus den Einstellungen erzeugt (nicht frei editierbar, damit sie stimmen).
- Impressum-URL und Datenschutz-URL (Pflicht, https).
- Bestätigung: Haken „Ich habe die Texte geprüft. Ich bin als Verkäufer für Rechtstexte, Produktangaben und Preise verantwortlich. WEBklar stellt Muster bereit und berät nicht rechtlich.“ (Zeit, Person, Hash der Texte gespeichert; jede Änderung der Texte hebt die Bestätigung auf und schließt den Shop nicht, verlangt aber neue Bestätigung vor der nächsten Öffnung — Festlegung: geöffnet bleibt geöffnet, Banner „bitte neu bestätigen“; neue Bestellungen werden mit der zuletzt bestätigten Fassung geschlossen, siehe 5.2).
Shop geöffnet (serverseitig bei jeder Bestellung geprüft): Funktion active · geoeffnet · Stripe charges_enabled ·
bestätigte Rechtstexte vorhanden · Artikel lieferbar. Fällt ein Punkt weg, zeigt das Widget „Der Shop ist gerade
geschlossen“ und nimmt keine Bestellungen an.
5. Shop auf der Website (Widget P/public/embed/shop.js)
5.1 Einbau
<div data-webklar-shop="<siteKey>"></div> + <script src="https://project.webklar.com/embed/shop.js" defer></script>
(Muster News/Termine). Eigene Styles mit Präfix/Shadow-DOM nach Vorbild Termine, Akzentfarbe aus den Einstellungen.
Ansichten: Kategorien + Artikelraster, Artikeldetail (Bilder, Beschreibung, Preis, Grundpreis, Varianten, Lieferhinweis,
Widerrufsausschluss, Produktsicherheit, Lebensmittelangaben), Warenkorb (im sessionStorage des Besuchers, nur Artikel-IDs
und Mengen; Begründung: technisch notwendig, § 25 Abs. 2 Nr. 2 TDDDG), Kasse, Danke-Seite, Widerrufsformular. Ganz oben
im Widget (Beginn des Bestellvorgangs, § 312j Abs. 1 BGB): Lieferländer und akzeptierte Zahlungsarten. Fußzeile immer:
AGB · Widerrufsbelehrung · Versand & Zahlung · Vertrag widerrufen · Impressum · Datenschutz.
5.2 Kasse und Vertragsschluss (W9)
Die verbindliche Bestellung geschieht im Widget mit dem Knopf „Zahlungspflichtig bestellen“ (§ 312j Abs. 3 BGB);
Stripe Checkout ist danach nur noch die Zahlung. Unmittelbar über dem Knopf (§ 312j Abs. 2, Art. 246a § 1 EGBGB):
Positionen mit wesentlichen Merkmalen, Einzel- und Gesamtpreis inkl. USt (bzw. Kleinunternehmer-Hinweis), Versandkosten,
Gesamtsumme, Lieferart, Lieferzeit, Zahlungsarten, Hinweis auf Widerrufsrecht bzw. dessen Ausschluss je Artikel, Link
AGB und Widerrufsbelehrung, Anbieter-Identität. Lieferart wählt der Käufer im Widget; Name, E-Mail, Telefon (optional)
und Lieferadresse sammelt Stripe Checkout (shipping_address_collection nur bei Versand, erlaubte Länder aus den
Einstellungen). Korrektur von Eingabefehlern (§ 312i Abs. 1 Nr. 1): Warenkorb und Lieferart sind bis zum Knopf änderbar,
Adresse in Stripe bis zur Zahlung.
Server (POST /api/public/shop/:siteKey/bestellung): prüft „Shop geöffnet“, rechnet alle Preise aus der Datenbank neu
(nie aus dem Browser), prüft und reserviert Bestand (offene Bestellung zählt bis ablaufAt), legt shopOrders mit
Status offen an (Schnappschuss der Positionen und der bestätigten Rechtstexte-Fassung), erstellt die Checkout-Session
auf dem verbundenen Konto (stripeAccount): mode: payment, price_data je Position + Versandkosten als
shipping_options (fester Betrag aus dem Server), payment_method_types aus den Einstellungen, locale: 'de',
expires_at = jetzt + 30 min, client_reference_id = Bestell-ID, metadata {shopId, orderId, bestellnummer},
customer_creation: 'if_required', phone_number_collection. success_url = Seite des Kunden mit
?wk_bestellung=<id>&wk_t=<token> (nur erlaubte Hosts des Projekts, sonst Standalone-Seite), cancel_url = Warenkorb.
Unter einer Sperre je Shop (withLocks), damit zwei gleichzeitige Käufe das letzte Stück nicht doppelt reservieren.
5.3 Zahlung, Bestätigung, Verlauf
- Connect-Webhook
POST /api/shop/stripe-webhook(eigenes Signatur-GeheimnisSTRIPE_CONNECT_WEBHOOK_SECRET, Endpunkt mitconnect: true):checkout.session.completed(beipayment_status: paid),checkout.session.async_payment_succeeded→bezahlt, Bestand endgültig abziehen, Käuferdaten aus der Session speichern, G09 Bestellbestätigung an den Käufer, K25 Neue Bestellung an den Händler.async_payment_failed/expired→storniert, Reservierung frei (G14 nur bei fehlgeschlagener Zahlung).account.updated→stripeStatus.charge.refunded→ Erstattung aus dem Stripe-Dashboard nachtragen. Alles idempotent (Status-Übergänge nur vorwärts, Sperre je Bestellung). - Danke-Seite fragt
GET /api/public/shop/:siteKey/bestellung/:id?t=<token>; ist die Bestellung nochoffen, holt der Server die Session bei Stripe und schließt selbst ab (gleicher Code wie der Webhook) — fällt der Webhook aus, geht trotzdem nichts verloren. - G09 Bestellbestätigung (dauerhafter Datenträger, § 312f Abs. 2 BGB): Anbieter, Bestellnummer, Positionen, Preise, Versand, Gesamt, Lieferart/-zeit, Lieferadresse, Zahlungsart, Hinweis auf Widerrufsausschlüsse, vollständige AGB, vollständige Widerrufsbelehrung und Muster-Widerrufsformular (im Mailtext), Link „Vertrag widerrufen“. Absender: WEBklar-Versandadresse mit Anzeigename „“, Reply-To = E-Mail des Händlers.
5.4 Standalone-Seite und Widerrufsfunktion (W10, § 356a BGB)
https://project.webklar.com/shop/<siteKey>zeigt das Widget auf einer neutralen Seite (Vorschau, Rücksprung-Ersatz).https://project.webklar.com/shop/<siteKey>/widerrufund die Widget-Ansicht „Vertrag widerrufen“: Schaltfläche „Vertrag widerrufen“ führt zu einem Formular (Name, E-Mail, Bestellnummer, optional Erklärung/Artikel), danach Bestätigungsschaltfläche „Widerruf bestätigen“. Eingang wird gespeichert, G12 Eingangsbestätigung mit Inhalt, Datum und Uhrzeit sofort an den Käufer (dauerhafter Datenträger), K26 an den Händler. Erreichbar ohne Login, auch ohne passende Bestellung (der Händler klärt), Missbrauchsschutz per Rate-Limit.
6. Bestellung der Funktion bei WEBklar (Register-Eintrag shop)
Registereintrag nach Muster termine: product: 'shopSetup' (29900 Cent, Lookup-Key webklar_shop_einrichtung_29900),
priceLabel '299 €', setupEstimate 'meist innerhalb von 2–3 Wochen', requiresWartungsAbo: false,
ticketFreischaltung: true, returnHash 'webshop', returnProjekt: true, Haken:
verfuegbar(email): SchalterSHOP_ENABLED/ Pilotliste.bestellpruefung: Anfrage des Projekts istangenommen(sonst offener Punkt „Anfrage noch nicht freigegeben“).unterlagenBereit: Leistungsbeschreibung + AVV Webshop der aktuellen Fassung abrufbar (HTML + PDF, sha256).belehrung: die gemeinsame Widerrufsbelehrung der Funktionen.freischaltbar(doc): wie News (Ticket geschlossen genügt).
Zustimmungen (alle Haken Pflicht, Fassung = config.shop.fassung):
- unterlagen: Leistungsbeschreibung Webshop und AVV Webshop in der Fassung X.
- verantwortung: „Ich verkaufe als Unternehmer in eigenem Namen. Für Produkte, Preise, Produktangaben und meine Rechtstexte bin ich verantwortlich. Ich verkaufe keine ausgeschlossenen Waren (Leistungsbeschreibung Nr. 3).“
- stripe: „Zahlungen laufen über mein eigenes Stripe-Konto zu den Bedingungen von Stripe. WEBklar erhält kein Geld meiner Käufer.“
- beginn: vorzeitiger Beginn, Erlöschen (§ 356 Abs. 5 Nr. 2 BGB), Wertersatz (§ 357a Abs. 2 BGB) – Wortlaut wie Terminplaner.
- information: Pflichtinformationen (Leistung, 299 € einmalig ohne USt nach § 19 UStG, Dauer, Laufzeit „solange die Website bei WEBklar läuft“), Widerrufsbelehrung mit Muster-Formular gelesen.
Ticket: Betreff „Webshop einbauen – “, Details: siteKey, Snippet, Domain, Anfrage (Warenart, Versand/Abholung,
Artikelzahl), Schritte (Shop-Seite anlegen, Snippet einsetzen, Menüpunkt, Fußzeilen-Link „Vertrag widerrufen“ auf allen
Seiten, Datenschutz-Absatz Webshop einfügen, Impressum prüfen, Testkauf im Stripe-Testmodus entfällt → Vorschau prüfen),
Verweis WOMS. Einbauanleitung docs/funktionen/shop-einbau.md.
7. WOMS
Funktionen-Panel des Kunden: Webshop-Zeile mit Anfrage (Inhalt, Zeit) und Knöpfen „Anfrage annehmen“ / „Ablehnen“ (Begründung Pflicht) → Portal-Admin-Route; Zustand, Ticket, Stripe-Status (nur lesen), Kennzahlen. Aktivieren/Abschalten über die vorhandenen generischen Aktionen. „Rechtliches“: Karten Leistungsbeschreibung Webshop und AVV Webshop.
8. Mails
Neue IDs (nächste freie laut Test-Liste; Zählung im Plan geprüft): K24 „Webshop-Anfrage: Entscheidung“ (Varianten angenommen/abgelehnt), K25 „Neue Bestellung im Webshop“ (an Händler), K26 „Widerruf im Webshop eingegangen“ (an Händler), T21 „Neue Webshop-Anfrage“ (Team). Käufer-Mails G09 Bestellbestätigung, G10 versendet, G11 abholbereit, G12 Eingangsbestätigung Widerruf, G13 Erstattung, G14 Zahlung fehlgeschlagen — eigene Vorlagen im Stil der G-Vorlagen des Terminplaners (Absender WEBklar, Anzeigename Shop, Reply-To Händler), über die vorhandene Gast-Mail-Warteschlange, falls wiederverwendbar (Plan), sonst eigene kleine Warteschlange mit Wiederholung. K15/K16/T16 bekommen ihre Texte aus dem Register (vorhanden).
9. Datenschutz und Aufbewahrung
- Rollen: Händler = Verantwortlicher für Käuferdaten; WEBklar = Auftragsverarbeiter (AVV Webshop, Hetzner als Unterauftragsverarbeiter, Mailversand über Hetzner); Stripe = eigener Vertragspartner des Händlers (nicht Unterauftragsverarbeiter von WEBklar). Gespeichert werden nur Bestelldaten, die für Abwicklung und Nachweis nötig sind.
- Aufbewahrung (W11): personenbezogene Käuferdaten (Name, E-Mail, Telefon, Adressen) einer Bestellung werden 3 Jahre
nach Ende des Bestelljahres anonymisiert (Gewährleistung 2 Jahre + Puffer); Beträge, Positionen, Bestellnummer
bleiben für die Buchhaltung des Händlers (dessen Aufbewahrungspflicht deckt der Händler über Stripe-Belege und eigene
Unterlagen; er kann jederzeit CSV exportieren). Unbezahlte/stornierte Bestellungen: Käuferdaten nach 30 Tagen weg.
Widerrufe: wie die Bestellung. Wird der Shop abgeschaltet: 90 Tage Lesen + Export, dann Löschung aller Shopdaten
(Bilder eingeschlossen). Job im bestehenden Löschlauf (
retention.js). - Portal-Datenschutzerklärung: neuer Abschnitt „Webshop“ (Stripe Connect: Weitergabe von E-Mail/Website des Kunden an Stripe zur Kontoeröffnung, Abruf des Kontostatus; Verarbeitung als Auftragsverarbeiter).
- Muster-Datenschutzabsatz für die Website des Händlers (
docs/funktionen/shop-datenschutz-absatz.md): Bestellabwicklung, WEBklar als Auftragsverarbeiter, Stripe (inkl. PayPal/Klarna über Stripe), Warenkorb im Browser-Speicher.
10. Rechtstexte von WEBklar (neu, Fassung 2026-10-05)
- Leistungsbeschreibung Webshop (
public/rechtliches/leistungsbeschreibung-webshop-2026-10-05.html+ PDF): Leistung (Anfrage, Einrichtung, Einbau, Betrieb des Widgets, Verwaltung im Portal), ausgeschlossene Waren, Voraussetzungen (eigenes Stripe-Konto, Unternehmer, eigene Rechtstexte), keine Rechtsberatung/Muster ohne Gewähr, Verfügbarkeit (keine Zusage einer bestimmten Verfügbarkeit über das Hosting hinaus), Haftung (Begrenzung wie bestehende LB; keine Haftung für Inhalte, Preise und Rechtstexte des Händlers), Preis 299 € einmalig, Laufzeit/Ende (mit dem Website-Betrieb; Abschalten, 90 Tage Export, Löschung), Änderungen. - AVV Webshop (Geschwister AVV Terminplaner): Gegenstand, Art der Daten (Käufer: Name, E-Mail, Telefon, Liefer- und Rechnungsadresse, Bestellung, Widerrufe), Betroffene (Käufer), Unterauftragsverarbeiter Hetzner, Löschfristen genau wie gebaut, TOM-Anlage, Weisungen über das Portal.
- Gemeinsame Widerrufsbelehrung Funktionen bleibt (bestehende Datei der aktuellen Fassung).
- Kanzlei-Fragen
docs/rechtliches/webshop-kanzlei-paket.md(Muster-AGB, Muster-Widerrufsbelehrung inkl. 356a, Zustimmungstexte, LB-Haftung, Rolle Plattform/ZAG) – voron, nicht vorpilot.
11. Livegang (je Schritt mit Freigabe des Inhabers; erst nach TP2-Livegang)
- Stripe-Dashboard WEBklar: Connect aktivieren (Plattformprofil: „Plattform, Verkäufer verkaufen eigene Waren, Direct Charges, keine Gebühr“), Branding (Name/Icon/Farbe für das Onboarding).
node scripts/shop-setup.mjs(Collections) undscripts/stripe-products.mjs --anlegen(shopSetup).- Connect-Webhook anlegen (
scripts/shop-setup.mjs --webhookoder Dashboard, Ereignisse aus 5.3), Geheimnis alsSTRIPE_CONNECT_WEBHOOK_SECRETin/home/webklar/apps/.env, Compose-ZeilenSHOP_ENABLED,SHOP_PILOT_EMAILS. - Push
feature/webshop→main(nach TP2), WOMS-Zweig →test. - Pilot mit Testkonto: Anfrage, Freigabe, Bestellung (Test-Coupon), Einbau auf
test-website, Stripe-Konto im Testmodus? (Live-Konto des Pilots; Testkauf 1 € mit Erstattung), Widerruf testen. - Kanzlei-Paket → Befund einarbeiten →
SHOP_ENABLED=on.
12. Entscheidungen (Claude, 2026-10-05, ohne Rückfrage auf Wunsch des Inhabers)
| Nr. | Entscheidung | Begründung |
|---|---|---|
| W1 | Nur Stripe; PayPal/Klarna/Wallets über Stripe Checkout | eine Integration, gleiche Zahlungsarten |
| W2 | Stripe Connect, eigenes Konto des Händlers, Direct Charges, keine Plattformgebühr | kein Fremdgeld bei WEBklar (ZAG), Händler hat volles Dashboard |
| W3 | Schalter SHOP_ENABLED off/pilot/on |
wie Terminplaner |
| W4 | „Auf Anfrage“ = Anfrage → Team-Freigabe → Bestellung | Prüfung erlaubter Waren, Wunsch des Inhabers |
| W5 | 299 € einmalig, kein Abo nötig, keine laufende Gebühr | „einmalige Kostensache“ |
| W6 | Verwaltung ab setup nutzbar |
Shop ist zum Einbau fertig befüllt |
| W7 | Bestellnummer zufällig JJ-XXXXXX |
keine Zählung/Sperre nötig |
| W8 | Händler-Rechtstexte als Muster + Pflichtbestätigung, keine Rechtsberatung | RDG, Verantwortung beim Verkäufer |
| 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 |
| 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) |