Webshop: Design und Umsetzungsplan
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
af63b69ec1
commit
b168b10485
2 files changed
+686
No files matched your search
@@ -0,0 +1,346 @@
|
||||
# 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 PayPal, Klarna, Apple Pay
|
||||
und Google Pay als Zahlungsarten **innerhalb** desselben Checkouts an, wenn der Händler sie in seinem Stripe-Konto
|
||||
einschaltet. Eine eigene PayPal-Anbindung hätte eine zweite Integration, einen zweiten Webhook und eine zweite
|
||||
Erstattungslogik bedeutet, ohne dem Käufer eine Zahlungsart mehr zu geben.
|
||||
|
||||
**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` | Mail |
|
||||
|---|---|---|---|
|
||||
| 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 des `portalFeatures`-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;
|
||||
PayPal, Klarna wählbar – nur einschalten, wenn im eigenen Stripe-Konto aktiviert; 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 `localStorage` 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-Geheimnis `STRIPE_CONNECT_WEBHOOK_SECRET`,
|
||||
Endpunkt mit `connect: true`): `checkout.session.completed` (bei `payment_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 noch `offen`, 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 „<Shopname>“, 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>/widerruf` und 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)`: Schalter `SHOP_ENABLED` / Pilotliste.
|
||||
- `bestellpruefung`: Anfrage des Projekts ist `angenommen` (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`):
|
||||
1. **unterlagen**: Leistungsbeschreibung Webshop und AVV Webshop in der Fassung X.
|
||||
2. **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).“
|
||||
3. **stripe**: „Zahlungen laufen über mein eigenes Stripe-Konto zu den Bedingungen von Stripe. WEBklar erhält kein Geld
|
||||
meiner Käufer.“
|
||||
4. **beginn**: vorzeitiger Beginn, Erlöschen (§ 356 Abs. 5 Nr. 2 BGB), Wertersatz (§ 357a Abs. 2 BGB) – Wortlaut wie
|
||||
Terminplaner.
|
||||
5. **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 – <Projekt>“, 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`)
|
||||
|
||||
1. **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.
|
||||
2. **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.
|
||||
3. Gemeinsame **Widerrufsbelehrung Funktionen** bleibt (bestehende Datei der aktuellen Fassung).
|
||||
4. **Kanzlei-Fragen** `docs/rechtliches/webshop-kanzlei-paket.md` (Muster-AGB, Muster-Widerrufsbelehrung inkl. 356a,
|
||||
Zustimmungstexte, LB-Haftung, Rolle Plattform/ZAG) – vor `on`, nicht vor `pilot`.
|
||||
|
||||
---
|
||||
|
||||
## 11. Livegang (je Schritt mit Freigabe des Inhabers; erst nach TP2-Livegang)
|
||||
|
||||
1. 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).
|
||||
2. `node scripts/shop-setup.mjs` (Collections) und `scripts/stripe-products.mjs --anlegen` (shopSetup).
|
||||
3. Connect-Webhook anlegen (`scripts/shop-setup.mjs --webhook` oder Dashboard, Ereignisse aus 5.3), Geheimnis als
|
||||
`STRIPE_CONNECT_WEBHOOK_SECRET` in `/home/webklar/apps/.env`, Compose-Zeilen `SHOP_ENABLED`, `SHOP_PILOT_EMAILS`.
|
||||
4. Push `feature/webshop` → `main` (nach TP2), WOMS-Zweig → `test`.
|
||||
5. 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.
|
||||
6. 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 |
|
||||
| W12 | Versand nur DE/AT, keine digitalen Produkte, keine Gutscheine | Umfang V1, Recht (digitale Inhalte, Mehrzweckgutscheine) |
|
||||
Reference in new issue
Block a user