Merge remote-tracking branch 'origin/main' into feature/oeffnungszeiten
Zusammenführung von TP2-Abschluss und Webshop (origin/main cab9c33) mit Öffnungszeiten live. Beide
Funktionen bleiben vollständig: jeder Konflikt ist als Vereinigung gelöst, Reihenfolge der Funktionsschlüssel
überall news, termine, shop, zeiten.
Konfliktdateien (28): public/app.js, dashboard.html, funktionen.js, widerrufen.html; scripts/stripe-products.mjs;
server/config.js, index.js, middleware/kundenansichtGuard.js, routes/abo.js, routes/admin/funktionenAdmin.js,
routes/funktionen.js, services/customerAdmin.js, customerViewLog.js, featureRegistry.js, retention.js,
siteFeatures.js, stripeBilling.js; test/customerViewLog, featureFreischaltung, featureRegistry, funktionenRoutes,
funktionenTermine, kundenansichtGuard, mailTemplates, publicForms, siteFeatures, stripeProductsScript (je
.test.js) und test/support/schema.js.
Entscheidungen: Mailvorlagen G01–G15, K01–K30, T01–T21 lückenlos (K24–K26 Webshop, K27–K30 Öffnungszeiten);
Stripe-Katalog mit shopSetup und zeitenSetup; Widerruf kennt shop und zeiten; ABO_FUNKTIONEN bleibt analyse
und termine (kein zeiten); activate() schreibt die Zeiten-Felder nur über extra; kein Halter von
feature-activate:<id> ruft disable() auf; die Aufbewahrungs-Löschung löscht Zeiten-Daten vor dem Abschalten und
hält den Kunden bei einem Fehler (held); die Admin-Antwort trägt ersatzTicket und warnung; Hash-Routen webshop
und zeiten, Ansichten view-webshop und view-zeiten; assertOwnProject behält die Meldung von main.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
e87140e5ba
245 files changed
+38799
-1050
No files matched your search
Binary file not shown.
@@ -0,0 +1 @@
|
||||
d73389a0cf78380029948d91db022e20d7d2f7182ea1c4fa78478d78c719c938
|
||||
@@ -0,0 +1,55 @@
|
||||
# Mustertext: Datenschutzerklärung der Kundenwebsite (Webshop)
|
||||
|
||||
**Muster – keine Rechtsberatung.** Der Absatz gehört in die Datenschutzerklärung jeder Kundenwebsite, auf der der Webshop läuft. Der Händler ist Verantwortlicher und verantwortet seine Datenschutzerklärung selbst; WEBklar liefert nur diesen Baustein (Leistungsbeschreibung Webshop Nr. 5 Abs. 10 und Nr. 6). Bau den Shop erst ein, wenn der Absatz in der Datenschutzerklärung steht.
|
||||
|
||||
Hinweise für das Team:
|
||||
|
||||
- Liegt die Datenschutzerklärung in der Website, die WEBklar betreut, fügen wir den Absatz beim Einbau ein (gehört zur Einrichtung). Liegt sie woanders, bekommt der Händler den Text im Chat und fügt ihn selbst ein.
|
||||
- Platzhalter in eckigen Klammern durch die Angaben des Händlers ersetzen. Nicht Zutreffendes streichen: Klarna nur, wenn der Händler Klarna in den Zahlungsarten eingeschaltet hat; den Satz zum Versanddienstleister nur, wenn er versendet; die Zeile zur Abholung nur bei Abholung.
|
||||
- Anrede und Ton an die übrige Erklärung der Website anpassen (meist Sie). Die Nummerierung der Überschrift der Website beachten.
|
||||
- Wenn der Händler im Impressum oder in der Erklärung einen Datenschutzbeauftragten nennt, dort dazuschreiben.
|
||||
- Die Löschfristen entsprechen dem AVV Webshop Nr. 9 und Abschnitt 6d der Datenschutzerklärung von WEBklar (project.webklar.com/datenschutz.html). Ändert sich dort etwas, hier mitändern. Die Frist für Server-Protokolle (14 Tage) steht in Abschnitt 3 dieser Erklärung.
|
||||
- Das Widget setzt keine Cookies. Der Warenkorb liegt in der `sessionStorage` des Tabs, das ist Absatz „Warenkorb im Browser“.
|
||||
- Im sichtbaren Text der Kundenwebsite die Wünsche des Händlers beachten (etwa keine Gedankenstriche).
|
||||
|
||||
---
|
||||
|
||||
## [Nummer]. Bestellungen in unserem Webshop
|
||||
|
||||
### Verantwortlicher und technischer Betrieb
|
||||
|
||||
Verantwortlich für die Verarbeitung Ihrer Daten bei einer Bestellung ist [Name des Händlers, Anschrift, E-Mail-Adresse], also der Verkäufer. Den Webshop betreibt technisch die WEBklar GbR, Am Schwimmbad 10, 67722 Winnweiler, in unserem Auftrag. Sie verarbeitet Ihre Daten nur nach unseren Weisungen und nicht für eigene Zwecke (Auftragsverarbeitung nach Art. 28 DSGVO). Der Server steht bei der Hetzner Online GmbH in Falkenstein (Deutschland); der Versand der E-Mails zu Ihrer Bestellung läuft über den E-Mail-Dienst der Hetzner Online GmbH in Nürnberg (Deutschland).
|
||||
|
||||
### Welche Daten wir bei einer Bestellung verarbeiten
|
||||
|
||||
Wenn Sie in unserem Webshop bestellen, verarbeiten wir die Artikel in Ihrem Warenkorb und Ihre Bestellung (Artikel, Preise, Versandart, Gesamtbetrag, Bestellnummer, Zeitpunkt), Ihren Namen, Ihre E-Mail-Adresse sowie Ihre Liefer- und Rechnungsanschrift, den Stand der Zahlung und den Stand Ihrer Bestellung (zum Beispiel bezahlt, versendet, abholbereit, erstattet). Name und Anschriften geben Sie auf der Zahlungsseite von Stripe ein (siehe unten); wir erhalten sie von dort, die Rechnungsanschrift nur, wenn Stripe sie für Ihre Zahlungsart abfragt. Eine Telefonnummer verlangen und speichern wir nicht. Die Verarbeitung ist nötig, um den Kaufvertrag mit Ihnen zu schließen und zu erfüllen (Art. 6 Abs. 1 lit. b DSGVO). Ohne diese Angaben können wir Ihre Bestellung nicht ausführen. Außerdem speichern wir in der noch offenen Bestellung einen Hashwert Ihrer IP-Adresse (nicht die IP-Adresse selbst; er wird mit einem geheimen Schlüssel des Servers berechnet, sodass sich daraus keine IP-Adresse ableiten lässt). Wir lassen damit je IP-Adresse höchstens drei offene, noch nicht bezahlte Bestellungen innerhalb von 30 Minuten zu und schützen den Webshop so vor automatisierten Massenbestellungen, die den Bestand blockieren. Das ist unser berechtigtes Interesse (Art. 6 Abs. 1 lit. f DSGVO); Sie können dieser Verarbeitung widersprechen (Art. 21 DSGVO). Wir verwenden den Hashwert nur dafür und löschen ihn, sobald Ihre Bestellung bezahlt oder storniert ist, spätestens mit der Löschung nicht bezahlter Bestellungen nach 30 Tagen.
|
||||
|
||||
### Zahlung über Stripe
|
||||
|
||||
Die Zahlung wickelt die Stripe Payments Europe, Limited, 1 Grand Canal Street Lower, Grand Canal Dock, Dublin, D02 H210, Irland („Stripe“) ab. Sie werden dazu auf die Zahlungsseite von Stripe weitergeleitet. Karten- und Kontodaten geben Sie nur dort ein; wir und der Betreiber des Webshops erhalten sie nicht. Stripe verarbeitet Ihre Daten teils im Rahmen der Zahlungsabwicklung für uns und teils als eigener Verantwortlicher, zum Beispiel zur Betrugsvorbeugung und zur Erfüllung gesetzlicher Pflichten; dafür gilt die Datenschutzerklärung von Stripe (stripe.com/de/privacy). Dabei können Daten an die Stripe, LLC in den USA übermittelt werden. Die Stripe, LLC ist nach dem EU-US Data Privacy Framework zertifiziert, für das ein Angemessenheitsbeschluss der EU-Kommission besteht. Rechtsgrundlage ist Art. 6 Abs. 1 lit. b DSGVO.
|
||||
|
||||
Welche Zahlungsarten angeboten werden, sehen Sie vor dem Kauf im Webshop. Zahlen Sie per SEPA-Lastschrift, gibt Stripe Ihre Kontodaten an die beteiligten Banken weiter und bucht den Betrag nach Ihrem Mandat ab. [Nur wenn Klarna eingeschaltet ist: Wählen Sie Klarna, übermittelt Stripe die zur Zahlung erforderlichen Daten an die Klarna Bank AB (publ), Sveavägen 46, 111 34 Stockholm, Schweden; es gelten die Datenschutzbestimmungen von Klarna, die Ihnen bei der Zahlung angezeigt werden.]
|
||||
|
||||
### Bestellbestätigung und weitere E-Mails
|
||||
|
||||
Zu Ihrer Bestellung senden wir Ihnen E-Mails an die angegebene E-Mail-Adresse: bei einer Zahlung, die noch aussteht (zum Beispiel SEPA-Lastschrift), zuerst eine Mail über den Eingang der Bestellung, Bestellbestätigung (mit den Vertragsbedingungen, der Widerrufsbelehrung und dem Muster-Widerrufsformular; es sind die Texte, die bei Ihrer Bestellung galten), Hinweise zum Versand [oder zur Abholbereitschaft], Bestätigungen zu einer Erstattung oder zu einem Widerruf und, wenn eine Zahlung nicht gelungen ist, einen Hinweis darauf. Das sind Mitteilungen zur Abwicklung Ihres Vertrags (Art. 6 Abs. 1 lit. b DSGVO), keine Werbung. Bis zum Versand liegt jede E-Mail in einer Warteschlange; nach dem Versand wird Empfängeradresse und Inhalt dort gelöscht, der Eintrag selbst nach sieben Tagen.
|
||||
|
||||
### Warenkorb im Browser
|
||||
|
||||
Ihren Warenkorb (Artikel und Mengen, keine persönlichen Daten) speichert der Webshop im Sitzungsspeicher (sessionStorage) Ihres Browsers, solange der Tab geöffnet ist. Das ist technisch erforderlich, damit der Warenkorb beim Durchklicken erhalten bleibt (§ 25 Abs. 2 Nr. 2 TDDDG); es werden keine Cookies gesetzt, und der Eintrag verschwindet, wenn Sie den Tab schließen. Beim Aufruf der Seite lädt Ihr Browser das Programm des Webshops und die Artikeldaten von der Adresse project.webklar.com. Dabei werden Ihre IP-Adresse, Datum und Uhrzeit des Aufrufs sowie Angaben zu Ihrem Browser und Betriebssystem übertragen und für 14 Tage in Server-Protokollen gespeichert, um den sicheren und störungsfreien Betrieb zu gewährleisten (Art. 6 Abs. 1 lit. f DSGVO). Zum Schutz vor Missbrauch begrenzen wir die Zahl der Bestellungen und Widerrufe je IP-Adresse; die Zeitpunkte der Aufrufe werden nur im Arbeitsspeicher des Servers gehalten. Mit Ihrer Bestellung oder Ihrem Widerruf wird Ihre IP-Adresse nicht gespeichert.
|
||||
|
||||
### Widerruf über die Widerrufsfunktion
|
||||
|
||||
Über den Link „Vertrag widerrufen“ können Sie Ihren Vertrag ohne Anmeldung online widerrufen. Dabei verarbeiten wir Ihren Namen, Ihre E-Mail-Adresse, die Bestellnummer, Ihre Erklärung und den Zeitpunkt des Eingangs. Sie erhalten sofort eine Eingangsbestätigung mit Inhalt, Datum und Uhrzeit Ihrer Erklärung. Rechtsgrundlage ist Art. 6 Abs. 1 lit. c DSGVO in Verbindung mit § 356a BGB sowie Art. 6 Abs. 1 lit. b DSGVO.
|
||||
|
||||
### Weitergabe
|
||||
|
||||
[Nur bei Versand: Zur Lieferung geben wir Ihren Namen und Ihre Lieferanschrift an das Versandunternehmen weiter, das wir beauftragen ([Name des Versanddienstleisters]).] Im Übrigen geben wir Ihre Daten nur weiter, soweit das für die Vertragsabwicklung nötig ist (Stripe und die genannten Zahlungsanbieter, Auftragsverarbeiter) oder wir gesetzlich dazu verpflichtet sind (zum Beispiel an das Finanzamt). Eine Weitergabe zu Werbezwecken findet nicht statt.
|
||||
|
||||
### Speicherdauer
|
||||
|
||||
Bestellungen, die nicht bezahlt oder die storniert wurden, löscht das Webshop-System nach 30 Tagen. Für bezahlte Bestellungen sind wir als Händler verpflichtet, Buchungsbelege und Geschäftsunterlagen nach Handels- und Steuerrecht aufzubewahren (in der Regel sechs bis zehn Jahre, § 257 HGB, § 147 AO; Art. 6 Abs. 1 lit. c DSGVO). Diese Unterlagen führen wir selbst und mit den Belegen von Stripe, nicht im Webshop-System. Im Webshop-System selbst werden Name, E-Mail-Adresse und Anschriften drei Jahre nach dem Ende des Jahres der Bestellung anonymisiert; die Bestellnummer, die Artikel und die Beträge bleiben ohne Personenbezug erhalten. Daten aus einem Widerruf werden ebenso behandelt wie die zugehörige Bestellung.
|
||||
|
||||
### Ihre Rechte
|
||||
|
||||
Sie haben das Recht auf Auskunft über Ihre Daten (Art. 15 DSGVO), auf Berichtigung (Art. 16), Löschung (Art. 17), Einschränkung der Verarbeitung (Art. 18), Datenübertragbarkeit (Art. 20) und auf Widerspruch gegen Verarbeitungen, die auf Art. 6 Abs. 1 lit. f DSGVO beruhen (Art. 21). Wenden Sie sich dazu an uns unter [E-Mail-Adresse des Händlers]; wir leiten Ihre Anfrage, soweit nötig, an den technischen Betreiber des Webshops weiter. Außerdem können Sie sich bei einer Datenschutz-Aufsichtsbehörde beschweren, etwa bei der für uns zuständigen Behörde [zuständige Aufsichtsbehörde des Händlers].
|
||||
@@ -0,0 +1,98 @@
|
||||
# Webshop auf der Kundenwebsite einbauen
|
||||
|
||||
Anleitung für das WEBklar-Team. Das Widget lässt Besucher auf der Website des Kunden („Händler“) Waren bestellen und bezahlen. Artikel, Versand, Zahlungsarten und Rechtstexte pflegt der Händler im Kundenportal (Tab „Funktionen“, Kachel „Webshop“). Den `siteKey` (16 Zeichen, a–z und 2–7) findest Du im Einrichtungs-Ticket und im Ticketsystem (WOMS): Kunde → Reiter „Funktionen“, Zeile Webshop. Im Ticket stehen Snippet und Schritte schon fertig; dieses Dokument ist die Referenz dahinter. Ohne aktive Funktion, ohne verbundenes Stripe-Konto, ohne bestätigte Rechtstexte oder ohne lieferbaren Artikel ist nichts bestellbar: Das Widget zeigt dann „Der Shop ist gerade geschlossen“ (oder den Ausweichtext, siehe Abschnitt 4).
|
||||
|
||||
Der Webshop ist an den Vertrag über den Betrieb der Website gekoppelt (Leistungsbeschreibung Webshop Nr. 10), aber **nicht in der Software**: Das Portal liest keine Abo-Daten für den Shop, schaltet ihn nicht von selbst ab, und bestellt werden kann er auch ohne Abo. Das Abschalten zum Vertragsende machen wir von Hand (Abschnitt 8).
|
||||
|
||||
## 1. Das Snippet
|
||||
|
||||
```html
|
||||
<div data-webklar-shop="SITEKEY" data-api="https://project.webklar.com">
|
||||
<p>Unser Shop ist gerade nicht erreichbar. Bitte schreiben Sie uns: info@beispiel.de</p> <!-- Ausweichtext, Abschnitt 4 -->
|
||||
</div>
|
||||
<script src="https://project.webklar.com/embed/shop.js" defer></script>
|
||||
```
|
||||
|
||||
Das Skript steht einmal pro Seite, vor `</body>`. Es hat keine Abhängigkeiten, setzt keine Cookies, lädt nichts von Dritten und fragt nur `project.webklar.com` an. Einziger Speicher im Browser ist der Warenkorb (nur Artikelkennungen und Mengen) in der `sessionStorage` des Tabs; er ist technisch erforderlich (§ 25 Abs. 2 Nr. 2 TDDDG) und verschwindet mit dem Tab. Texte des Händlers setzt das Skript nur als Text ein, nie als HTML. Das Widget rechnet nie selbst: Preise, Summen, Versand und Steuerhinweise kommen fertig vom Server.
|
||||
|
||||
| Attribut | Pflicht | Bedeutung |
|
||||
| --- | --- | --- |
|
||||
| `data-webklar-shop` | ja | der `siteKey` des Projekts |
|
||||
| `data-api` | nein | Herkunft des Portals, Standard `https://project.webklar.com`. Bei Modul-Import, Tag-Manager oder nachgeladenem Skript ist `data-api` Pflicht. |
|
||||
| `data-ansicht` | nein | `widerruf` öffnet gleich die Widerrufsfunktion („Vertrag widerrufen“). Die Adresse der Seite kennt außerdem `?ansicht=widerruf` und `?ansicht=agb`. |
|
||||
|
||||
## 2. Wohin damit
|
||||
|
||||
1. **Eigene Seite „Shop“** anlegen (zum Beispiel `/shop` oder `/shop.html`) und das Snippet an die Stelle des Inhalts setzen. Das `div` braucht Breite (normaler Blockcontainer; in einem Flex- oder Grid-Element ohne Breite schrumpft es auf null). Der Warenkorb liegt nur im Tab, also darf die Seite beim Bestellen nicht in einem iframe liegen und der Händler braucht keine zweite Seite für die Kasse: Kasse, Danke-Seite und Widerruf laufen im selben Widget.
|
||||
2. **Menüpunkt „Shop“** im Hauptmenü auf diese Seite, auf allen Seiten.
|
||||
3. Vom Shop zurück: Nach der Zahlung bei Stripe kehrt der Käufer auf die Seite zurück, auf der er bestellt hat (nur Adressen der Website des Projekts sind erlaubt, sonst die Standalone-Seite). Die Seite darf also nicht hinter einer Weiterleitung oder einem Passwortschutz liegen.
|
||||
4. **Mehrere Widgets** auf einer Seite sind nicht vorgesehen. Der Shop ist ein Widget je Website.
|
||||
|
||||
Aussehen: Das Widget nimmt die Akzentfarbe aus den Einstellungen des Händlers. Wie beim Terminplaner stehen die Stile in `@layer wks`, jede Regel der Kundenseite gewinnt also. Anpassen per CSS-Variablen auf dem `div`, in der Seiten-CSS oder auf `:root`, zum Beispiel `[data-webklar-shop]{ --wks-akzent:#0f766e; --wks-akzent-text:#fff; }`:
|
||||
|
||||
| Variable | Standard |
|
||||
| --- | --- |
|
||||
| `--wks-akzent` und `--wks-akzent-text` (Knöpfe; setze beide zusammen) | Akzentfarbe des Händlers, sonst `#1d4ed8` und `#fff` |
|
||||
| `--wks-hintergrund` | `#fff` |
|
||||
| `--wks-text` | `#1f2937` |
|
||||
| `--wks-rand` | `#858f9e` |
|
||||
| `--wks-radius` | `10px` |
|
||||
| `--wks-schrift`, `--wks-schrift-titel` | Schrift der Seite |
|
||||
|
||||
Kontrast (WCAG 2.1 AA): Text auf dem Hintergrund und Schrift auf dem Akzent mindestens 4,5 : 1, Rand gegen den Hintergrund mindestens 3 : 1. Prüfe beim Abnehmen, ob globale Regeln des Themes (`button{…}`, `input{…}`) das Widget stören, und überschreibe sie gezielt (`[data-webklar-shop] .wks-btn{ … }`).
|
||||
|
||||
## 3. Link „Vertrag widerrufen“ auf jeder Seite (Pflicht)
|
||||
|
||||
Seit dem 19.06.2026 muss jeder Online-Shop eine elektronische Widerrufsfunktion bieten (§ 356a BGB). Sie muss **auf jeder Seite** der Website dauerhaft erreichbar sein, nicht nur im Shop:
|
||||
|
||||
- In der **Fußzeile jeder Seite** (auch Impressum, Datenschutz und 404-Seite) steht der Link **„Vertrag widerrufen“** auf `https://project.webklar.com/shop/<siteKey>/widerruf` (mit dem echten `siteKey`).
|
||||
- Der Linktext lautet genau „Vertrag widerrufen“. Er darf nicht ausgeblendet, versteckt oder in einem Aufklappmenü untergebracht werden.
|
||||
- Der Link braucht keine Anmeldung. Er führt zur Schaltfläche „Vertrag widerrufen“, danach zum Formular und zur Bestätigungsschaltfläche „Widerruf bestätigen“.
|
||||
- Im Shop-Widget selbst steht der Knopf „Vertrag widerrufen“ zusätzlich in der Fußzeile des Widgets.
|
||||
|
||||
Der Händler darf den Link nicht entfernen oder ändern (Leistungsbeschreibung Nr. 5 Abs. 6). Weise ihn bei der Übergabe darauf hin.
|
||||
|
||||
## 4. Ausweichtext
|
||||
|
||||
Alles, was im `div` steht, gehört der Kundenwebsite. Das Skript ersetzt es nur, wenn die Konfiguration geladen werden konnte. Bei ausgeschaltetem JavaScript, unbekanntem Schlüssel, abgeschalteter Funktion oder Netzfehler bleibt der Text stehen. Schreib dort einen Hinweis und die Kontaktdaten des Händlers hinein. Ist der Shop zwar eingebunden, aber (noch) nicht geöffnet, zeigt das Widget selbst „Der Shop ist gerade geschlossen“.
|
||||
|
||||
## 5. Vor der Abnahme prüfen
|
||||
|
||||
**Impressum des Händlers.** Der Händler ist der Verkäufer. Sein Impressum muss ihn nennen (Name beziehungsweise Firma, vollständige Anschrift, E-Mail, Telefon, gegebenenfalls Register und USt-IdNr.). Es reicht nicht, wenn nur WEBklar als Betreiber der Website erscheint. Die Adressen von Impressum und Datenschutzerklärung (https) hat der Händler im Portal unter Rechtliches eingetragen; das Widget verlinkt sie in der Fußzeile und in der Kasse. Prüfe, dass beide Adressen auf die echten Seiten der Website führen. Fällt Dir ein offensichtlicher Mangel auf, sag es dem Händler im Chat (Leistungsbeschreibung Nr. 5 Abs. 10: Eine Prüfpflicht entsteht daraus nicht).
|
||||
|
||||
**Datenschutz-Absatz.** Liegt die Datenschutzerklärung in der Website, die wir betreuen, fügen wir den Absatz aus `docs/funktionen/shop-datenschutz-absatz.md` dort ein (gehört zur Einrichtung). Liegt sie woanders, bekommt der Händler den Text per Chat und fügt ihn selbst ein. Anrede und Nummerierung an die übrige Erklärung anpassen; die Angaben zur Speicherdauer entsprechen dem AVV Webshop Nr. 9 und Abschnitt 6d unserer Datenschutzerklärung. Ändert sich dort etwas, ändere den Absatz mit.
|
||||
|
||||
**Rechtstexte.** AGB, Widerrufsbelehrung, Muster-Widerrufsformular sowie Versand und Zahlung bestätigt der Händler im Portal (Rechtliches). Die Muster liegen in `docs/funktionen/shop-muster-rechtstexte.md`. Ohne Bestätigung öffnet der Shop nicht.
|
||||
|
||||
**Artikel.** Wirf einen Blick auf die Pflichtangaben: Preis, Grundpreis (immer je 1 kg, 1 l, 1 m oder 1 m²), Lieferzeit, bei Lebensmitteln die Pflichtangaben und Allergene, die Angaben zur Produktsicherheit und Widerrufsausschlüsse. Das ist Sache des Händlers; ein Hinweis im Chat hilft.
|
||||
|
||||
## 6. Vorschau und Testkauf
|
||||
|
||||
1. **Vorschau:** `https://project.webklar.com/shop/<siteKey>` zeigt das Widget auf einer neutralen Seite. Prüfe dort Artikel, Warenkorb, Versand und die Fußzeile (AGB, Widerrufsbelehrung, Versand & Zahlung, Vertrag widerrufen, Impressum, Datenschutz). Prüfe danach dasselbe auf der echten Seite der Kundenwebsite, auch auf dem Handy.
|
||||
2. **Testkauf mit dem Händler.** Lege mit dem Händler einen Artikel für **1 €** an (nur für den Test, danach wieder löschen oder deaktivieren). Der Händler öffnet den Shop (Übersicht → „Shop geöffnet“, alle Haken grün), Du kaufst den Artikel auf der echten Seite mit Deiner E-Mail-Adresse und zahlst echt (das Stripe-Konto des Händlers ist ein Live-Konto). Prüfe: Bestätigungsseite „Danke“, Bestellbestätigungsmail beim Käufer (mit AGB, Widerrufsbelehrung und Muster-Widerrufsformular im Text), Mail „Neue Bestellung“ beim Händler, Bestellung im Portal als bezahlt.
|
||||
3. **Widerruf testen:** Rufe den Link „Vertrag widerrufen“ aus der Fußzeile auf, erkläre den Widerruf zur Testbestellung und bestätige ihn. Du musst sofort eine Eingangsbestätigung mit Inhalt, Datum und Uhrzeit bekommen; der Händler bekommt die Mitteilung, der Widerruf steht an der Bestellung im Portal.
|
||||
4. **Erstatten:** Der Händler erstattet den Testkauf im Portal (Bestellung → Erstatten). Prüfe die Erstattungsmail und dass Stripe die Rückzahlung zeigt. Den Testartikel danach löschen.
|
||||
5. Ist alles in Ordnung, **schließe das Einrichtungs-Ticket**. Das schaltet die Funktion frei (Kunde erhält die Mail „Webshop bereit“). Trag die Zeit im Arbeitsblatt ein; sie wird nicht auf Abo-Stunden angerechnet (Leistungsbeschreibung Nr. 9 Abs. 4).
|
||||
|
||||
## 7. Was das Widget zeigt (Kurzüberblick)
|
||||
|
||||
1. Ganz oben: Lieferländer und akzeptierte Zahlungsarten (Beginn des Bestellvorgangs, § 312j Abs. 1 BGB).
|
||||
2. Artikel (Kategorien, Raster), Detail mit Bildern, Beschreibung, Preis, Grundpreis, Varianten, Lieferhinweis, Widerrufsausschluss, Produktsicherheit und Lebensmittelangaben.
|
||||
3. Warenkorb, Lieferart (Versand oder Abholung).
|
||||
4. Kasse: Positionen mit Einzel- und Gesamtpreis, Versandkosten, Gesamtsumme, Lieferzeit, Zahlungsarten, Hinweis auf das Widerrufsrecht beziehungsweise seinen Ausschluss, Links zu AGB und Widerrufsbelehrung, **Hinweis auf die gesetzliche Gewährleistung** (Anhang I der Verordnung (EU) 2025/1960, hervorgehoben über dem Knopf) und der Knopf **„Zahlungspflichtig bestellen“**. Erst mit diesem Klick im Widget kommt der Vertrag zustande; Stripe Checkout ist danach nur noch die Zahlung (Name, Telefon und Lieferadresse sammelt Stripe).
|
||||
5. Danke-Seite mit dem Stand der Bestellung; die Bestellbestätigungsmail geht an den Käufer.
|
||||
6. Widerruf: Formular (Name, E-Mail, Bestellnummer, optional Erklärung) und „Widerruf bestätigen“.
|
||||
|
||||
## 8. Wenn der Vertrag über die Website endet (Abschalten von Hand)
|
||||
|
||||
Der Webshop läuft, solange WEBklar die Website des Händlers betreibt (Hosting-Abo, Wartungs-Abo oder ein anderer Vertrag über den Betrieb der Website; Leistungsbeschreibung Nr. 10). Die Software prüft das **nicht**. Du musst es selbst im Blick haben, wenn ein solcher Vertrag endet, gekündigt oder widerrufen wird:
|
||||
|
||||
1. **Vorher** schreibst Du dem Händler eine E-Mail (oder Chat): Der Vertrag endet am TT.MM.JJJJ, damit endet auch der Webshop, WEBklar schaltet ihn zum Vertragsende ab. Er soll offene Bestellungen und Widerrufe abarbeiten, seine Bestellungen als CSV exportieren (Aufbewahrungspflichten sind seine Sache) und seine Käufer informieren, dass der Shop nicht mehr angeboten wird.
|
||||
2. **Zum Vertragsende** im Ticketsystem (WOMS): Kunde → Reiter „Funktionen“ → Webshop → **„Abschalten“**. Danach nimmt der Shop keine Bestellungen mehr an, das Widget zeigt den Ausweichtext, unbezahlte Bestellungen laufen aus.
|
||||
3. Der Händler kann Bestellungen und Widerrufe danach **90 Tage** lang im Portal lesen und exportieren. Danach löscht der tägliche Job automatisch alle Shopdaten (Artikel, Bilder, Bestellungen, Widerrufe, Einstellungen); das Einrichtungs-Ticket bleibt als Arbeitsnachweis ohne Käuferdaten. Die Widerrufsfunktion bleibt bis zu dieser Löschung erreichbar, solange das Widget eingebunden ist.
|
||||
4. Nimm nach dem Abschalten den Link „Vertrag widerrufen“ aus der Fußzeile und das Snippet aus der Website, sobald die 90 Tage um sind (oder der Händler den Shop nicht mehr braucht), und entferne den Shop-Menüpunkt.
|
||||
5. Beginnt **innerhalb der 90 Tage** wieder ein Vertrag über den Betrieb der Website, schaltest Du den Webshop auf Wunsch des Händlers in Textform ohne weitere Kosten wieder ein (WOMS → Funktionen → Webshop → Aktivieren; Daten und Einstellungen sind noch da). Nach der Löschung ist eine neue Anfrage und Einrichtung nötig (neuer Preis).
|
||||
6. **Erstattung (Leistungsbeschreibung Nr. 10 Abs. 6):** Endet der Vertrag über den Betrieb der Website, **bevor** wir den Webshop freigeschaltet haben (Ticket noch offen, Funktion im Zustand „in Einrichtung“), erstatten wir die 299 € vollständig, in Stripe von Hand. In allen anderen Fällen (Webshop war schon freigeschaltet, Abschalten auf Wunsch nach Abs. 7, Abschalten wegen verbotener Waren) gibt es keine Erstattung; nur der Widerruf der Bestellung richtet sich nach Nr. 9 Abs. 6.
|
||||
7. Widerruft ein Verbraucher oder Existenzgründer die Bestellung der Einrichtung (Seite „Vertrag widerrufen“, Auswahl „Webshop (Einrichtung)“), kommt die Mitteilung T09 mit Fristende und Preis; es wird nichts automatisch geändert. Du prüfst, erstattest in Stripe innerhalb von 14 Tagen (gegebenenfalls abzüglich Wertersatz für den begonnenen Einbau) und schaltest den Webshop ab. Mit dem Widerruf endet der Webshop.
|
||||
|
||||
Das Stripe-Konto des Händlers bleibt in jedem Fall beim Händler; wir rühren es nicht an.
|
||||
@@ -0,0 +1,91 @@
|
||||
# Webshop – Livegang
|
||||
|
||||
Reihenfolge für den Inhaber. Jeder Schritt braucht seine Freigabe; nichts davon hat Claude gegen Produktion oder das
|
||||
Live-Stripe-Konto ausgeführt. Code: Portal `feature/webshop` (Worktree `dev-webshop`), WOMS `feature/webshop`
|
||||
(Worktree `ticket.webklar.com/dev-webshop`). Beide Zweige bauen auf `feature/terminplaner-tp2` auf.
|
||||
|
||||
## 0. Voraussetzung
|
||||
|
||||
- **TP2 (Terminplaner-Verkauf) ist live.** Der Webshop nutzt das Funktionen-Register aus TP2. Erst TP2 nach `main`
|
||||
(Portal) bzw. `test` (WOMS), dann diesen Zweig darauf mergen (`git merge origin/main` im Worktree, Tests laufen lassen).
|
||||
- Der Zweig `feature/oeffnungszeiten` baut ebenfalls auf TP2 auf. Wer zuerst live geht, ist egal; der zweite merged den
|
||||
ersten nach (Konflikte erwartbar in `server/config.js` features, `public/app.js`, `public/funktionen.js`,
|
||||
`test/mailTemplates.test.js` (Vorlagen-IDs), `server/index.js`).
|
||||
|
||||
## 1. Stripe-Dashboard (WEBklar-Konto, live)
|
||||
|
||||
1. **Connect aktivieren**: Einstellungen → Connect → Plattformprofil ausfüllen: „Plattform/Software, deren Nutzer an
|
||||
eigene Kunden verkaufen“, Geldfluss **Direct Charges**, **keine** Plattformgebühr, Konten mit eigenem vollen
|
||||
Stripe-Dashboard (Standard-Konten). Danach unter Connect → Branding Name „WEBklar“, Icon und Farbe für das Onboarding.
|
||||
2. **Connect-Webhook** anlegen: Entwickler → Webhooks → Endpunkt hinzufügen, **„Ereignisse in verbundenen Konten“**,
|
||||
URL `https://project.webklar.com/api/shop/stripe-webhook`, Ereignisse:
|
||||
`account.updated`, `checkout.session.completed`, `checkout.session.async_payment_succeeded`,
|
||||
`checkout.session.async_payment_failed`, `checkout.session.expired`, `charge.refunded`.
|
||||
Das Signatur-Geheimnis (`whsec_…`) kommt in Schritt 2 in die `.env`.
|
||||
3. Nichts weiter: Händler-Konten legt das Portal selbst an, wenn ein Händler „Stripe-Konto einrichten“ klickt.
|
||||
|
||||
## 2. Server-Konfiguration
|
||||
|
||||
In `/home/webklar/apps/.env`:
|
||||
```
|
||||
SHOP_ENABLED=pilot
|
||||
SHOP_PILOT_EMAILS=kenso.gri@gmail.com
|
||||
STRIPE_CONNECT_WEBHOOK_SECRET=whsec_…
|
||||
```
|
||||
In `/home/webklar/apps/docker-compose.yml` beim Dienst `kundenbereich-server` die drei Variablen durchreichen
|
||||
(`SHOP_ENABLED=${SHOP_ENABLED:-off}`, `SHOP_PILOT_EMAILS=${SHOP_PILOT_EMAILS:-}`,
|
||||
`STRIPE_CONNECT_WEBHOOK_SECRET=${STRIPE_CONNECT_WEBHOOK_SECRET:-}`) und danach
|
||||
`docker compose -f /home/webklar/apps/docker-compose.yml up -d --no-deps kundenbereich-server`
|
||||
(ein `docker restart` liest die `.env` nicht neu). Artikelbilder liegen unter `build/upload-data/shop/` (gitignored,
|
||||
überleben Deploys wie die News-Fotos).
|
||||
|
||||
## 3. Datenbank und Stripe-Preis (vor dem Deploy)
|
||||
|
||||
```bash
|
||||
cd /home/webklar/apps/kundenbereich.webklar.com/build
|
||||
docker exec -w /app webklar-kundenbereich-server node scripts/shop-setup.mjs --check
|
||||
docker exec -w /app webklar-kundenbereich-server node scripts/shop-setup.mjs
|
||||
docker exec -w /app webklar-kundenbereich-server node scripts/stripe-products.mjs --pruefen
|
||||
docker exec -w /app webklar-kundenbereich-server node scripts/stripe-products.mjs --anlegen # legt shopSetup 299 € an
|
||||
```
|
||||
`shop-setup.mjs` legt fünf Collections an (`shopConfig`, `shopProducts`, `shopOrders`, `shopWiderrufe`,
|
||||
`shopRechtstexteArchiv`). Die Käufer-Mails laufen über `termineOutbox`; die Collection kommt aus dem TP2-Livegang
|
||||
(`scripts/termine-setup.mjs`). Die Skripte müssen aus dem neuen Code laufen (nach Schritt 4 oder vorher aus dem Worktree
|
||||
mit derselben Umgebung).
|
||||
|
||||
## 4. Deploy
|
||||
|
||||
- Portal: `feature/webshop` → `main` pushen (Webhook deployt).
|
||||
- WOMS: `feature/webshop` → `test` pushen; danach im WOMS „Rechtliches“: `scripts/setup-legal.mjs` laufen lassen (neue
|
||||
Karten „Leistungsbeschreibung Webshop“, „AVV Webshop“) und die Fassungen 2026-10-05 hochladen
|
||||
(`public/rechtliches/leistungsbeschreibung-webshop-2026-10-05.pdf`, `avv-webshop-2026-10-05.pdf`).
|
||||
- Prüfen: `https://project.webklar.com/rechtliches/leistungsbeschreibung-webshop.html` und
|
||||
`…/avv-webshop.html` antworten 200; `https://project.webklar.com/embed/shop.js` antwortet 200.
|
||||
|
||||
## 5. Pilot mit dem Testkonto
|
||||
|
||||
1. Im Portal als Testkunde: Funktionen → Webshop → Anfrage stellen (Projekt `test-website`).
|
||||
2. WOMS → Kunde → Funktionen → Webshop → „Anfrage annehmen“ (Mail K24 kommt an).
|
||||
3. Einrichtung: entweder bestellen (299 €, danach in Stripe erstatten) oder im WOMS „Aktivieren (ohne Zahlung)“
|
||||
(Pilot-Adressen brauchen keine Textform).
|
||||
4. Einbau nach `docs/funktionen/shop-einbau.md` auf der Test-Website, Ticket schließen → Freischaltung, Mail K16.
|
||||
5. Portal → Webshop → Übersicht → „Stripe-Konto einrichten“ (echtes Onboarding mit den Daten von WEBklar oder einem
|
||||
Testbetrieb; Stripe prüft Identität und Bankkonto).
|
||||
6. Einstellungen ausfüllen (Steuerart wählen!), einen Artikel für 1,00 € anlegen, Rechtstexte: Muster einsetzen,
|
||||
Platzhalter ersetzen, Impressum-/Datenschutz-URL, bestätigen, „Shop öffnen“.
|
||||
7. Testkauf auf der Website mit echter Karte → Mails G09 (Käufer) und K25 (Händler) prüfen → im Portal „Versendet“
|
||||
(G10) → „Erstatten“ (G13) → im Stripe-Dashboard des Händlers sichtbar.
|
||||
8. Widerruf über den Fußzeilen-Link testen (G12 mit Datum und Uhrzeit, K26 an den Händler).
|
||||
|
||||
## 6. Freigabe für alle
|
||||
|
||||
- Kanzlei-Paket `docs/rechtliches/webshop-kanzlei-paket.md` an die Kanzlei, Befund einarbeiten (neue Fassung, falls
|
||||
sich Texte ändern; ausgelieferte Fassungen werden nie überschrieben).
|
||||
- Checkliste in Abschnitt 7 des Kanzlei-Pakets abhaken.
|
||||
- `SHOP_ENABLED=on`, Compose neu anwenden (wie Schritt 2).
|
||||
|
||||
## Zurück
|
||||
|
||||
`SHOP_ENABLED=off` blendet die Kachel aus und stoppt neue Bestellungen. Bestehende Shops (Zustand aktiv oder
|
||||
abgeschaltet) behalten ihre Widerrufsfunktion, ihre Fristen und die Löschjobs laufen weiter. Einzelne Shops schaltet das
|
||||
Team im WOMS ab (Funktionen → Webshop → „Abschalten“), die Daten werden 90 Tage danach gelöscht.
|
||||
@@ -0,0 +1,166 @@
|
||||
# Shop: Muster-Rechtstexte (Widerrufsbelehrung, Widerrufsformular, AGB)
|
||||
|
||||
**Muster – keine Rechtsberatung.** Diese Texte sind Muster, die das Kundenportal im Webshop vorbefüllt (`server/services/shop/rechtstexte.js`, dort mit den Angaben aus den Shop-Einstellungen). Der Händler prüft sie, passt sie an und bestätigt sie selbst; WEBklar berät nicht rechtlich. Diese Datei enthält dieselben Texte mit Platzhaltern in eckigen Klammern zur Prüfung durch eine Kanzlei (Plan 2026-10-05, Task C5; Offene Punkte 5 und 9 der Recherche).
|
||||
|
||||
Stand: 2026-10-05.
|
||||
|
||||
## Quellen und Fundstellen
|
||||
|
||||
- Widerrufsbelehrung: Anlage 1 zu Art. 246a § 1 Abs. 2 S. 2 EGBGB, Fassung 2026 mit dem Baustein zur Widerrufsfunktion (https://www.gesetze-im-internet.de/bgbeg/BJNR006049896.html). Ein richtig ausgefülltes Muster erfüllt die Informationspflicht (Art. 246a § 1 Abs. 2 S. 3 EGBGB).
|
||||
- Elektronische Widerrufsfunktion: § 356a BGB (seit 19.06.2026), Hinweispflicht in der Belehrung nach Art. 246a § 1 Abs. 2 S. 1 Nr. 1 EGBGB.
|
||||
- Widerrufsformular: Anlage 2 zu Art. 246a § 1 Abs. 2 S. 1 Nr. 1 EGBGB.
|
||||
- Ausschluss und vorzeitiges Erlöschen: § 312g Abs. 2 Nr. 1, 2, 3 BGB; Information nach Art. 246a § 1 Abs. 3 EGBGB.
|
||||
- Gewährleistungshinweis: Art. 246a § 1 Abs. 1 S. 1 Nr. 11 EGBGB i. V. m. Durchführungsverordnung (EU) 2025/1960 Anhang I (seit 27.09.2026).
|
||||
- Streitbeilegung: § 36 VSBG. Die EU-Plattform zur Online-Streitbeilegung wurde am 20.07.2025 eingestellt; es gibt keinen Link darauf.
|
||||
- Button und Vertragsschluss: § 312j Abs. 3 und 4 BGB, § 312i BGB. Recherche: `/root/reports/webshop-2026-10-05/recherche-recht-stripe.md` (B.1 bis B.10).
|
||||
|
||||
## Hinweise zur Prüfung (nicht Teil der Texte)
|
||||
|
||||
- Fristbeginn: Die Belehrung enthält beide gesetzlichen Alternativen (eine Ware bzw. die Waren; mehrere Waren einer Bestellung, getrennt geliefert: die letzte Ware), weil der Warenkorb beides erzeugen kann. Teilsendungen und regelmäßige Lieferung sind nicht vorgesehen.
|
||||
- Rücksendekosten: Standard ist „Käufer trägt“. Variante „Händler trägt“ steht unten.
|
||||
- Abholung: Bietet der Shop Abholung an, kommt der Satz „Haben Sie die Waren bei uns abgeholt …“ hinzu.
|
||||
- Ausschlussgründe: Der letzte Absatz der Belehrung nennt § 312g Abs. 2 Nr. 1, 2 und 3 BGB in allgemeiner Form. Ob und für welche Waren er zutrifft, entscheidet der Händler; Einordnung von Lieferung und Abholung von Speisen (§ 312g Abs. 2 Nr. 9, § 312 Abs. 2 Nr. 8 BGB) ist offen (Recherche, Offene Punkte 6).
|
||||
- Gewährleistungshinweis: amtliche Grafik und wörtlicher Text aus Anhang I eingesetzt; offen bleibt nur der Abgleich der Grafikdatei mit der Originaldatei der Kommission.
|
||||
- Widerrufsfunktion: Erreichbarkeit ohne Login bei Gastbestellungen, Identifikation der Bestellung und Zeitstempel mit der Kanzlei abstimmen (Recherche, Offene Punkte 7).
|
||||
|
||||
## Widerrufsbelehrung
|
||||
|
||||
Widerrufsbelehrung
|
||||
|
||||
Widerrufsrecht
|
||||
|
||||
Sie haben das Recht, binnen vierzehn Tagen ohne Angabe von Gründen diesen Vertrag zu widerrufen.
|
||||
|
||||
Die Widerrufsfrist beträgt vierzehn Tage ab dem Tag, an dem Sie oder ein von Ihnen benannter Dritter, der nicht der Beförderer ist, die Waren in Besitz genommen haben bzw. hat.
|
||||
|
||||
Haben Sie mehrere Waren im Rahmen einer einheitlichen Bestellung bestellt und werden diese getrennt geliefert, beträgt die Widerrufsfrist vierzehn Tage ab dem Tag, an dem Sie oder ein von Ihnen benannter Dritter, der nicht der Beförderer ist, die letzte Ware in Besitz genommen haben bzw. hat.
|
||||
|
||||
Um Ihr Widerrufsrecht auszuüben, müssen Sie uns ([Name des Händlers], [Straße Nr.], [PLZ] [Ort], E-Mail: [E-Mail-Adresse]) mittels einer eindeutigen Erklärung (z. B. ein mit der Post versandter Brief oder eine E-Mail) über Ihren Entschluss, diesen Vertrag zu widerrufen, informieren. Sie können dafür das beigefügte Muster-Widerrufsformular verwenden, das jedoch nicht vorgeschrieben ist.
|
||||
|
||||
Sie können Ihr Widerrufsrecht auch online unter [Internetadresse der Widerrufsfunktion im Shop] ausüben. Wenn Sie diese Online-Funktion nutzen, übermitteln wir Ihnen auf einem dauerhaften Datenträger (z. B. durch eine E-Mail) unverzüglich eine Eingangsbestätigung mit Informationen zum Inhalt der Widerrufserklärung sowie dem Datum und der Uhrzeit ihres Eingangs.
|
||||
|
||||
Zur Wahrung der Widerrufsfrist reicht es aus, dass Sie die Mitteilung über die Ausübung des Widerrufsrechts vor Ablauf der Widerrufsfrist absenden.
|
||||
|
||||
Folgen des Widerrufs
|
||||
|
||||
Wenn Sie diesen Vertrag widerrufen, haben wir Ihnen alle Zahlungen, die wir von Ihnen erhalten haben, einschließlich der Lieferkosten (mit Ausnahme der zusätzlichen Kosten, die sich daraus ergeben, dass Sie eine andere Art der Lieferung als die von uns angebotene, günstigste Standardlieferung gewählt haben), unverzüglich und spätestens binnen vierzehn Tagen ab dem Tag zurückzuzahlen, an dem die Mitteilung über Ihren Widerruf dieses Vertrags bei uns eingegangen ist. Für diese Rückzahlung verwenden wir dasselbe Zahlungsmittel, das Sie bei der ursprünglichen Transaktion eingesetzt haben, es sei denn, mit Ihnen wurde ausdrücklich etwas anderes vereinbart; in keinem Fall werden Ihnen wegen dieser Rückzahlung Entgelte berechnet.
|
||||
|
||||
Wir können die Rückzahlung verweigern, bis wir die Waren wieder zurückerhalten haben oder bis Sie den Nachweis erbracht haben, dass Sie die Waren zurückgesandt haben, je nachdem, welches der frühere Zeitpunkt ist.
|
||||
|
||||
Sie haben die Waren unverzüglich und in jedem Fall spätestens binnen vierzehn Tagen ab dem Tag, an dem Sie uns über den Widerruf dieses Vertrags unterrichten, an uns ([Name des Händlers], [Straße Nr.], [PLZ] [Ort]) zurückzusenden oder zu übergeben. Die Frist ist gewahrt, wenn Sie die Waren vor Ablauf der Frist von vierzehn Tagen absenden.
|
||||
|
||||
Sie tragen die unmittelbaren Kosten der Rücksendung der Waren.
|
||||
|
||||
Sie müssen für einen etwaigen Wertverlust der Waren nur aufkommen, wenn dieser Wertverlust auf einen zur Prüfung der Beschaffenheit, Eigenschaften und Funktionsweise der Waren nicht notwendigen Umgang mit ihnen zurückzuführen ist.
|
||||
|
||||
Ausschluss bzw. vorzeitiges Erlöschen des Widerrufsrechts
|
||||
|
||||
Das Widerrufsrecht besteht nicht bei Verträgen zur Lieferung von Waren, die nicht vorgefertigt sind und für deren Herstellung eine individuelle Auswahl oder Bestimmung durch den Verbraucher maßgeblich ist oder die eindeutig auf die persönlichen Bedürfnisse des Verbrauchers zugeschnitten sind (§ 312g Abs. 2 Nr. 1 BGB), z. B. Waren mit Wunschaufdruck oder Maßanfertigungen.
|
||||
Das Widerrufsrecht besteht nicht bei Verträgen zur Lieferung von Waren, die schnell verderben können oder deren Verfallsdatum schnell überschritten würde (§ 312g Abs. 2 Nr. 2 BGB), z. B. frische Lebensmittel.
|
||||
Das Widerrufsrecht erlischt vorzeitig bei Verträgen zur Lieferung versiegelter Waren, die aus Gründen des Gesundheitsschutzes oder der Hygiene nicht zur Rückgabe geeignet sind, wenn ihre Versiegelung nach der Lieferung entfernt wurde (§ 312g Abs. 2 Nr. 3 BGB).
|
||||
|
||||
Ende der Widerrufsbelehrung
|
||||
|
||||
### Variante: Händler trägt die Rücksendekosten
|
||||
|
||||
Statt „Sie tragen die unmittelbaren Kosten der Rücksendung der Waren.“ steht:
|
||||
|
||||
Wir tragen die Kosten der Rücksendung der Waren.
|
||||
|
||||
### Variante: Abholung angeboten
|
||||
|
||||
Nach dem Absatz zur Rücksendung folgt zusätzlich:
|
||||
|
||||
Haben Sie die Waren bei uns abgeholt, übergeben Sie diese zur Rückgabe an unserer Anschrift: [Abholadresse].
|
||||
|
||||
## Widerrufsformular
|
||||
|
||||
Muster-Widerrufsformular
|
||||
|
||||
(Wenn Sie den Vertrag widerrufen wollen, dann füllen Sie bitte dieses Formular aus und senden Sie es zurück.)
|
||||
|
||||
An [Name des Händlers], [Straße Nr.], [PLZ] [Ort], E-Mail: [E-Mail-Adresse]:
|
||||
|
||||
Hiermit widerrufe(n) ich/wir (*) den von mir/uns (*) abgeschlossenen Vertrag über den Kauf der folgenden Waren (*)/die Erbringung der folgenden Dienstleistung (*)
|
||||
|
||||
– Bestellt am (*)/erhalten am (*)
|
||||
– Name des/der Verbraucher(s)
|
||||
– Anschrift des/der Verbraucher(s)
|
||||
– Unterschrift des/der Verbraucher(s) (nur bei Mitteilung auf Papier)
|
||||
– Datum
|
||||
|
||||
(*) Unzutreffendes streichen.
|
||||
|
||||
## Allgemeine Geschäftsbedingungen
|
||||
|
||||
Die Kopfzeile „Muster – bitte prüfen und anpassen“ steht im vorbefüllten Text und wird vom Händler nach der Prüfung entfernt. Die Zahlungsarten, Länder und Abholung werden aus den Shop-Einstellungen eingesetzt; die Platzhalter unten zeigen den Standardfall.
|
||||
|
||||
Muster – bitte prüfen und anpassen
|
||||
|
||||
Allgemeine Geschäftsbedingungen
|
||||
|
||||
§ 1 Geltung und Anbieter
|
||||
|
||||
Diese Allgemeinen Geschäftsbedingungen gelten für alle Bestellungen von Verbrauchern in unserem Online-Shop. Anbieter und Vertragspartner ist [Name des Händlers], [Straße Nr.], [PLZ] [Ort], E-Mail: [E-Mail-Adresse].
|
||||
|
||||
§ 2 Vertragsschluss
|
||||
|
||||
Die Darstellung der Waren im Shop ist kein verbindliches Angebot. Mit dem Klick auf die Schaltfläche „Zahlungspflichtig bestellen“ geben Sie eine verbindliche Bestellung ab. Den Eingang Ihrer Bestellung bestätigen wir Ihnen sofort auf einer Bestätigungsseite im Shop. Bei den Zahlungsarten Kreditkarte, Apple Pay, Google Pay, Klarna erhalten Sie die Bestellbestätigung per E-Mail unmittelbar nach erfolgreicher Zahlung; mit dieser E-Mail nehmen wir Ihre Bestellung an und der Vertrag kommt zustande. Bei SEPA-Lastschrift senden wir Ihnen zunächst eine E-Mail über den Eingang Ihrer Bestellung; der Vertrag kommt in diesem Fall erst zustande, wenn wir Ihnen nach Eingang der Zahlung die Bestellbestätigung per E-Mail senden. Bestellungen, die nicht bezahlt werden, werden nach Ablauf der Zahlungsfrist verworfen.
|
||||
|
||||
§ 3 Preise und Versandkosten
|
||||
|
||||
Es gelten die zum Zeitpunkt der Bestellung im Shop angegebenen Preise in Euro. Alle Preise enthalten die gesetzliche Umsatzsteuer. Versandkosten und etwaige weitere Kosten werden vor Abgabe der Bestellung gesondert ausgewiesen und richten sich nach dem Zielland. Einzelheiten finden Sie unter „Versand & Zahlung“.
|
||||
|
||||
§ 4 Zahlung
|
||||
|
||||
Die Zahlung erfolgt über den Zahlungsdienstleister Stripe. Es stehen folgende Zahlungsarten zur Verfügung: Kreditkarte, Apple Pay, Google Pay, Klarna, SEPA-Lastschrift. Bei SEPA-Lastschrift versenden wir die Ware erst nach Eingang der Zahlung.
|
||||
|
||||
§ 5 Lieferung und Abholung
|
||||
|
||||
Wir liefern in folgende Länder: Deutschland. Die Lieferzeit beträgt [Lieferzeit], gerechnet ab Zahlungseingang. Ist eine Ware nicht verfügbar, informieren wir Sie unverzüglich.
|
||||
|
||||
§ 6 Eigentumsvorbehalt
|
||||
|
||||
Die Waren bleiben bis zur vollständigen Bezahlung unser Eigentum.
|
||||
|
||||
§ 7 Widerrufsrecht
|
||||
|
||||
Verbrauchern steht ein gesetzliches Widerrufsrecht zu. Einzelheiten ergeben sich aus der Widerrufsbelehrung, die Sie unter „Widerrufsbelehrung“ im Shop finden.
|
||||
|
||||
§ 8 Gewährleistung
|
||||
|
||||
Es gelten die gesetzlichen Mängelhaftungsrechte. Der Hinweis auf das gesetzliche Gewährleistungsrecht steht vor Abgabe der Bestellung im Shop.
|
||||
|
||||
§ 9 Technische Schritte, Vertragstext und Sprache
|
||||
|
||||
Zum Vertragsschluss führen folgende Schritte: Sie legen die gewünschten Waren in den Warenkorb, prüfen die Bestellübersicht mit allen Angaben und klicken auf „Zahlungspflichtig bestellen“. Danach zahlen Sie bei unserem Zahlungsdienstleister Stripe. Der Vertrag kommt nach § 2 mit unserer Bestellbestätigung per E-Mail zustande. Vor dem Klick auf „Zahlungspflichtig bestellen“ können Sie Eingabefehler erkennen und berichtigen: Im Warenkorb ändern Sie Mengen oder entfernen Waren, aus der Bestellübersicht gelangen Sie über „Zurück zum Warenkorb“ dorthin zurück, und Ihre Angaben zu Adresse und Zahlung prüfen und korrigieren Sie bei Stripe, bevor Sie die Zahlung abschließen. Wir speichern den Vertragstext nach Vertragsschluss nicht für Sie abrufbar. Ihre Bestelldaten, diese Allgemeinen Geschäftsbedingungen und die Widerrufsbelehrung erhalten Sie mit der Bestellbestätigung per E-Mail; bitte bewahren Sie diese E-Mail auf. Die Vertragssprache ist Deutsch. Wir unterwerfen uns keinen besonderen Verhaltenskodizes.
|
||||
|
||||
§ 10 Streitbeilegung
|
||||
|
||||
[Name des Händlers] ist nicht bereit und nicht verpflichtet, an einem Streitbeilegungsverfahren vor einer Verbraucherschlichtungsstelle teilzunehmen (§ 36 VSBG).
|
||||
|
||||
Zu § 2: Der Satz zur sofortigen Bestellbestätigung nennt nur die Zahlungsarten Karte/Apple Pay/Google Pay/Klarna; der SEPA-Satz erscheint nur, wenn SEPA-Lastschrift aktiv ist. Ohne SEPA entfällt er, bei ausschließlich SEPA entfällt der Satz davor. Der Ablauf im Shop: Bestätigungsseite sofort nach der Bestellung; bei Karte/Klarna folgt die Bestellbestätigung (G09, Vertragsschluss) unmittelbar nach der Zahlung; bei SEPA zuerst die Eingangsmail (G15), die Bestellbestätigung (G09) erst nach Zahlungseingang.
|
||||
|
||||
Zu § 9: enthält die Angaben nach Art. 246c EGBGB (technische Schritte, Eingabefehler erkennen und berichtigen, Speicherung des Vertragstextes, Sprache, Verhaltenskodizes).
|
||||
|
||||
Bei Kleinunternehmern steht in § 3 statt „Alle Preise enthalten die gesetzliche Umsatzsteuer.“ der Satz „Gemäß § 19 UStG wird keine Umsatzsteuer berechnet und ausgewiesen.“
|
||||
|
||||
## Hinweis auf das gesetzliche Gewährleistungsrecht (Kasse und Bestellbestätigung)
|
||||
|
||||
Amtliche Grafik nach Durchführungsverordnung (EU) 2025/1960 Anhang I (ABl. L vom 2.10.2025), nicht editierbar, online farbig: `public/img/gewaehrleistung-eu.webp` (aus dem Amtsblatt ausgeschnitten; vor Livegang mit der Originaldatei der Kommission abgleichen, Kanzlei-Frage). Wörtlicher Text der Grafik (Alternativtext, Bestellbestätigung):
|
||||
|
||||
GESETZLICHE GEWÄHRLEISTUNG
|
||||
|
||||
Mindestens zwei Jahre gesetzliche Gewährleistung der Vertragsmäßigkeit für Waren, die in der Europäischen Union verkauft werden.
|
||||
|
||||
Verbraucherinnen und Verbraucher können ihre Rechte im Rahmen des gesetzlichen Gewährleistungsrechts geltend machen, z. B. wenn die Waren nicht der Beschreibung entsprechen, nicht bestimmungsgemäß funktionieren.
|
||||
|
||||
Verkäufer haften für jede Vertragswidrigkeit, die zum Zeitpunkt der Lieferung der Waren bestand und innerhalb des Zeitraums der gesetzlichen Gewährleistung erkennbar wird. Verkäufer müssen in solchen Fällen Folgendes anbieten: kostenlose Nachbesserung oder kostenlose Ersatzlieferung, in bestimmten Fällen eine Preisminderung oder eine vollständige Erstattung des Kaufpreises.
|
||||
|
||||
In einigen Ländern gilt ein längerer Zeitraum für die gesetzliche Gewährleistung. Für gebrauchte Waren kann ein kürzerer Zeitraum gelten, jedoch nicht weniger als ein Jahr.
|
||||
|
||||
Für weitere Informationen zu Ihren Rechten in einem bestimmten Land scannen Sie den nachstehenden QR-Code oder fragen Sie den Verkäufer. europa.eu/youreurope/garantien
|
||||
|
||||
Was ist zu tun, wenn Sie vertragswidrige Waren erhalten? 1. Melden Sie dem Verkäufer das Problem so bald wie möglich. 2. Legen Sie einen Kaufnachweis vor, z. B. die Quittung, Rechnung oder einen Kontoauszug.
|
||||
|
||||
Verkäufer und Hersteller können auch gewerbliche Garantien gewähren, die unabhängig von der gesetzlichen Gewährleistung gelten. Diese GARAN-Kennzeichnung zeigt beispielsweise, dass der Hersteller eine gewerbliche Haltbarkeitsgarantie ohne zusätzliche Kosten gewährt, die die gesamte Ware abdeckt.
|
||||
@@ -115,7 +115,7 @@ Bricht das Netz mitten in der Strecke ab, zeigt das Widget „Gerade nicht errei
|
||||
2. Person wählen („Egal“ zuerst). Nur wenn mehrere Personen infrage kommen und der Betrieb die Wahl erlaubt; bei Plätzen entfällt der Schritt.
|
||||
3. Tag (Monatsraster, nur Tage mit freien Zeiten sind wählbar) und Uhrzeit. Alle Daten und Uhrzeiten kommen vom Server in Berliner Zeit. Das Widget rechnet nie mit dem Datum oder der Zeitzone des Browsers.
|
||||
4. Angaben: Name, E-Mail, Telefon, Nachricht und Zusatzfelder nach den Einstellungen des Betriebs.
|
||||
5. Zusammenfassung mit Beschreibung der Leistung, Stornobedingungen (daneben der Link „AGB des Betriebs“, wenn im Portal eine https-Adresse hinterlegt ist), Widerrufsbelehrung (aufklappbar, bei Verbraucher-Betrieben), Datenschutz-Hinweis, den Haken (Storno, vorzeitiger Beginn, Erinnerung) und dem Knopf („Zahlungspflichtig buchen“, „Termin buchen“ oder „Termin anfragen“, kommt vom Server).
|
||||
5. Zusammenfassung mit Beschreibung der Leistung, Stornobedingungen (nur bei einer Leistung mit Preis; daneben der Link „AGB des Betriebs“, wenn im Portal eine https-Adresse hinterlegt ist), Widerrufsbelehrung (aufklappbar, bei Verbraucher-Betrieben), Datenschutz-Hinweis, den Haken (Storno nur bei einer Leistung mit Preis, vorzeitiger Beginn, Erinnerung) und dem Knopf („Zahlungspflichtig buchen“, „Termin buchen“ oder „Termin anfragen“, kommt vom Server).
|
||||
6. „Fast geschafft“: Der Gast bestätigt den Termin über den Link in der E-Mail. Die Reservierung gilt 30 Minuten.
|
||||
|
||||
Im Fuß stehen „Vertrag widerrufen“ als zweiter Knopf (kein bloßer Textlink; für Verbraucher bei jeder Leistung, ob sofort buchbar oder als Anfrage, weil auch ein angenommener Anfrage-Vertrag widerrufen werden kann), „Datenschutz“ (Link auf die Datenschutzerklärung des Betriebs) und klein „Terminbuchung über WEBklar“. Im Modus `knopf` steht der Fuß nur im geöffneten Dialog. Damit die Widerrufsfunktion (§ 356a BGB) trotzdem dauerhaft auffindbar ist, lädt das Widget dort schon beim Laden der Seite die Konfiguration (ein einziger Abruf, sonst nichts) und setzt, wenn der Betrieb ein Widerrufsrecht hat, den Knopf „Vertrag widerrufen“ als zweiten Knopf neben den Buchungsknopf. Er öffnet den Dialog gleich beim Widerrufsformular. Anrede (Sie oder Du) und Betriebsfarbe kommen aus den Einstellungen. Die rechtlich vorgeschriebenen Texte (Haken, Belehrung) stehen immer in der Sie-Form.
|
||||
|
||||
@@ -31,4 +31,4 @@ Die Texte, die ein Gast beim Buchen sieht (Hinweis am Formular, Texte der Haken,
|
||||
|
||||
„Gültig ab“ einer neuen Fassung wird beim Livegang eingetragen; ihr Zeitraum endet mit dem „gültig ab“ der nächsten.
|
||||
|
||||
Änderungen der Fassung 2026-10-05 gegenüber 2026-10-02 (nur diese beiden Stellen): Der Haken für die Erinnerung endet mit dem Satz „Diese Einwilligung kann ich jederzeit widerrufen.“ (Art. 7 Abs. 3 DSGVO); der Datenschutz-Absatz sagt bei der Abschaltung: „Wird die Online-Terminbuchung abgeschaltet, zum Beispiel weil unser Vertrag mit WEBklar endet, werden alle Termindaten 30 Tage nach dem Abschalten gelöscht.“ Buchungen, die vor dem Livegang der neuen Fassung entstanden sind, behalten im Nachweis die Fassung 2026-10-02.
|
||||
Änderungen der Fassung 2026-10-05 gegenüber 2026-10-02 (Textänderungen: nur diese beiden Stellen): Der Haken für die Erinnerung endet mit dem Satz „Diese Einwilligung kann ich jederzeit widerrufen.“ (Art. 7 Abs. 3 DSGVO); der Datenschutz-Absatz sagt bei der Abschaltung: „Wird die Online-Terminbuchung abgeschaltet, zum Beispiel weil unser Vertrag mit WEBklar endet, werden alle Termindaten 30 Tage nach dem Abschalten gelöscht.“ Außerdem ändert sich die Regel zum Haken „Stornobedingungen“, nicht sein Text: Pflicht ist er nur noch bei einer Leistung mit Preis, bei kostenlosen Leistungen fragt der Terminplaner nicht danach (die Fassung 2026-10-02 verlangte ihn bei jeder Leistung, sobald der Betrieb Stornobedingungen hat). Buchungen, die vor dem Livegang der neuen Fassung entstanden sind, behalten im Nachweis die Fassung 2026-10-02.
|
||||
@@ -26,7 +26,7 @@ Die Haken stehen immer in der Sie-Form, auch bei Betrieben, die ihre Gäste duze
|
||||
|
||||
> Ich verlange ausdrücklich, dass {{Name des Betriebs}} mit der Leistung vor Ablauf der Widerrufsfrist beginnt. Mir ist bekannt, dass mein Widerrufsrecht mit der vollständigen Erbringung der Leistung erlischt (§ 356 Abs. 5 Nr. 2 BGB). Widerrufe ich vor der vollständigen Erbringung, schulde ich einen angemessenen Betrag für die bis dahin erbrachte Leistung (§ 357a Abs. 2 BGB).
|
||||
|
||||
### Stornobedingungen (Pflicht, wenn der Betrieb Stornobedingungen hat; der Text der Bedingungen steht beim Betrieb und trägt eine eigene Version)
|
||||
### Stornobedingungen (Pflicht bei einer Leistung mit Preis, wenn der Betrieb Stornobedingungen hat; bei kostenlosen Leistungen fragt der Terminplaner nicht danach; der Text der Bedingungen steht beim Betrieb und trägt eine eigene Version)
|
||||
|
||||
> Ich habe die Stornobedingungen gelesen.
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ zwischen dem Kunden als Verantwortlichem („Kunde“) und der WEBklar GbR, Am S
|
||||
|
||||
(1) Der Kunde schließt diesen Vertrag, wenn er im Kundenportal die Einrichtung des Terminplaners bestellt und dabei per Haken diesem Vertrag und der Leistungsbeschreibung „Terminplaner“ („Leistungsbeschreibung“) in der angezeigten Fassung zustimmt. Das Portal speichert die Fassung, den Zeitpunkt, das Kundenkonto und die angemeldete Person. Anschließend sendet WEBklar dem Kunden eine E-Mail, die beide Texte und die Widerrufsbelehrung in dieser Fassung als PDF enthält und auf sie verlinkt.
|
||||
|
||||
(2) Ein Mitarbeiter von WEBklar darf die Funktion nur einschalten, wenn der Kunde vorher in Textform (etwa im Chat des Kundenportals oder per E-Mail) erklärt hat, dass er diesem Vertrag und der Leistungsbeschreibung in der aktuellen Fassung zustimmt. WEBklar speichert die Fassung, das Datum und den Weg dieser Erklärung, den Zeitpunkt des Einschaltens und den Namen des Mitarbeiters. Der Kunde erhält die E-Mail nach Absatz 1 anschließend.
|
||||
(2) Ein Mitarbeiter von WEBklar darf die Funktion nur einschalten, wenn der Kunde vorher in Textform (etwa im Chat des Kundenportals oder per E-Mail) erklärt hat, dass er diesem Vertrag und der Leistungsbeschreibung in der aktuellen Fassung zustimmt. WEBklar speichert die Fassung, das Datum und den Weg dieser Erklärung, den Zeitpunkt des Einschaltens und den Namen des Mitarbeiters. Der Kunde erhält anschließend eine E-Mail mit diesen beiden Texten als PDF.
|
||||
|
||||
(3) Dieser Vertrag ist ein eigener Vertrag für den Terminplaner. Er ergänzt den Auftragsverarbeitungsvertrag in Anlage 1 der Abo-Bedingungen. Für den Terminplaner geht er Anlage 1 vor; im Übrigen gilt Anlage 1 ergänzend. In Datenschutzfragen geht er den Abo-Bedingungen und der Leistungsbeschreibung vor.
|
||||
|
||||
@@ -72,7 +72,7 @@ WEBklar setzt nur Personen ein, die zur Vertraulichkeit verpflichtet sind oder e
|
||||
|
||||
(1) WEBklar leitet Anfragen von Gästen und Beschäftigten, die an WEBklar gerichtet sind, unverzüglich an den Kunden weiter. WEBklar unterstützt ihn mit geeigneten technischen und organisatorischen Maßnahmen bei der Beantwortung.
|
||||
|
||||
(2) Dafür bietet das Kundenportal die Aktion „Gastdaten löschen“ und den Export der Termine als CSV-Datei. Mit „Gastdaten löschen“ leert der Kunde Name, E-Mail-Adresse, Telefonnummer, Nachricht und Zusatzfelder einer Buchung; Status und Zeiten bleiben. Noch nicht versandte E-Mails zu dieser Buchung löscht WEBklar dabei, und die Links des Gastes werden ungültig. Eine noch nicht bestätigte Buchung verfällt von selbst (Nummer 9 Abs. 1).
|
||||
(2) Dafür bietet das Kundenportal die Aktion „Gastdaten löschen“ und den Export der Termine als CSV-Datei. Mit „Gastdaten löschen“ leert der Kunde Name, E-Mail-Adresse, Telefonnummer, Nachricht und Zusatzfelder einer Buchung; Status und Zeiten bleiben. Dabei löscht WEBklar die E-Mails zu dieser Buchung, die noch auf den Versand warten, und die Links des Gastes werden ungültig. E-Mails, deren Versand endgültig fehlgeschlagen ist, behalten Empfängeradresse und Inhalt, bis WEBklar sie nach Nummer 9 Abs. 4 löscht (48 Stunden nach dem Fehlschlag). Eine noch nicht bestätigte Buchung verfällt von selbst (Nummer 9 Abs. 1).
|
||||
|
||||
(3) Verletzungen des Schutzes personenbezogener Daten meldet WEBklar dem Kunden unverzüglich, möglichst innerhalb von 24 Stunden nach Kenntnis, mit den Informationen, die zu diesem Zeitpunkt vorliegen.
|
||||
|
||||
@@ -94,7 +94,7 @@ WEBklar setzt nur Personen ein, die zur Vertraulichkeit verpflichtet sind oder e
|
||||
|
||||
(5) Der Löschlauf läuft täglich; gelöscht wird frühestens nach Ablauf der jeweiligen Frist. Ist an einem Tag mehr zu löschen, als ein Lauf schafft, folgt der Rest im nächsten Lauf.
|
||||
|
||||
(6) Ist der Terminplaner abgeschaltet oder das Wartungs-Abo beendet, löscht WEBklar alle Termindaten und die Einstellungen 30 Tage danach. Bis dahin kann der Kunde die Termine im Kundenportal als CSV-Datei exportieren. Beginnt innerhalb dieser Frist wieder ein Wartungs-Abo, schaltet WEBklar den Terminplaner mit den vorhandenen Daten und Einstellungen wieder ein (Nr. 8 Abs. 4 der Leistungsbeschreibung). Früher löscht der Kunde einzelne Gastdaten selbst mit der Aktion „Gastdaten löschen“ (Nummer 8 Abs. 2); alle Daten löscht WEBklar auf Weisung des Kunden in Textform unverzüglich.
|
||||
(6) Ist der Terminplaner abgeschaltet oder das Wartungs-Abo beendet, löscht WEBklar alle Termindaten und die Einstellungen 30 Tage danach. Eine Ausnahme ist das Einrichtungs-Ticket, in dem WEBklar die Einrichtung als Arbeitsnachweis festhält: Es bleibt mit einer Kurzfassung der Einstellungen bestehen (zum Beispiel Betriebsangaben, Namen der Leistungen, die ersten acht Namen der Personen oder Plätze und die Wünsche des Kunden zum Einbau) und enthält keine Daten von Gästen; dafür gilt die Datenschutzerklärung von WEBklar (Abschnitt 6). Bis zur Löschung kann der Kunde die Termine im Kundenportal als CSV-Datei exportieren. Beginnt innerhalb dieser Frist wieder ein Wartungs-Abo, schaltet WEBklar den Terminplaner mit den vorhandenen Daten und Einstellungen wieder ein (Nr. 8 Abs. 4 der Leistungsbeschreibung). Früher löscht der Kunde einzelne Gastdaten selbst mit der Aktion „Gastdaten löschen“ (Nummer 8 Abs. 2); alle Daten löscht WEBklar auf Weisung des Kunden in Textform unverzüglich.
|
||||
|
||||
(7) Entwürfe aus dem Fragebogen, aus denen keine Bestellung geworden ist, löscht WEBklar 90 Tage nach der letzten Änderung.
|
||||
|
||||
@@ -138,7 +138,7 @@ WEBklar stellt dem Kunden alle Informationen zur Verfügung, die zum Nachweis de
|
||||
| Missbrauchsschutz | Ein verstecktes Feld (Honeypot), eine Mindestzeit bis zum Absenden des Formulars mit signiertem Formular-Token und eine Mindestantwortzeit schützen vor automatisierten Buchungen. Grenzen je IP-Adresse, je Website und je E-Mail-Adresse sowie das Mailbudget je Stunde (insgesamt und je Funktion) gelten nur im Arbeitsspeicher; ein Neustart leert sie. |
|
||||
| Datenminimierung | Pflicht sind immer nur Name und E-Mail-Adresse; ob Telefonnummer, Nachricht und Zusatzfelder abgefragt werden, legt der Kunde fest. Mit der Buchung werden keine IP-Adresse und kein Browser-Kennzeichen gespeichert. Der Nachweis der Zustimmungen besteht aus der Fassung der Gästetexte, dem Zeitpunkt, dem Text des Buchungsknopfs, der Anrede und Hashes der übrigen gezeigten Texte, nicht aus diesen Texten. Nach dem Versand einer E-Mail werden Empfängeradresse und Inhalt in der Warteschlange geleert. |
|
||||
| Trennung | Alle Buchungen, Belegungen und Warteschlangeneinträge tragen die Kennung der Funktion (eines Terminplaners je Website-Projekt); das Kundenportal und die öffentliche Schnittstelle lesen immer nur die Daten der jeweiligen Funktion. Die Hintergrundläufe (Löschung, Versand der E-Mails, Erinnerungen) gehen die Dokumente aller Funktionen nacheinander durch und schreiben jeweils nur in das Dokument, um das es geht. Die Einstellungen liegen je Funktion in einem eigenen Dokument. |
|
||||
| Löschung | Täglicher Löschlauf für Buchungen, Belegungen, Warteschlange und Entwürfe nach den Fristen der Nummer 9; Termindaten und Einstellungen abgeschalteter Funktionen 30 Tage nach dem Abschalten. Vor der Löschung einer Buchung, einer abgeschalteten Funktion oder eines nicht bestellten Fragebogens wird der Zustand frisch gelesen; bei einem Lesefehler wird nichts gelöscht. |
|
||||
| Löschung | Täglicher Löschlauf für Buchungen, Belegungen, Warteschlange und Entwürfe nach den Fristen der Nummer 9; Termindaten und Einstellungen abgeschalteter Funktionen 30 Tage nach dem Abschalten; das Einrichtungs-Ticket bleibt als Arbeitsnachweis mit einer Kurzfassung der Einstellungen bestehen und enthält keine Daten von Gästen (Nummer 9 Abs. 6). Vor der Löschung einer Buchung, einer abgeschalteten Funktion oder eines nicht bestellten Fragebogens wird der Zustand frisch gelesen; bei einem Lesefehler wird nichts gelöscht. |
|
||||
| Protokolle | Die Programmprotokolle des Terminplaners und der Tagesbericht enthalten nur Zählungen und Buchungsnummern, keine Namen, E-Mail-Adressen oder Nachrichten von Gästen. Server-Protokolle des Webservers werden nach 14 Tagen gelöscht (Anlage 2 der Abo-Bedingungen). |
|
||||
| Datensicherung | Tägliche Vollsicherung des Servers durch Hetzner; verfügbar sind die letzten sieben Tagessicherungen (§ 3 Abs. 5 der Abo-Bedingungen). |
|
||||
| Rechenzentrum | Hetzner Online GmbH, Rechenzentren Falkenstein und Nürnberg (Deutschland). Hetzner ist nach ISO/IEC 27001 zertifiziert; der Geltungsbereich umfasst die Hosting-Dienstleistungen und die Rechenzentren. |
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
# Auftragsverarbeitungsvertrag Webshop (Art. 28 DSGVO)
|
||||
|
||||
**Fassung 2026-10-05**
|
||||
|
||||
zwischen dem Kunden als Verantwortlichem („Kunde“; in der Leistungsbeschreibung „Händler“) und der WEBklar GbR, Am Schwimmbad 10, 67722 Winnweiler, vertreten durch die Gesellschafter Kenso Grimm und Justin Klein, als Auftragsverarbeiter („WEBklar“).
|
||||
|
||||
## Vorbemerkung: Abschluss und Verhältnis zu anderen Verträgen
|
||||
|
||||
(1) Der Kunde schließt diesen Vertrag, wenn er im Kundenportal die Einrichtung des Webshops bestellt und dabei per Haken diesem Vertrag und der Leistungsbeschreibung „Webshop“ („Leistungsbeschreibung“) in der angezeigten Fassung zustimmt. Das Portal speichert die Fassung, den Zeitpunkt, das Kundenkonto und die angemeldete Person. Anschließend sendet WEBklar dem Kunden eine E-Mail, die beide Texte und die Widerrufsbelehrung in dieser Fassung als PDF enthält und auf sie verlinkt.
|
||||
|
||||
(2) Ein Mitarbeiter von WEBklar darf die Funktion nur einschalten, wenn der Kunde vorher in Textform (etwa im Chat des Kundenportals oder per E-Mail) erklärt hat, dass er diesem Vertrag und der Leistungsbeschreibung in der aktuellen Fassung zustimmt. Das Portal verlangt dafür das Datum und den Weg der Erklärung; fehlt eine Angabe, schaltet es nicht ein. WEBklar speichert vor dem Einschalten einen Zustimmungsnachweis mit der Fassung der beiden Texte, dem Datum und dem Weg dieser Erklärung, dem Zeitpunkt des Einschaltens und dem Namen des Mitarbeiters (ohne IP-Adresse und Browser-Kennzeichen, weil der Kunde nicht im Portal zugestimmt hat). Der Kunde erhält anschließend eine E-Mail mit diesen beiden Texten als PDF.
|
||||
|
||||
(3) Dieser Vertrag ist ein eigener Vertrag für den Webshop. Er ergänzt den Auftragsverarbeitungsvertrag in Anlage 1 der Abo-Bedingungen. Für den Webshop geht er Anlage 1 vor; im Übrigen gilt Anlage 1 ergänzend. In Datenschutzfragen geht er den Abo-Bedingungen und der Leistungsbeschreibung vor.
|
||||
|
||||
(4) Die Rollen: Der Kunde ist Verantwortlicher für die personenbezogenen Daten seiner Käufer im Webshop. WEBklar ist sein Auftragsverarbeiter. Stripe ist nicht Auftragsverarbeiter von WEBklar, sondern Vertragspartner des Kunden (Nummer 7 Abs. 4).
|
||||
|
||||
## 1. Gegenstand und Dauer
|
||||
|
||||
(1) WEBklar betreibt für die öffentliche Website des Kunden ein Shop-Widget mit einer Bestellverwaltung im Kundenportal („Webshop“) und verarbeitet dabei personenbezogene Daten der Käufer des Kunden im Auftrag des Kunden. Den Umfang beschreibt die Leistungsbeschreibung.
|
||||
|
||||
(2) Der Vertrag beginnt mit dem Einschalten der Funktion. Er endet mit dem Abschalten, spätestens mit dem Ende des Vertrags, aufgrund dessen WEBklar die Website des Kunden betreibt; den Webshop schaltet WEBklar dann zum Vertragsende ab (Nr. 10 Abs. 1 der Leistungsbeschreibung). Die Pflichten aus diesem Vertrag gelten fort, bis alle Daten nach Nummer 9 gelöscht sind.
|
||||
|
||||
## 2. Art und Zweck der Verarbeitung
|
||||
|
||||
(1) Art: Übermitteln der Bestellung vom Widget an den Server von WEBklar, Übernehmen der Zahlungs- und Adressangaben aus der Checkout-Sitzung bei Stripe, Speichern, Verwalten und Anzeigen im Kundenportal, Versenden von E-Mails, Bereitstellen einer CSV-Datei, Weitergeben von Erstattungen an Stripe im Auftrag des Kunden, Anonymisieren und Löschen.
|
||||
|
||||
(2) Zwecke: Der Kunde soll Bestellungen seiner Käufer abwickeln. Dazu nimmt der Webshop Bestellungen entgegen, reserviert den Bestand, gleicht Zahlungen mit Stripe ab, bestätigt Bestellungen (bei einer Zahlung, die noch aussteht, etwa einer SEPA-Lastschrift, zunächst nur mit einer Eingangsmail ohne Vertragsschluss), bildet Versand und Abholung ab, nimmt Widerrufe entgegen, erstattet Zahlungen im Auftrag des Kunden und benachrichtigt den Kunden. WEBklar versendet die E-Mails im Auftrag des Kunden. Außerdem schützt WEBklar den Webshop für den Kunden vor Missbrauch (Mengenbegrenzung, Begrenzung der offenen Bestellungen je IP-Adresse, Erkennen automatisierter Bestellungen und Widerrufe).
|
||||
|
||||
(3) WEBklar verwendet die Daten für keine eigenen Zwecke, auch nicht in anonymisierter Form und nicht für Auswertungen über mehrere Kunden. Es findet keine Werbung und keine Zusammenführung mit anderen Daten statt. WEBklar bildet keine Profile einzelner Käufer. Zählungen ohne Personenbezug, die WEBklar für den sicheren Betrieb braucht (etwa die Zahl der anonymisierten Bestellungen je Tag oder die Länge der Versandwarteschlange), sind keine eigenen Zwecke.
|
||||
|
||||
## 3. Datenarten und Betroffene
|
||||
|
||||
(1) Daten der Käufer, die WEBklar aus der Checkout-Sitzung bei Stripe und aus dem Widget erhält und speichert: Name, E-Mail-Adresse, Lieferadresse und Rechnungsadresse (die Rechnungsadresse nur, soweit die Zahlungsseite von Stripe sie für die gewählte Zahlungsart abfragt); die Bestellung (Bestellnummer, Positionen mit Namen, Mengen und Preisen zum Zeitpunkt der Bestellung, Versandkosten, Lieferart, Steuerausweis, Gesamtbetrag); der Status der Bestellung mit den Zeitpunkten (bestellt, bezahlt, versendet, abholbereit, abgeschlossen, storniert, erstattet); Zahlungsstatus, Zahlungsart (zum Beispiel Karte oder SEPA-Lastschrift) und Kennungen der Zahlung bei Stripe; Versanddienst und Sendungsnummer, wenn der Kunde sie einträgt; Erstattungen mit Betrag, Zeitpunkt und Grund; die Prüfsumme (Hash) der Rechtstexte des Kunden, die der Käufer bei der Bestellung gesehen hat (den Wortlaut der Fassung verwahrt das Archiv nach Nummer 8 Abs. 5); ein Hashwert der IP-Adresse (Absatz 4); der Zustellstatus der E-Mails an den Käufer, auch der Eingangsmail bei ausstehender Zahlung. **Kartendaten und Kontodaten (IBAN) erhält und speichert WEBklar nicht;** die gibt der Käufer nur bei Stripe ein. **Eine Telefonnummer des Käufers erhebt und speichert WEBklar nicht:** Das Widget hat kein Telefonfeld, die Zahlungsseite von Stripe ist so eingestellt, dass sie keine Telefonnummer verlangt, und eine Telefonnummer, die Stripe in den Kundendaten der Sitzung dennoch übermitteln sollte, übernimmt WEBklar nicht in die Bestellung. Die Telefonnummer des Kunden selbst (Anbieterangaben in seinen Einstellungen) gehört nicht zu den Käuferdaten.
|
||||
|
||||
(2) Daten von Personen, die über die Widerrufsfunktion einen Vertrag widerrufen (Käufer und Interessenten, auch ohne passende Bestellung): Name, E-Mail-Adresse, Bestellnummer, die Erklärung als Freitext und der Zeitpunkt des Eingangs, außerdem der Zeitpunkt der Eingangsbestätigung und der Bearbeitung durch den Kunden.
|
||||
|
||||
(3) Besondere Kategorien personenbezogener Daten (Art. 9 DSGVO) werden nicht verarbeitet. Waren, aus deren Kauf sich besondere Kategorien ergeben können, etwa Arzneimittel, sind nach Nr. 3 der Leistungsbeschreibung ausgeschlossen; der Kunde fordert von seinen Käufern keine entsprechenden Angaben (Nummer 11 Abs. 4).
|
||||
|
||||
(4) IP-Adresse und Browser-Kennzeichen (User-Agent) erhält der Server von WEBklar technisch bei jedem Abruf des Widgets und jeder Anfrage. Die Mengenbegrenzung (Missbrauchsschutz) verwendet die IP-Adresse im Arbeitsspeicher des Servers; ein Neustart leert sie. Mit dem Widerruf wird keine IP-Adresse und kein Browser-Kennzeichen gespeichert. Mit der Bestellung werden ebenfalls weder eine IP-Adresse im Klartext noch ein Browser-Kennzeichen gespeichert, sondern nur ein **Hashwert der IP-Adresse („IP-Hash“)**: die ersten 16 Hexadezimalzeichen eines SHA-256-Werts, den WEBklar mit einem geheimen Schlüssel des Servers salzt. Der IP-Hash ist ein pseudonymes technisches Datum und bleibt ein personenbezogenes Datum, weil WEBklar mit dem Schlüssel für dieselbe IP-Adresse denselben Wert berechnen kann; ohne den Schlüssel lässt sich aus ihm keine IP-Adresse ableiten. WEBklar verwendet ihn ausschließlich zum Missbrauchsschutz: Je IP-Hash und Webshop sind höchstens drei offene (noch nicht bezahlte) Bestellungen innerhalb von 30 Minuten zulässig. Er dient nicht dazu, Käufer zu identifizieren, wiederzuerkennen oder zu profilieren, wird dem Kunden weder angezeigt noch exportiert und steht nur im Datensatz der Bestellung. WEBklar löscht ihn aus dem Datensatz der Bestellung, sobald die Bestellung bezahlt oder storniert wird, spätestens mit der Löschung nicht bezahlter Bestellungen nach 30 Tagen (Nummer 9 Abs. 1); bei bezahlten Bestellungen bleibt er nicht bis zur Anonymisierung (Nummer 9 Abs. 2) erhalten. In den Server-Protokollen stehen IP-Adresse und Browser-Kennzeichen 14 Tage (Anlage 2 der Abo-Bedingungen).
|
||||
|
||||
(5) Der Webshop setzt keine Cookies. Das Widget speichert im Browser des Besuchers nur den Warenkorb (Kennungen der Artikel und Mengen, ohne Personenbezug) im Sitzungsspeicher (sessionStorage) des Tabs, solange der Tab geöffnet ist; das ist für den Warenkorb technisch erforderlich. Andere Daten legt das Widget im Browser nicht ab.
|
||||
|
||||
(6) Betroffene: Käufer, die über die Website des Kunden Waren bestellen, und Personen, die über die Widerrufsfunktion einen Vertrag widerrufen. Beschäftigte des Kunden sind von diesem Vertrag nicht betroffen.
|
||||
|
||||
## 4. Weisungen
|
||||
|
||||
(1) WEBklar verarbeitet die Daten nur auf dokumentierte Weisung des Kunden, auch in Bezug auf eine Übermittlung in ein Drittland oder an eine internationale Organisation. Das gilt nicht, wenn das Recht der Europäischen Union oder Deutschlands WEBklar zu einer Verarbeitung verpflichtet. In diesem Fall teilt WEBklar dem Kunden die Pflicht vorher mit, soweit das Gesetz es nicht verbietet.
|
||||
|
||||
(2) Mit der Bestellung weist der Kunde WEBklar an, den Webshop nach seinen Einstellungen und der Leistungsbeschreibung zu betreiben. Weitere Weisungen ergeben sich aus den Einstellungen und Aktionen im Kundenportal (Artikel, Versand, Abholung, Zahlungsarten, Rechtstexte, Bestellstatus, Erstattungen, Öffnen und Schließen des Webshops, Export). Sonstige Weisungen erteilt der Kunde in Textform, etwa im Chat des Kundenportals. Weisungen, die über diese Einstellungen hinausgehen, setzt WEBklar nur um, wenn das technisch vorgesehen ist; sonst kann der Kunde den Webshop abschalten lassen (Nr. 10 Abs. 7 der Leistungsbeschreibung). Die Anonymisierung und Löschung auf Weisung nach Nummer 8 Abs. 2 und Nummer 9 Abs. 6 setzt WEBklar immer um.
|
||||
|
||||
(3) Hält WEBklar eine Weisung für rechtswidrig, weist es den Kunden unverzüglich darauf hin. WEBklar darf die Ausführung aussetzen, bis der Kunde die Weisung ändert oder die Rechtslage geklärt ist.
|
||||
|
||||
## 5. Vertraulichkeit
|
||||
|
||||
WEBklar setzt nur Personen ein, die zur Vertraulichkeit verpflichtet sind oder einer gesetzlichen Verschwiegenheitspflicht unterliegen.
|
||||
|
||||
## 6. Technische und organisatorische Maßnahmen
|
||||
|
||||
(1) WEBklar trifft die Maßnahmen nach Art. 32 DSGVO, die in der Anlage zu diesem Vertrag beschrieben sind. WEBklar darf sie an den Stand der Technik anpassen, sofern das Schutzniveau nicht sinkt.
|
||||
|
||||
(2) Für den Server gelten ergänzend die Maßnahmen der Anlage 2 der Abo-Bedingungen, soweit die Anlage zu diesem Vertrag nichts anderes regelt.
|
||||
|
||||
## 7. Unterauftragsverarbeiter
|
||||
|
||||
(1) Der Kunde genehmigt den Einsatz der Hetzner Online GmbH, Industriestraße 25, 91710 Gunzenhausen, für den Betrieb des Servers, auf dem der Webshop läuft, und für dessen Sicherung (Rechenzentrum Falkenstein, Deutschland) sowie für den Versand der Bestell-E-Mails über den SMTP-Dienst des Webhostings (Rechenzentrum Nürnberg, Deutschland). Eine Übermittlung in Drittländer findet nicht statt.
|
||||
|
||||
(2) Will WEBklar einen weiteren Unterauftragsverarbeiter hinzuziehen oder einen ersetzen, teilt es das dem Kunden mindestens vier Wochen vorher in Textform mit. Der Kunde kann innerhalb von zwei Wochen nach Zugang der Mitteilung aus wichtigem datenschutzrechtlichem Grund in Textform widersprechen. WEBklar setzt den neuen Unterauftragsverarbeiter nicht vor Ablauf dieser Frist ein. Kündigt ein Unterauftragsverarbeiter von WEBklar eine eigene Änderung mit kürzerer Frist an, informiert WEBklar den Kunden unverzüglich, nachdem die Ankündigung eingegangen ist; der Kunde kann dann unverzüglich widersprechen. Einigen sich die Parteien nicht, kann der Kunde den Webshop abschalten lassen und WEBklar ihn für diese Website zu dem Zeitpunkt beenden, zu dem der neue Unterauftragsverarbeiter eingesetzt wird. Die Daten werden dann nach Nummer 9 gelöscht, auf Weisung des Kunden früher (Nummer 9 Abs. 6).
|
||||
|
||||
(3) WEBklar verpflichtet jeden Unterauftragsverarbeiter vertraglich auf die Datenschutzpflichten nach Art. 28 Abs. 4 DSGVO. Bei der Hetzner Online GmbH ist das deren Vertrag zur Auftragsverarbeitung, den WEBklar abgeschlossen hat. Kommt ein Unterauftragsverarbeiter seinen Datenschutzpflichten nicht nach, haftet WEBklar dem Kunden für die Einhaltung dieser Pflichten (Art. 28 Abs. 4 Satz 2 DSGVO).
|
||||
|
||||
(4) **Stripe ist kein Unterauftragsverarbeiter von WEBklar.** Der Kunde verwendet sein eigenes Stripe-Konto und hat den Vertrag darüber unmittelbar mit Stripe geschlossen (Nr. 4 der Leistungsbeschreibung). Die Zahlungsseite (Stripe Checkout), die Erhebung von Zahlungs-, Karten- und Kontodaten, die Prüfung von Zahlungen und die Auszahlungen verantworten der Kunde und Stripe nach ihrem Vertrag; WEBklar erhält diese Daten nicht. Auf Weisung des Kunden ruft WEBklar bei Stripe die Angaben zu Zahlungen und Bestellungen ab, die der Webshop braucht (Nummer 2 Abs. 1), und veranlasst Erstattungen. Daten von Käufern gehen dabei nicht von WEBklar an Stripe, außer der Erstattung selbst und der Kennung der Zahlung. Die Weitergabe von Daten durch Stripe, auch an Stellen außerhalb der Europäischen Union, regeln der Kunde und Stripe; dafür ist WEBklar nicht verantwortlich.
|
||||
|
||||
## 8. Unterstützung des Kunden, Betroffenenrechte
|
||||
|
||||
(1) WEBklar leitet Anfragen von Käufern, die an WEBklar gerichtet sind, unverzüglich an den Kunden weiter. WEBklar unterstützt ihn mit geeigneten technischen und organisatorischen Maßnahmen bei der Beantwortung.
|
||||
|
||||
(2) Dafür bietet das Kundenportal den Export der Bestellungen als CSV-Datei und die Anzeige der Bestellungen und Widerrufe, mit denen der Kunde Auskunft erteilen kann. Auf Weisung des Kunden in Textform (etwa im Chat des Kundenportals) anonymisiert WEBklar die Käuferdaten einer Bestellung oder eines Widerrufs unverzüglich: Name, E-Mail-Adresse, Liefer- und Rechnungsadresse und die Freitexte werden gelöscht oder unkenntlich gemacht (ein IP-Hash ist zu diesem Zeitpunkt längst gelöscht); Bestellnummer, Positionen, Beträge und Status bleiben. Dabei löscht WEBklar die E-Mails zu dieser Bestellung, die noch auf den Versand warten. E-Mails, deren Versand endgültig fehlgeschlagen ist, behalten Empfängeradresse und Inhalt, bis WEBklar sie nach Nummer 9 Abs. 4 löscht (48 Stunden nach dem Fehlschlag). Gesetzliche Aufbewahrungspflichten des Kunden (Nummer 9 Abs. 8) prüft der Kunde vor seiner Weisung selbst.
|
||||
|
||||
(3) Verletzungen des Schutzes personenbezogener Daten meldet WEBklar dem Kunden unverzüglich, möglichst innerhalb von 24 Stunden nach Kenntnis, mit den Informationen, die zu diesem Zeitpunkt vorliegen.
|
||||
|
||||
(4) WEBklar unterstützt den Kunden bei seinen Pflichten aus Art. 32 bis 36 DSGVO. Dazu gehören die Angaben, die er für sein Verzeichnis von Verarbeitungstätigkeiten und für eine etwaige Datenschutz-Folgenabschätzung braucht.
|
||||
|
||||
(5) Bestätigt der Kunde seine Rechtstexte im Kundenportal (Allgemeine Geschäftsbedingungen, Widerrufsbelehrung, Muster-Widerrufsformular, Versand und Zahlung, Gewährleistungshinweis sowie die Adressen von Impressum und Datenschutzerklärung), legt WEBklar jede bestätigte Fassung einmal im Archiv der Rechtstexte ab: mit ihrer Prüfsumme (Hash), dem Zeitpunkt und der Angabe, welche angemeldete Person des Kunden (Name oder E-Mail-Adresse) sie bestätigt hat. Die Bestellung speichert die Prüfsumme der Fassung, die der Käufer bei der Bestellung gesehen hat. Die Bestellbestätigung (G09) zeigt die archivierte Fassung zu dieser Prüfsumme, nicht die Fassung, die beim Versand der E-Mail gerade gilt; fehlt die archivierte Fassung ausnahmsweise, verwendet WEBklar die aktuell bestätigte Fassung. Das Archiv enthält keine Daten von Käufern. WEBklar löscht es mit den übrigen Daten des Webshops (Nummer 9 Abs. 6), nicht früher. Das Archiv hilft dem Kunden, die Fassung nachzuweisen, die ein Käufer erhalten hat; es ersetzt nicht seine eigene Aufbewahrung (Nummer 9 Abs. 8). Die Bestellbestätigung enthält diese Texte; WEBklar löscht ihren Inhalt nach dem Versand aus der Versandwarteschlange (Nummer 9 Abs. 4).
|
||||
|
||||
(6) Die Käufer informiert der Kunde (Art. 13 DSGVO; Nummer 11 Abs. 2).
|
||||
|
||||
## 9. Löschung und Rückgabe
|
||||
|
||||
(1) Eine Bestellung, die nicht bezahlt wird, verfällt 30 Minuten nach dem Anlegen und gilt als storniert. WEBklar löscht nicht bezahlte Bestellungen (offen oder storniert) 30 Tage nach dem Anlegen. Den IP-Hash löscht WEBklar aus dem Datensatz, sobald die Bestellung bezahlt oder storniert wird, spätestens mit dieser Löschung.
|
||||
|
||||
(2) Bei bezahlten Bestellungen anonymisiert WEBklar die Käuferdaten 3 Jahre nach dem Ende des Kalenderjahres, in dem die Bestellung angelegt wurde (Bestelljahr; maßgeblich ist die Zeit in Berlin): Name, E-Mail-Adresse sowie Liefer- und Rechnungsadresse werden gelöscht (der IP-Hash ist mit der Zahlung bereits gelöscht; eine Telefonnummer des Käufers wird nicht gespeichert). Bestellnummer, Positionen, Beträge, Steuerausweis, Status, Versanddienst und Sendungsnummer bleiben für die Buchhaltung des Kunden erhalten.
|
||||
|
||||
(3) Widerrufe nach Nummer 3 Abs. 2 behandelt WEBklar wie bezahlte Bestellungen: Name, E-Mail-Adresse und Erklärung werden 3 Jahre nach dem Ende des Kalenderjahres des Eingangs gelöscht; die Bestellnummer, die die Person angegeben hat, und der Zeitpunkt des Eingangs bleiben.
|
||||
|
||||
(4) In der Versandwarteschlange für E-Mails leert WEBklar die Empfängeradresse und den Inhalt einer E-Mail nach dem Versand und löscht den Eintrag 7 Tage nach dem Versand. E-Mails, die nicht zugestellt werden können, löscht WEBklar mit Empfängeradresse und Inhalt 48 Stunden nach dem endgültigen Fehlschlag.
|
||||
|
||||
(5) Der Löschlauf läuft täglich; gelöscht oder anonymisiert wird frühestens nach Ablauf der jeweiligen Frist. Ist an einem Tag mehr zu bearbeiten, als ein Lauf schafft, folgt der Rest im nächsten Lauf.
|
||||
|
||||
(6) Ist der Webshop abgeschaltet oder der Vertrag über den Betrieb der Website beendet, kann der Kunde die Bestellungen und Widerrufe 90 Tage lang im Kundenportal lesen und als CSV-Datei exportieren. Danach löscht WEBklar alle Daten des Webshops: Artikel, Bilder, Bestellungen, Widerrufe, Einstellungen, Rechtstexte und das Archiv der bestätigten Fassungen der Rechtstexte (Nummer 8 Abs. 5). Eine Ausnahme ist das Einrichtungs-Ticket, in dem WEBklar die Einrichtung als Arbeitsnachweis festhält: Es bleibt mit einer Kurzfassung der Angaben zur Einrichtung bestehen (zum Beispiel Adresse der Website, Einbau-Code und die Angaben des Kunden aus der Anfrage) und enthält keine Daten von Käufern; dafür gilt die Datenschutzerklärung von WEBklar (Abschnitt 6). Beginnt innerhalb dieser Frist wieder ein Vertrag über den Betrieb der Website, schaltet WEBklar den Webshop auf Wunsch des Kunden mit den vorhandenen Daten wieder ein (Nr. 10 Abs. 5 der Leistungsbeschreibung). Früher anonymisiert WEBklar einzelne Daten auf Weisung des Kunden (Nummer 8 Abs. 2); alle Daten des Webshops löscht WEBklar auf Weisung des Kunden in Textform unverzüglich.
|
||||
|
||||
(7) Die Daten sind in der täglichen Vollsicherung des Servers enthalten (§ 3 Abs. 5 der Abo-Bedingungen). Gelöschte oder anonymisierte Daten bleiben dort bis zu sieben Tage erhalten und werden dann überschrieben. Wird eine Sicherung zurückgespielt, holt der tägliche Löschlauf die fälligen Löschungen und Anonymisierungen nach; Löschungen auf Weisung des Kunden wiederholt WEBklar.
|
||||
|
||||
(8) Gesetzliche Aufbewahrungspflichten bleiben unberührt. Der Kunde ist für seine eigenen Aufbewahrungspflichten (zum Beispiel für Buchungsbelege) selbst verantwortlich; WEBklar ist kein Archiv im Sinne der Buchführung, und das Archiv der Rechtstexte dient nur dem Nachweis der Texte. Der Kunde sichert die Belege, die er braucht, rechtzeitig vor der Anonymisierung oder Löschung durch den Export und die Belege von Stripe.
|
||||
|
||||
## 10. Nachweise und Kontrollen
|
||||
|
||||
WEBklar stellt dem Kunden alle Informationen zur Verfügung, die zum Nachweis der Pflichten aus Art. 28 DSGVO nötig sind, und ermöglicht Überprüfungen einschließlich Inspektionen durch den Kunden oder einen von ihm beauftragten, zur Verschwiegenheit verpflichteten Prüfer. Sie finden nach Anmeldung mit angemessener Frist (in der Regel 14 Tage) zu den Servicezeiten statt, ohne den Betrieb unzumutbar zu stören. Für das Rechenzentrum genügen in der Regel die Nachweise des Betreibers (Zertifizierung der Hetzner Online GmbH nach ISO/IEC 27001). Für mehr als eine Vor-Ort-Kontrolle pro Jahr kann WEBklar eine angemessene Vergütung verlangen.
|
||||
|
||||
## 11. Pflichten des Kunden
|
||||
|
||||
(1) Der Kunde ist Verantwortlicher für die Verarbeitung der Daten seiner Käufer im Webshop.
|
||||
|
||||
(2) Der Kunde informiert seine Käufer nach Art. 13 DSGVO. Dazu nimmt er den Datenschutz-Absatz für den Webshop in seine Datenschutzerklärung auf (Nr. 5 Abs. 10 der Leistungsbeschreibung) und nennt in seinen Rechtstexten (Datenschutzerklärung, Impressum) die Angaben, die das Gesetz verlangt.
|
||||
|
||||
(3) Der Kunde schließt mit Stripe den Vertrag über sein Stripe-Konto selbst und verantwortet die Verarbeitung von Daten durch Stripe, soweit sie nicht in diesem Vertrag geregelt ist (Nummer 7 Abs. 4).
|
||||
|
||||
(4) Der Kunde verkauft keine ausgeschlossenen Waren (Nr. 3 der Leistungsbeschreibung) und fordert von seinen Käufern keine Angaben zu ihrer Gesundheit oder zu anderen besonderen Kategorien personenbezogener Daten, auch nicht im Freitext der Widerrufserklärung.
|
||||
|
||||
(5) Der Kunde verändert den Einbau-Code und die gesetzlich vorgeschriebenen Hinweise des Widgets nicht und entfernt den Link „Vertrag widerrufen“ nicht (Nr. 5 Abs. 6 und 9 der Leistungsbeschreibung).
|
||||
|
||||
## 12. Haftung, Änderungen, Schlussbestimmungen
|
||||
|
||||
(1) Gegenüber betroffenen Personen gilt Art. 82 DSGVO. Für die Haftung zwischen Kunde und WEBklar gilt § 13 der Abo-Bedingungen.
|
||||
|
||||
(2) Eine neue Fassung dieses Vertrags oder der Leistungsbeschreibung, auch wegen geänderter Datenerhebung, teilt WEBklar dem Kunden mindestens sechs Wochen vor ihrem Wirksamwerden in Textform mit. Sie wird wirksam, wenn der Kunde ihr im Kundenportal oder in Textform zustimmt. Stimmt er nicht zu, gilt die bisherige Fassung weiter.
|
||||
|
||||
(3) Ist die neue Fassung nötig, weil ein Gesetz, eine Gerichtsentscheidung oder eine Vorgabe einer Aufsichtsbehörde den Betrieb in der bisherigen Form nicht mehr zulässt, und stimmt der Kunde ihr nicht zu, darf WEBklar den Webshop für seine Website nach Nr. 11 Abs. 2 der Leistungsbeschreibung beenden oder nach Nr. 11 Abs. 3 der Leistungsbeschreibung aussetzen. Das Abo bleibt davon unberührt.
|
||||
|
||||
(4) Im Übrigen gilt § 16 der Abo-Bedingungen.
|
||||
|
||||
## Anlage: Technische und organisatorische Maßnahmen des Webshops (Art. 32 DSGVO)
|
||||
|
||||
| Bereich | Maßnahmen |
|
||||
|---|---|
|
||||
| Übertragung | Das Widget, die Seiten der Käufer und das Kundenportal werden nur über TLS (HTTPS) übertragen. Der Einbau-Code verweist auf eine Adresse mit https://. Die Zahlung geschieht auf der Zahlungsseite von Stripe. |
|
||||
| Speicher im Browser | Es werden keine Cookies gesetzt. Das Widget speichert nur den Warenkorb (Kennungen der Artikel und Mengen) im Sitzungsspeicher des Tabs. Es sendet seine Anfragen ohne Cookies und ruft nur die Schnittstelle des Webshops auf, keine Dienste Dritter. |
|
||||
| Zugriffskontrolle | Das Kundenportal ist nur nach Anmeldung erreichbar; jeder Kunde sieht nur die Daten seiner eigenen Projekte. Die öffentliche Schnittstelle des Widgets liefert nur für einen vorhandenen Webshop Daten und ist über den Website-Schlüssel (Site-Key) adressiert; Bestellungen und Widerrufe nimmt sie nur von Seiten des Kunden und des Portals an. Den Stand einer Bestellung ruft der Käufer nur mit einem geheimen Token ab, das nur als Prüfsumme gespeichert wird. Benachrichtigungen von Stripe werden nur nach Prüfung ihrer Signatur verarbeitet; die Schlüssel für Stripe liegen nur auf dem Server. |
|
||||
| Missbrauchsschutz | Ein verstecktes Feld (Honeypot), eine Mindestzeit bis zum Absenden des Formulars mit signiertem Formular-Token und eine Mindestantwortzeit schützen vor automatisierten Bestellungen und Widerrufen. Grenzen je IP-Adresse und je Webshop gelten im Arbeitsspeicher; ein Neustart leert sie. Die Obergrenze offener Bestellungen je IP-Adresse (drei in 30 Minuten) arbeitet mit einem gesalzenen Hashwert der IP-Adresse in der Bestellung (Nummer 3 Abs. 4). |
|
||||
| Datenminimierung | Kartendaten und Kontodaten erhält und speichert WEBklar nicht. Preise berechnet der Server aus den gespeicherten Artikeln, nie aus Angaben des Browsers. Eine Telefonnummer wird weder abgefragt noch gespeichert, die Rechnungsadresse nur, soweit Stripe sie für die Zahlungsart braucht. Mit der Bestellung und mit dem Widerruf werden keine IP-Adresse im Klartext und kein Browser-Kennzeichen gespeichert, bei der Bestellung nur der gesalzene IP-Hash, und der nur, solange die Bestellung offen ist (gelöscht mit Zahlung oder Stornierung). Die Käuferdaten werden nach den Fristen der Nummer 9 anonymisiert. Nach dem Versand einer E-Mail werden Empfängeradresse und Inhalt in der Warteschlange geleert. |
|
||||
| Trennung | Alle Artikel, Bestellungen und Widerrufe tragen die Kennung des Webshops und des Kunden; das Kundenportal und die öffentliche Schnittstelle lesen immer nur die Daten des jeweiligen Webshops. Artikelbilder liegen in einem eigenen Ordner je Webshop. Die Hintergrundläufe (Abgleich offener Bestellungen, Versand der E-Mails, Anonymisierung und Löschung) schreiben jeweils nur in das Dokument, um das es geht. |
|
||||
| Löschung | Täglicher Löschlauf für Bestellungen, Widerrufe und Warteschlange nach den Fristen der Nummer 9; die Daten abgeschalteter Webshops einschließlich des Archivs der bestätigten Rechtstexte 90 Tage nach dem Abschalten; das Einrichtungs-Ticket bleibt als Arbeitsnachweis mit einer Kurzfassung der Angaben zur Einrichtung bestehen und enthält keine Daten von Käufern (Nummer 9 Abs. 6). |
|
||||
| Protokolle | Die Programmprotokolle des Webshops und der Tagesbericht enthalten nur Zählungen und Bestellnummern, keine Namen, E-Mail-Adressen oder Adressen von Käufern. Server-Protokolle des Webservers werden nach 14 Tagen gelöscht (Anlage 2 der Abo-Bedingungen). |
|
||||
| Datensicherung | Tägliche Vollsicherung des Servers durch Hetzner; verfügbar sind die letzten sieben Tagessicherungen (§ 3 Abs. 5 der Abo-Bedingungen). |
|
||||
| Rechenzentrum | Hetzner Online GmbH, Rechenzentren Falkenstein und Nürnberg (Deutschland). Hetzner ist nach ISO/IEC 27001 zertifiziert; der Geltungsbereich umfasst die Hosting-Dienstleistungen und die Rechenzentren. |
|
||||
| Organisation | Verpflichtung aller Beteiligten auf Vertraulichkeit, Ablauf für Datenschutzverletzungen (Nummer 8 Abs. 3), Überprüfung der Maßnahmen mindestens einmal im Jahr. |
|
||||
@@ -90,7 +90,7 @@
|
||||
|
||||
(2) Mit dem Abschalten nimmt der Terminplaner keine neuen Buchungen mehr an, und das Widget zeigt den Ausweichtext. Die Termine bleiben im Kundenportal sichtbar, und der Kunde kann sie als CSV-Datei exportieren. Gäste können über ihren Link absagen. Bereits angeforderte Erinnerungen laufen weiter. Der Kunde informiert seine Gäste.
|
||||
|
||||
(3) WEBklar löscht alle Termindaten und die Einstellungen 30 Tage nach dem Abschalten.
|
||||
(3) WEBklar löscht alle Termindaten und die Einstellungen 30 Tage nach dem Abschalten. Eine Ausnahme ist das Einrichtungs-Ticket, in dem WEBklar die Einrichtung als Arbeitsnachweis festhält: Es bleibt mit einer Kurzfassung der Einstellungen bestehen und enthält keine Daten von Gästen (Nummer 9 Abs. 6 des AVV Terminplaner).
|
||||
|
||||
(4) Beginnt innerhalb dieser 30 Tage wieder ein Wartungs-Abo, schaltet WEBklar den Terminplaner mit den vorhandenen Daten und Einstellungen ohne weitere Kosten wieder ein. Nach der Löschung ist eine neue Einrichtung nötig (neuer Fragebogen, neuer Preis nach Nr. 5).
|
||||
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
# Leistungsbeschreibung Webshop
|
||||
|
||||
**Fassung 2026-10-05** · Zusatzbedingungen zum Vertrag über den Betrieb der Website
|
||||
|
||||
## 1. Geltung
|
||||
|
||||
(1) Diese Leistungsbeschreibung regelt die Funktion „Webshop“. Mit dem Webshop verkauft der Kunde Waren über seine Website („Händler“); Käufer sind die Besucher seiner Website, die im Webshop bestellen („Käufer“). Die Leistungsbeschreibung ist eine Zusatzleistung zum Vertrag über den Betrieb der Website: Sie setzt voraus, dass WEBklar die Website des Händlers aufgrund eines Vertrags betreibt, etwa im Hosting-Abo, im Wartungs-Abo oder aufgrund eines anderen Vertrags über den Betrieb der Website. Sie richtet sich nur an Unternehmer, die in eigenem Namen und für eigene Rechnung verkaufen.
|
||||
|
||||
(2) Sie wird vereinbart, wenn der Händler die Einrichtung des Webshops im Kundenportal bestellt und dabei dieser Leistungsbeschreibung, dem Auftragsverarbeitungsvertrag Webshop („AVV Webshop“) und der Widerrufsbelehrung zustimmt. Wie die Zustimmung abläuft, regelt die Vorbemerkung des AVV Webshop. Bestellen kann der Händler erst, nachdem WEBklar seine Anfrage für den Webshop geprüft und freigegeben hat (Nr. 2 Abs. 1).
|
||||
|
||||
(3) Im Übrigen gelten die Abo-Bedingungen. Bei Widersprüchen geht für den Webshop diese Leistungsbeschreibung vor, in Datenschutzfragen der AVV Webshop.
|
||||
|
||||
(4) Leistungsbeschreibung A Nr. 2 Abs. 3 und Nr. 4 sowie Leistungsbeschreibung B Nr. 3 der Abo-Bedingungen (Formulare mit Server, „Rechtstexte … nicht enthalten“) stehen dem Webshop nicht entgegen. Das Widget des Webshops läuft auf dem Server von WEBklar, nicht auf dem Server der Website, und die Muster-Rechtstexte nach Nr. 6 sind Teil des Webshops. Das gilt auch im Hosting-Abo.
|
||||
|
||||
## 2. Leistung
|
||||
|
||||
(1) Der Händler fragt den Webshop im Kundenportal an und beschreibt, was er verkauft. WEBklar prüft die Anfrage, vor allem nach Nr. 3, und gibt sie frei oder lehnt sie mit einer Begründung ab. Ein Anspruch auf Freigabe besteht nicht. Erst nach der Freigabe kann der Händler die Einrichtung bestellen.
|
||||
|
||||
(2) Die Einrichtung umfasst den Einbau eines Shop-Widgets an einer Stelle der Website des Händlers, die Seite des Webshops mit einem Verweis im Menü und den Link „Vertrag widerrufen“ in der Fußzeile der Seiten der Website, außerdem die Freischaltung durch WEBklar. Für jede Website wird ein Webshop eingerichtet. Zusätzlich steht dem Händler eine eigenständige Seite des Webshops bei WEBklar zur Verfügung (Standalone-Seite), etwa als Vorschau.
|
||||
|
||||
(3) Im Kundenportal verwaltet der Händler:
|
||||
- Artikel mit Beschreibung, Bildern, Preis, Varianten, Bestand und den Pflichtangaben zum Artikel,
|
||||
- Versand (nur nach Deutschland und Österreich) und Abholung mit Versandkosten und Lieferzeiten,
|
||||
- Bestellungen mit Status (zum Beispiel bezahlt, versendet, abholbereit, abgeschlossen), die er als CSV-Datei exportieren kann,
|
||||
- Erstattungen, ganz oder teilweise, über sein Stripe-Konto (Nr. 4),
|
||||
- die Widerrufe, die Käufer über die Widerrufsfunktion erklärt haben,
|
||||
- die Verbindung mit seinem Stripe-Konto, seine Einstellungen und seine Rechtstexte, deren bestätigte Fassungen WEBklar archiviert (Nr. 6 Abs. 5).
|
||||
|
||||
(4) Der Webshop enthält die Widerrufsfunktion nach § 356a BGB: Das Widget zeigt in seiner Fußzeile und die Standalone-Seite zeigt unter einer eigenen Adresse die Schaltfläche „Vertrag widerrufen“. Sie führt zu einem Formular und danach zur Bestätigungsschaltfläche „Widerruf bestätigen“. WEBklar speichert den Eingang, sendet dem Erklärenden sofort eine Eingangsbestätigung mit Inhalt, Datum und Uhrzeit und benachrichtigt den Händler. Die Funktion ist ohne Anmeldung erreichbar, auch wenn zur Erklärung keine Bestellung gefunden wird. Der Händler bearbeitet jeden Widerruf selbst (Nr. 5 Abs. 6).
|
||||
|
||||
(5) Der Webshop versendet im Namen des Händlers E-Mails an die Käufer: Eingang der Bestellung (bei einer Zahlung, die noch aussteht, etwa einer SEPA-Lastschrift, vor dem Vertragsschluss), Bestellbestätigung, Versand, Abholbereitschaft, Eingangsbestätigung eines Widerrufs, Erstattung und Hinweis auf eine fehlgeschlagene Zahlung. Absender ist die Versandadresse von WEBklar mit dem Namen des Shops, als Antwortadresse dient die E-Mail-Adresse des Händlers. Den Händler benachrichtigt der Webshop über neue Bestellungen und eingegangene Widerrufe.
|
||||
|
||||
(6) Der Webshop enthält einen Missbrauchsschutz gegen automatisierte Bestellungen und Widerrufe, darunter eine Obergrenze von drei offenen Bestellungen je IP-Adresse in 30 Minuten. Dafür speichert WEBklar mit der offenen Bestellung nur einen Hashwert der IP-Adresse, nicht die IP-Adresse selbst; er wird gelöscht, sobald die Bestellung bezahlt oder storniert wird (Nummer 3 Abs. 4 des AVV Webshop). Eine Telefonnummer des Käufers wird nicht erhoben und nicht gespeichert. Die Löschfristen für Bestelldaten nennt Nummer 9 des AVV Webshop.
|
||||
|
||||
(7) Nicht enthalten sind der Verkauf an Verbraucher außerhalb von Deutschland und Österreich, Rechnungen (Nr. 5 Abs. 7), die Verwaltung mehrerer Lager, Rabattcodes, Marktplatzfunktionen, die Anbindung an Warenwirtschaft oder Versanddienstleister, Mehrsprachigkeit und die Zahlung per PayPal (Nr. 4 Abs. 5). Zusätzliche Wünsche, etwa ein Sonderdesign, ein zweiter Webshop oder eine größere Zahl von Artikeln als vorgesehen, gehören nicht zur Einrichtung nach Nr. 9. WEBklar führt sie nach Absprache nach Stunden aus (Nr. 9 Abs. 5).
|
||||
|
||||
## 3. Ausgeschlossene Waren und Leistungen
|
||||
|
||||
(1) Über den Webshop dürfen nicht verkauft werden:
|
||||
- alkoholische Getränke,
|
||||
- Tabakwaren, E-Zigaretten und Zubehör,
|
||||
- Arzneimittel und Medizinprodukte,
|
||||
- Waffen, Munition, Feuerwerk und Sprengstoffe,
|
||||
- Waren mit erotischem oder pornografischem Inhalt,
|
||||
- lebende Tiere,
|
||||
- digitale Inhalte und Downloads,
|
||||
- Gutscheine, Geschenkkarten und Guthaben,
|
||||
- Abonnements und andere wiederkehrende Zahlungen,
|
||||
- Waren und Leistungen, die nach der Liste der verbotenen und eingeschränkten Geschäfte von Stripe nicht erlaubt sind, und
|
||||
- Waren, deren Verkauf oder Versand nach deutschem, österreichischem oder europäischem Recht verboten ist.
|
||||
|
||||
(2) Der Händler liefert nur nach Deutschland und Österreich. Eine Lieferung in andere Länder bietet der Webshop nicht an.
|
||||
|
||||
(3) Der Händler bestätigt bei der Anfrage und bei der Bestellung, dass er keine dieser Waren und Leistungen verkauft. WEBklar darf eine Anfrage ablehnen, wenn der Händler Waren nach Absatz 1 verkaufen will oder die Angaben dazu nicht ausreichen. Verkauft der Händler später Waren nach Absatz 1, darf WEBklar den Webshop nach einem Hinweis in Textform abschalten; bei einem Verstoß, der Rechte Dritter oder Käufer gefährdet, darf WEBklar ihn auch ohne vorherigen Hinweis abschalten und informiert den Händler unverzüglich. Für das Abschalten gilt Nr. 10 Abs. 2 und 3. Eine Erstattung des Einrichtungspreises folgt daraus nicht, wenn der Händler den Verstoß zu vertreten hat.
|
||||
|
||||
## 4. Zahlungen über Stripe
|
||||
|
||||
(1) Zahlungen im Webshop laufen über Stripe Connect. Der Händler verbindet dazu sein eigenes Stripe-Konto mit dem Webshop. Den Vertrag über dieses Konto schließt der Händler unmittelbar mit Stripe; WEBklar ist daran nicht beteiligt. Für das Konto, die Prüfung des Händlers durch Stripe (zum Beispiel zur Identität) und die Auszahlungen gelten die Bedingungen von Stripe.
|
||||
|
||||
(2) Das Geld der Käufer fließt direkt an den Händler. WEBklar erhält und verwahrt kein Geld der Käufer, leitet keines weiter und beteiligt sich nicht am Umsatz des Händlers. WEBklar ist kein Zahlungsdienstleister. Die Gebühren von Stripe trägt der Händler; sie fallen nur zwischen dem Händler und Stripe an.
|
||||
|
||||
(3) Der Händler prüft selbst, ob sein Stripe-Konto Zahlungen annehmen und Auszahlungen leisten kann. WEBklar zeigt dem Händler im Kundenportal den Stand, den Stripe meldet, hat aber keinen Einfluss darauf, ob und wann Stripe ein Konto freischaltet, einschränkt oder sperrt, und haftet dafür nicht. Rückbuchungen, Streitfälle mit Käufern und Zahlungsausfälle regeln der Händler und Stripe.
|
||||
|
||||
(4) Der Händler erstattet Zahlungen im Kundenportal oder im Stripe-Dashboard. WEBklar trägt Erstattungen aus dem Stripe-Dashboard in die Bestellung nach. Eine Erstattung führt der Händler rechtzeitig aus (Nr. 5 Abs. 6).
|
||||
|
||||
(5) Der Webshop bietet die Zahlungsarten an, die Stripe im Checkout für das Konto des Händlers bereitstellt und der Händler in seinen Einstellungen wählt, zum Beispiel Karte und SEPA-Lastschrift. PayPal ist über Stripe für diese Art der Anbindung nicht verfügbar und deshalb nicht Teil des Webshops.
|
||||
|
||||
(6) Die Zahlungsseite selbst (Stripe Checkout) betreibt Stripe. Kartendaten und Kontodaten gibt der Käufer nur dort ein; WEBklar erhält sie nicht.
|
||||
|
||||
## 5. Pflichten des Händlers
|
||||
|
||||
(1) Der Händler hat einen laufenden Vertrag mit WEBklar über den Betrieb seiner Website (Hosting-Abo, Wartungs-Abo oder ein anderer Vertrag über den Betrieb der Website), ist Unternehmer und verkauft in eigenem Namen. Er stellt sein eigenes Stripe-Konto bereit und schließt die Anmeldung bei Stripe selbst ab.
|
||||
|
||||
(2) Er verantwortet seine Rechtstexte: Impressum, Datenschutzerklärung, Allgemeine Geschäftsbedingungen, Widerrufsbelehrung, Muster-Widerrufsformular sowie die Angaben zu Versand und Zahlung. Die Mustertexte nach Nr. 6 prüft er, passt sie an seinen Betrieb an und bestätigt sie im Kundenportal, bevor er den Webshop öffnet. Er trägt vor der Bestellung die Adressen von Impressum und Datenschutzerklärung seiner Website ein.
|
||||
|
||||
(3) Er verantwortet seine Artikel, Preise und Produktangaben. Dazu gehören richtige Preise einschließlich Steuerausweis (Umsatzsteuer oder Hinweis auf § 19 UStG), Grundpreise, Versandkosten und Lieferzeiten, die Pflichtangaben für Lebensmittel (zum Beispiel Zutaten, Allergene, Füllmenge, verantwortlicher Lebensmittelunternehmer), die Angaben zur Produktsicherheit (zum Beispiel Hersteller, Warn- und Sicherheitshinweise), Widerrufsausschlüsse bei einzelnen Artikeln und die Rechte an seinen Texten und Bildern.
|
||||
|
||||
(4) Er liefert die Waren, wie bestellt und beschrieben, und trägt alle Pflichten eines Verkäufers gegenüber seinen Käufern, auch Gewährleistung, Kundenservice und Beschwerden.
|
||||
|
||||
(5) Er pflegt Bestand, Bestellstatus und Versandstatus im Kundenportal. Die Folgen falscher Angaben oder Statusmeldungen, die der Händler setzt, trägt er.
|
||||
|
||||
(6) Er bearbeitet Widerrufe und Erstattungen selbst, fristgerecht und nach dem Gesetz. Er entfernt den Link „Vertrag widerrufen“ nicht und verändert die Widerrufsfunktion nicht. Er prüft das Kundenportal regelmäßig auf neue Bestellungen und Widerrufe und lässt sich über die Benachrichtigungen informieren.
|
||||
|
||||
(7) Rechnungen und Belege an die Käufer erstellt der Händler selbst; WEBklar stellt sie nicht aus. Die Quittung von Stripe und die Bestellbestätigung des Webshops ersetzen keine Rechnung nach dem Umsatzsteuergesetz, soweit der Händler eine ausstellen muss.
|
||||
|
||||
(8) Er erfüllt seine Aufbewahrungspflichten selbst, etwa nach der Abgabenordnung und dem Handelsgesetzbuch. WEBklar ist kein Archiv für die Buchführung und kein Buchführungssystem (das Archiv der Rechtstexte nach Nr. 6 Abs. 5 dient nur dem Nachweis der Texte): Personenbezogene Käuferdaten werden nach Nummer 9 des AVV Webshop anonymisiert und nach dem Ende des Webshops gelöscht. Der Händler exportiert seine Bestellungen deshalb rechtzeitig als CSV-Datei und bewahrt sie mit den Belegen von Stripe auf.
|
||||
|
||||
(9) Er hält die Vorgaben nach Nr. 3 ein und verändert den Einbau-Code und die gesetzlich vorgeschriebenen Hinweise des Widgets nicht.
|
||||
|
||||
(10) Liegt die Datenschutzerklärung in der von WEBklar betreuten Website, fügt WEBklar beim Einbau dort den Datenschutz-Absatz für den Webshop ein; das gehört zur Einrichtung. Liegt sie woanders, erhält der Händler den Absatz als Textbaustein und fügt ihn selbst ein. Für den übrigen Inhalt seiner Datenschutzerklärung bleibt er verantwortlich (§ 6 Abs. 3 der Abo-Bedingungen). Fällt WEBklar beim Einfügen ein offensichtlicher Widerspruch auf, weist WEBklar den Händler darauf hin; eine Pflicht zur Prüfung der Datenschutzerklärung entsteht daraus nicht.
|
||||
|
||||
(11) Der Händler informiert seine Käufer nach Art. 13 DSGVO. Die Rollen im Datenschutz regelt der AVV Webshop: Der Händler ist Verantwortlicher, WEBklar Auftragsverarbeiter, Stripe Vertragspartner des Händlers.
|
||||
|
||||
## 6. Muster-Rechtstexte, keine Rechtsberatung
|
||||
|
||||
(1) WEBklar stellt dem Händler Mustertexte bereit: eine Muster-Widerrufsbelehrung, ein Muster-Widerrufsformular, Allgemeine Geschäftsbedingungen und den Text zu Versand und Zahlung. Das Widget zeigt außerdem die Hinweise, die für die Kasse und die Bestellbestätigung vorgesehen sind, darunter den Hinweis auf die gesetzliche Gewährleistung.
|
||||
|
||||
(2) Die Mustertexte sind **Muster ohne Gewähr**. Sie passen zur Standardkonfiguration des Webshops und können auf den Betrieb des Händlers nicht zugeschnitten sein. WEBklar sorgt dafür, dass sie die Technik des Webshops richtig beschreiben, und passt sie an, wenn sich die Technik ändert. Eine rechtliche Beratung im Einzelfall schuldet WEBklar nicht (kein Rechtsrat im Sinne des Rechtsdienstleistungsgesetzes).
|
||||
|
||||
(3) WEBklar empfiehlt, die Rechtstexte vor der Öffnung des Webshops durch eine Rechtsanwältin oder einen Rechtsanwalt prüfen zu lassen, besonders bei besonderen Waren (zum Beispiel Lebensmitteln, Kosmetik, Textilien mit Pflegekennzeichnung, Elektrogeräten).
|
||||
|
||||
(4) Der Händler bleibt für seine Rechtstexte verantwortlich, auch wenn er Mustertexte unverändert übernimmt (Nr. 5 Abs. 2).
|
||||
|
||||
(5) Bestätigt der Händler seine Rechtstexte im Kundenportal, legt WEBklar jede bestätigte Fassung mit Prüfsumme, Zeitpunkt und der bestätigenden Person im Archiv ab. Die Bestellbestätigung zeigt dem Käufer die archivierte Fassung, die zum Zeitpunkt seiner Bestellung galt. WEBklar löscht das Archiv mit den übrigen Daten des Webshops (Nr. 10 Abs. 3; Nummer 8 Abs. 5 des AVV Webshop). Der Händler bewahrt die Fassungen, die er für eigene Aufbewahrungspflichten braucht, selbst auf (Nr. 5 Abs. 8).
|
||||
|
||||
## 7. Verfügbarkeit und Störungen
|
||||
|
||||
(1) Das Verfügbarkeitsziel nach § 5 der Abo-Bedingungen gilt für die Website, nicht für den Webshop. WEBklar sagt für den Webshop keine bestimmte Verfügbarkeit zu, die über den Betrieb der Website im Abo hinausgeht. Störungen des Webshops beheben wir in angemessener Frist (§ 12 der Abo-Bedingungen).
|
||||
|
||||
(2) Angekündigte Wartungsfenster (§ 5 der Abo-Bedingungen) können den Webshop kurzzeitig unterbrechen. Während einer Unterbrechung sind Bestellungen im Widget nicht möglich; das Widget zeigt dann einen Ausweichtext.
|
||||
|
||||
(3) Zahlungen übernimmt der Webshop über Benachrichtigungen von Stripe und einen regelmäßigen Abgleich in die Bestellung. Fällt die Verbindung zu Stripe oder zum Server aus, kann sich die Bestätigung einer Bestellung verzögern. Ausfälle von Stripe, von Zahlungsarten und von Banken liegen nicht im Einflussbereich von WEBklar.
|
||||
|
||||
(4) Alle Zeiten gelten in der Zeitzone Europe/Berlin.
|
||||
|
||||
(5) WEBklar übernimmt keine Gewähr dafür, dass E-Mails zugestellt und gelesen werden. Das hängt vom Postfach des Empfängers und von den Mailgrenzen seines Anbieters ab.
|
||||
|
||||
(6) Die gesetzlichen Rechte des Händlers sowie §§ 12 und 13 der Abo-Bedingungen bleiben unberührt.
|
||||
|
||||
## 8. Haftung
|
||||
|
||||
(1) Der Webshop ist ein Hilfsmittel für den Verkauf des Händlers. Der Vertrag über die Ware besteht allein zwischen dem Händler und dem Käufer; WEBklar ist daran nicht beteiligt.
|
||||
|
||||
(2) Für entgangenen Gewinn und Folgeschäden aus Störungen des Webshops, aus Fehlern bei Bestand oder Bestellstatus und aus nicht zugestellten E-Mails haftet WEBklar nur im Rahmen von § 13 Abs. 1 und 2 der Abo-Bedingungen.
|
||||
|
||||
(3) WEBklar haftet nicht für Inhalte des Händlers, insbesondere nicht für seine Artikel, Preise, Produktangaben und Rechtstexte, auch nicht für Mustertexte, die er übernommen hat (Nr. 6), und nicht für die Erfüllung seiner Pflichten gegenüber den Käufern (Nr. 5).
|
||||
|
||||
(4) WEBklar haftet nicht für Ausfälle, Sperren oder Entscheidungen von Stripe und der Zahlungsarten sowie für Rückbuchungen und Zahlungsausfälle (Nr. 4 Abs. 3).
|
||||
|
||||
(5) Die Haftung nach § 13 Abs. 1 der Abo-Bedingungen (unter anderem bei Vorsatz, grober Fahrlässigkeit, Verletzung von Leben, Körper oder Gesundheit und nach dem Produkthaftungsgesetz) bleibt unberührt. Der Haftungsrahmen des Händlers gegenüber seinen Käufern bleibt unberührt.
|
||||
|
||||
## 9. Preis, Abrechnung und Widerruf
|
||||
|
||||
(1) Die Einrichtung kostet 299 € einmalig je Website-Projekt, ohne Umsatzsteuer gemäß § 19 UStG. Für den Betrieb des Webshops berechnet WEBklar nichts weiter; es gibt keine laufende Gebühr und keine Beteiligung am Umsatz. Der Webshop läuft, solange WEBklar die Website betreibt (Nr. 10).
|
||||
|
||||
(2) Die Einrichtung dauert nach der Erfahrung von WEBklar meist 2 bis 3 Wochen ab der Bestellung. Sie hängt davon ab, wie schnell der Händler Stripe-Konto, Artikel und Rechtstexte bereitstellt und wie umfangreich der Einbau ist. Die Dauer ist eine Einschätzung und keine verbindliche Zusage eines Termins.
|
||||
|
||||
(3) Der Händler bezahlt bei der Bestellung im Kundenportal über Stripe und erhält eine Rechnung.
|
||||
|
||||
(4) Der Preis steht neben den Leistungen eines Abos: Die Einrichtung wird weder auf ein Stundenkontingent angerechnet noch damit bezahlt.
|
||||
|
||||
(5) Mehraufwand, der nicht zur Einrichtung gehört (Nr. 2 Abs. 7), führt WEBklar nur nach Absprache vor der Ausführung und nach Freigabe des Händlers in Textform aus. WEBklar nennt vorher den geschätzten Aufwand und berechnet 100 € je Stunde in Schritten von 15 Minuten, ohne Umsatzsteuer gemäß § 19 UStG (§ 4 der Abo-Bedingungen). Der Mehraufwand wird gesondert berechnet und nicht auf ein Stundenkontingent angerechnet.
|
||||
|
||||
(6) Verbraucher können die Bestellung der Einrichtung nach der Widerrufsbelehrung widerrufen. Belehrung und Muster-Widerrufsformular stehen in der Anlage „Widerrufsbelehrung und Muster-Widerrufsformular (Einrichtung von Funktionen)“. Ob das Widerrufsrecht erlischt und ob der Händler einen angemessenen Betrag für die bis dahin erbrachte Leistung schuldet, richtet sich nach der Zustimmung, die er bei der Bestellung gibt, und nach den Hinweisen zum Widerruf in der Anlage. Mit dem Widerruf endet der Webshop.
|
||||
|
||||
## 10. Laufzeit und Ende
|
||||
|
||||
(1) Der Webshop läuft, solange WEBklar die Website des Händlers betreibt. Endet der Vertrag über den Betrieb der Website, endet auch der Webshop; WEBklar schaltet ihn zum Vertragsende ab und informiert den Händler vorher per E-Mail. Das gilt auch beim Widerruf dieses Vertrags.
|
||||
|
||||
(2) Mit dem Abschalten nimmt der Webshop keine neuen Bestellungen mehr an, und das Widget zeigt den Ausweichtext. Bestellungen, die noch nicht bezahlt sind, laufen aus. Die Bestellungen und Widerrufe bleiben im Kundenportal 90 Tage lesbar, und der Händler kann sie als CSV-Datei exportieren. Die Widerrufsfunktion bleibt bis zur Löschung nach Absatz 3 erreichbar, soweit das Widget noch eingebunden ist. Der Händler erfüllt seine Pflichten gegenüber den Käufern weiter (Nr. 5) und informiert sie, wenn er den Webshop nicht mehr anbietet.
|
||||
|
||||
(3) WEBklar löscht alle Daten des Webshops, also Artikel, Bilder, Bestellungen, Widerrufe, Einstellungen, Rechtstexte und das Archiv der bestätigten Fassungen, 90 Tage nach dem Abschalten. Eine Ausnahme ist das Einrichtungs-Ticket, in dem WEBklar die Einrichtung als Arbeitsnachweis festhält: Es bleibt mit einer Kurzfassung der Angaben zur Einrichtung bestehen und enthält keine Daten von Käufern (Nummer 9 Abs. 6 des AVV Webshop).
|
||||
|
||||
(4) Das Stripe-Konto bleibt beim Händler. Das Ende des Webshops berührt weder das Konto noch die Auszahlungen noch den Vertrag mit Stripe. Der Händler kann die Verbindung zwischen dem Konto und dem Webshop im Stripe-Dashboard beenden.
|
||||
|
||||
(5) Beginnt innerhalb der 90 Tage wieder ein Vertrag über den Betrieb der Website, schaltet WEBklar den Webshop auf Wunsch des Händlers in Textform mit den vorhandenen Daten und Einstellungen ohne weitere Kosten wieder ein. Nach der Löschung ist eine neue Anfrage und eine neue Einrichtung nötig (neuer Preis nach Nr. 9).
|
||||
|
||||
(6) Endet der Vertrag über den Betrieb der Website, bevor WEBklar den Webshop freigeschaltet hat, erstattet WEBklar den Einrichtungspreis vollständig.
|
||||
|
||||
(7) Auf Wunsch des Händlers in Textform schaltet WEBklar den Webshop jederzeit ab. Eine Erstattung des Einrichtungspreises folgt daraus nicht; für den Widerruf gilt Nr. 9 Abs. 6.
|
||||
|
||||
## 11. Änderungen und Einstellung
|
||||
|
||||
(1) WEBklar darf die Technik des Webshops verbessern, solange der Leistungsumfang nicht schlechter wird (§ 3 Abs. 7 der Abo-Bedingungen). Ändert sich dabei, welche Daten erhoben werden, gilt Nummer 12 des AVV Webshop.
|
||||
|
||||
(2) WEBklar darf den Webshop für eine Website mit einer Frist von vier Wochen beenden oder für alle Händler mit einer Frist von drei Monaten einstellen, wenn
|
||||
- ein Gesetz, eine Gerichtsentscheidung oder eine Vorgabe einer Aufsichtsbehörde den Betrieb in der bisherigen Form unzulässig macht oder nur mit wesentlich anderer Technik erlaubt und der Händler einer dafür nötigen neuen Fassung nicht zustimmt (AVV Webshop Nummer 12),
|
||||
- Stripe die Anbindung nach Nr. 4 beendet oder wesentlich ändert, so dass der Webshop in der bisherigen Form nicht weiterbetrieben werden kann, oder
|
||||
- eine Sicherheitslücke sich nicht anders beheben lässt.
|
||||
|
||||
Die Mitteilung erfolgt in Textform und nennt den Grund, die Merkmale der Änderung, den Zeitpunkt und bei Verbrauchern das Recht, den Vertrag nach § 327r Abs. 3 BGB zu beenden, mit seiner Frist.
|
||||
|
||||
(3) Ist der Weiterbetrieb aus einem Grund nach Absatz 2 schon vor Ablauf der Frist nicht zulässig, darf WEBklar den Webshop ohne Vorankündigung aussetzen, solange das nötig ist. WEBklar informiert den Händler darüber unverzüglich in Textform mit dem Grund und nimmt den Betrieb wieder auf, sobald das zulässig und technisch möglich ist.
|
||||
|
||||
(4) Eine Einstellung ohne einen Grund nach Absatz 2 ist ausgeschlossen. Gesetzliche Rechte von Verbrauchern bei Änderungen digitaler Produkte (§ 327r BGB) bleiben unberührt.
|
||||
|
||||
## 12. Hinweise
|
||||
|
||||
(1) **Keine Rechtsberatung.** Der Webshop und die Mustertexte sind technische Hilfsmittel, keine Rechtsberatung (Nr. 6). Die rechtliche Einordnung seiner Waren, seiner Preisangaben und seines Angebots trägt der Händler.
|
||||
|
||||
(2) **Barrierefreiheit.** Das Widget ist für die Bedienung mit Tastatur und Hilfstechniken gestaltet (Beschriftungen, Fokusführung, Fehlermeldungen als Live-Region). Eine Konformität nach WCAG 2.1 AA oder EN 301 549 sagt WEBklar nicht zu. Ob der Händler als Anbieter von Dienstleistungen im elektronischen Geschäftsverkehr unter das BFSG fällt (Ausnahme für Kleinstunternehmen, § 3 Abs. 3 BFSG), entscheidet und prüft der Händler.
|
||||
@@ -11,5 +11,5 @@ Fassung 2026-10-05 (Stand der Datenschutzerklärung 05.10.2026). Markdown-Quelle
|
||||
<p>Der Kunde sieht, ändert und löscht die Termine im Kundenportal. Dort verarbeiten wir als Verantwortliche seine Einstellungen für den Terminplaner (Betriebsangaben, Leistungen, Namen und Zeiten der gebuchten Personen oder Plätze, Öffnungszeiten, Absagefristen und Texte), wie die übrigen Portal-Daten (Abschnitt 6) auf Grundlage von Art. 6 Abs. 1 lit. b DSGVO. Bevor der Kunde den Terminplaner bestellt, füllt er im Kundenportal einen Fragebogen aus (Branche und Betrieb, Personen oder Plätze, Leistungen, Zeiten, Regeln, Angaben von Gästen, Benachrichtigungen und Website); seine Eingaben speichern wir als Entwurf seiner Einstellungen. Bestellt er nicht, löschen wir den Entwurf 90 Tage nach der letzten Änderung, bei der Löschung seines Kundenkontos zugleich mit dem Konto. Die Bestellung und ihren Nachweis beschreibt Abschnitt 7. Für jede Person oder jeden Platz kann der Kunde einen geheimen Link erzeugen, mit dem er die bestätigten Termine in seinem Kalenderprogramm abonniert; wer den Link kennt, sieht diese Termine. Der Kunde kann den Link jederzeit durch einen neuen ersetzen.</p>
|
||||
<p>Im Namen des Kunden senden wir E-Mails an Gäste (Bestätigung der E-Mail-Adresse, Bestätigung, Änderung oder Absage eines Termins, Eingangsbestätigung eines Widerrufs und, wenn der Gast das gewählt hat, eine Erinnerung) und an den Kunden (neue Buchung, Absage, Widerruf). Der Versand läuft über den E-Mail-Dienst der Hetzner Online GmbH (Rechenzentrum Nürnberg, Abschnitt 8); als Absender erscheint der Kunde, Antworten gehen an ihn. Wir verschicken keine Werbung, setzen keine Tracking-Pixel ein und erhalten keine Kopie. Bis zum Versand liegt jede E-Mail in einer Warteschlange. Nach dem Versand löschen wir dort Empfängeradresse und Inhalt, den Eintrag selbst nach sieben Tagen; E-Mails, die nicht zugestellt werden konnten, löschen wir nach 48 Stunden.</p>
|
||||
<p>Beim Aufruf einer Kundenwebsite mit Terminbuchung lädt der Browser das Programm der Terminbuchung von unserem Server (project.webklar.com) und schon beim Laden der Seite die Konfiguration mit den Texten und Leistungen. Ist die Terminbuchung unmittelbar in die Seite eingebaut, holt sie die freien Termine, sobald der Gast durchklickt; sitzt sie hinter einem Knopf, geschieht das erst nach dem Klick auf den Knopf. Dabei gelten die Protokolle in Abschnitt 3. Die Terminbuchung setzt keine Cookies und speichert nichts im Browser des Gastes. Zum Schutz vor Missbrauch und um die Zahl der Anfragen zu begrenzen (höchstens zehn Buchungen je zehn Minuten und je IP-Adresse), merken wir uns die Zeitpunkte der Aufrufe je IP-Adresse nur im Arbeitsspeicher des Servers, nicht dauerhaft; mit der Buchung speichern wir die IP-Adresse nicht. Rechtsgrundlage ist Art. 6 Abs. 1 lit. f DSGVO; unser berechtigtes Interesse ist der sichere und störungsfreie Betrieb.</p>
|
||||
<p>Löschfristen: Nicht bestätigte Buchungen löschen wir 48 Stunden nach Ablauf der Bestätigungsfrist von 30 Minuten, abgesagte oder abgelehnte Termine nach 30 Tagen, bestätigte Termine nach der Frist, die der Kunde eingestellt hat (Vorgabe 90 Tage, einstellbar von 14 bis 365 Tagen nach dem Termin). Nicht bestellte Entwürfe löschen wir 90 Tage nach der letzten Änderung. Endet das Wartungs-Abo oder schalten wir die Funktion ab, schalten wir den Terminplaner spätestens am folgenden Tag ab und löschen alle Termindaten und Einstellungen 30 Tage danach; beginnt innerhalb dieser Frist wieder ein Wartungs-Abo, schalten wir ihn mit den vorhandenen Daten wieder ein. In unseren Datensicherungen bleiben gelöschte Daten noch bis zu sieben Tage erhalten und werden dann überschrieben. Die Daten liegen auf unserem Server bei Hetzner im Rechenzentrum Falkenstein (Deutschland), wie die übrigen Portal-Daten (Abschnitt 3).</p>
|
||||
<p>Löschfristen: Nicht bestätigte Buchungen löschen wir 48 Stunden nach Ablauf der Bestätigungsfrist von 30 Minuten, abgesagte oder abgelehnte Termine nach 30 Tagen, bestätigte Termine nach der Frist, die der Kunde eingestellt hat (Vorgabe 90 Tage, einstellbar von 14 bis 365 Tagen nach dem Termin). Nicht bestellte Entwürfe löschen wir 90 Tage nach der letzten Änderung. Endet das Wartungs-Abo oder schalten wir die Funktion ab, schalten wir den Terminplaner spätestens am folgenden Tag ab und löschen alle Termindaten und Einstellungen 30 Tage danach; beginnt innerhalb dieser Frist wieder ein Wartungs-Abo, schalten wir ihn mit den vorhandenen Daten wieder ein. Eine Ausnahme ist das Einrichtungs-Ticket, in dem wir die Einrichtung als Arbeitsnachweis festhalten (Abschnitt 6): Es bleibt mit einer Kurzfassung der Einstellungen bestehen und enthält keine Daten von Gästen. In unseren Datensicherungen bleiben gelöschte Daten noch bis zu sieben Tage erhalten und werden dann überschrieben. Die Daten liegen auf unserem Server bei Hetzner im Rechenzentrum Falkenstein (Deutschland), wie die übrigen Portal-Daten (Abschnitt 3).</p>
|
||||
```
|
||||
@@ -29,7 +29,7 @@ Wortlaut immer in der Sie-Form, auch bei Betrieben, die ihre Gäste duzen: Es si
|
||||
| Haken | Wann | Pflicht | Wortlaut |
|
||||
|---|---|---|---|
|
||||
| Vorzeitiger Beginn | Verbraucher, mit Preis: bei sofort buchbaren Leistungen, wenn der Terminbeginn nicht nach dem Ende der Widerrufsfrist liegt (14 Tage ab dem Tag nach dem Vertragsschluss, Berliner Kalender, §§ 187, 188, 193 BGB; gerechnet mit dem Zeitpunkt der Buchung plus Haltezeit; R29); bei bepreisten Anfragen immer, weil der Tag der Annahme nicht feststeht (R31) | ja | Ich verlange ausdrücklich, dass {{Firma}} mit der Leistung vor Ablauf der Widerrufsfrist beginnt. Mir ist bekannt, dass mein Widerrufsrecht mit der vollständigen Erbringung der Leistung erlischt (§ 356 Abs. 5 Nr. 2 BGB). Widerrufe ich vor der vollständigen Erbringung, schulde ich einen angemessenen Betrag für die bis dahin erbrachte Leistung (§ 357a Abs. 2 BGB). |
|
||||
| Stornobedingungen | der Betrieb hat Stornobedingungen hinterlegt | ja | Ich habe die Stornobedingungen gelesen. |
|
||||
| Stornobedingungen | der Betrieb hat Stornobedingungen hinterlegt und die gebuchte Leistung hat einen Preis (bei kostenlosen Leistungen fragt der Terminplaner nicht danach) | ja | Ich habe die Stornobedingungen gelesen. |
|
||||
| Erinnerung | der Betrieb bietet Erinnerungen an | nein | Ich möchte {{n}} Stunden vor dem Termin eine Erinnerung per E-Mail erhalten. Diese Einwilligung kann ich jederzeit widerrufen. |
|
||||
|
||||
## 3. Hinweis über dem Nachrichtenfeld
|
||||
|
||||
@@ -0,0 +1,966 @@
|
||||
# Kanzlei-Paket Terminplaner (WEBklar)
|
||||
|
||||
Dieses Paket gehört zum Verkaufsweg des Terminplaners der WEBklar GbR (Am Schwimmbad 10, 67722 Winnweiler, vertreten durch die Gesellschafter Kenso Grimm und Justin Klein). Es zeigt einer Kanzlei, was verkauft wird, wie der Ablauf ist, welche Texte Kunden und Gäste in Endfassung sehen, wo diese Texte im Programm stehen und was wir wissen möchten.
|
||||
|
||||
Fassungen: Leistungsbeschreibung, Auftragsverarbeitungsvertrag (AVV), Widerrufsbelehrung und Bestelltexte tragen die Fassung **2026-10-05** (Konstante `config.termine.avvVersion`). Die Gästetexte (Hinweise, Haken, Belehrung und Datenschutz-Absatz für die Gäste unserer Kunden) tragen die Fassung **2026-10-05** (Konstante `RECHTSTEXTE_VERSION`). Beide Konstanten haben heute dasselbe Datum, sind aber getrennt und können sich später unterscheiden.
|
||||
|
||||
## 1. Zweck und Umfang
|
||||
|
||||
**Zweck.** Der Inhaber lässt die Texte des Terminplaners durch eine Kanzlei prüfen, bevor der erste fremde Betrieb den Terminplaner nutzt. Bis dahin ist der Terminplaner nicht für fremde Betriebe freigeschaltet. Das Paket soll ohne Rückfragen zeigen, was die Kanzlei prüfen soll und von welchen Annahmen wir ausgehen.
|
||||
|
||||
**Kernpaket.** Der Inhaber hat festgelegt, dass die Kanzlei dieses Kernpaket prüft und alles Übrige WEBklar selbst prüft (Hausrunde). Die Kanzlei prüft:
|
||||
|
||||
- die Widerrufsbelehrung und das Muster-Widerrufsformular der Bestellung der Einrichtung für 149 € (Abschnitt 3.2),
|
||||
- den Zustimmungssatz zum vorzeitigen Beginn mit Erlöschen des Widerrufsrechts und Wertersatz (Abschnitt 3.1, Haken 3),
|
||||
- den Auftragsverarbeitungsvertrag Terminplaner (Anlage A, Auszüge in Abschnitt 3.5),
|
||||
- die Haftungs- und Ende-mit-dem-Abo-Klauseln der Leistungsbeschreibung (Anlage B, Auszüge in Abschnitt 3.4),
|
||||
- die Gästetexte mit Gesetzlichkeitsfiktion: Widerrufsbelehrung für Gäste und die Hinweise und Haken im Widget (Abschnitte 3.6 bis 3.10).
|
||||
|
||||
**Hausrunde.** Die Datenschutzerklärung von WEBklar (Abschnitt 6c und die Ergänzungen in den Abschnitten 6 und 7), die Hinweise im Kundenportal und die Mailtexte prüft WEBklar selbst. Zu einzelnen Punkten daraus stellen wir trotzdem Fragen (Q15 bis Q19 und Q24); was wir nicht fragen, steht in Abschnitt 6.
|
||||
|
||||
**Begriffe.** *Betriebsart* (im Programm „Modus“) des Terminplaners: `off` (abgeschaltet), `pilot` (nur Kunden auf einer Liste von Pilotadressen) oder `on` (alle Kunden mit Wartungs-Abo). *Pilot* ist ein Kunde auf dieser Liste; derzeit sind das nur Konten des Inhabers. `on` setzt der Inhaber erst nach Ihrem Befund und seiner Freigabe. *Kulanz* heißt, dass das Team einem Kunden den Terminplaner ohne Bestellung und ohne Zahlung freischaltet (Abschnitt 2.1). *Fassung* ist ein Stand der Texte mit Datum.
|
||||
|
||||
**Keine Rechtsberatung durch WEBklar.** Die Texte sind Mustertexte für die Standardkonfiguration und keine Rechtsberatung (Leistungsbeschreibung Nr. 11 Abs. 1). Die rechtlichen Aussagen in diesem Paket (vor allem „unsere Annahme“ bei den Fragen) sind ein Arbeitsstand von WEBklar und kein Rechtsurteil.
|
||||
|
||||
**Fassungen und Änderungen.** Eine ausgelieferte Fassung wird nie überschrieben. Ändern wir nach dem Befund einen Text, bekommt er ein neues Fassungsdatum, neue Dateien (HTML und PDF) und ein neues Archiv der Gästetexte; die Zitate in diesem Paket werden dann angepasst. Ein Test prüft jedes Zitat wörtlich gegen den Quelltext, damit Paket und Programm nicht auseinanderlaufen.
|
||||
|
||||
**Aufbau.** Abschnitt 2 beschreibt den Produktablauf, Abschnitt 3 enthält die Texte, Abschnitt 4 unsere Fragen Q1 bis Q26, Abschnitt 5 die Fundstellen im Programm, Abschnitt 6 was nicht Teil der Prüfung ist, Abschnitt 7 die Checkliste für den Inhaber. Hinter jedem Zitat in Abschnitt 3 steht „⚖ Q…“ mit den Fragen, die den Text betreffen. Anlagen: **Anlage A** ist der AVV Terminplaner, **Anlage B** die Leistungsbeschreibung Terminplaner, beide in voller Länge als PDF (Dateien in Abschnitt 3.11). **Anlage C** sind die gerenderten Mails K15 (Bestellbestätigung mit den Zustimmungen) und K16 (Terminplaner bereit); sie folgt nach der Mail-Überarbeitung des Inhabers, weil ihr Wortlaut bis dahin noch nicht endgültig ist.
|
||||
|
||||
## 2. Produktablauf in Kurzform
|
||||
|
||||
Der Terminplaner ist ein Buchungs-Widget für die Website eines Betriebs (zum Beispiel Friseur, Werkstatt oder Studio) mit einer Verwaltung im Kundenportal von WEBklar. Der Betrieb ist Kunde von WEBklar („Kunde“); seine Besucher, die einen Termin buchen, sind „Gäste“. Verkauft wird die Einrichtung für 149 € einmalig je Website-Projekt, ohne Umsatzsteuer gemäß § 19 UStG. Für den Betrieb berechnet WEBklar neben dem Wartungs-Abo nichts; der Terminplaner läuft, solange das Wartungs-Abo läuft. Bezahlt wird beim Terminplaner nur vor Ort, es gibt keine Online-Zahlung der Gäste.
|
||||
|
||||
### 2.1 Bestellung und Einrichtung
|
||||
|
||||
| Schritt | Was geschieht | Frage |
|
||||
|---|---|---|
|
||||
| 1. Abo-Prüfung | Nur Kunden mit laufendem Wartungs-Abo können bestellen; ein Hosting-Abo genügt nicht. Bestellbar ist der Terminplaner nur, solange das Wartungs-Abo aktiv ist und keine Kündigung vorgemerkt ist. | ⚖ Q25 |
|
||||
| 2. Fragebogen-Entwurf | Der Kunde beantwortet im Kundenportal einen Fragebogen in acht Schritten. Jeder Schritt wird sofort als Entwurf der späteren Einstellungen gespeichert; ein Entwurf ohne Bestellung wird 90 Tage nach der letzten Änderung gelöscht. Die Branchenfrage kennt den Gesundheitsbereich; wählt der Kunde ihn, ist die Bestellung gesperrt. Zusätzlich warnt eine Stichwortprüfung über Betriebsname, Leistungen und Felder das Team, das die Einrichtung ablehnen kann. | ⚖ Q11, Q24 |
|
||||
| 3. Bestellung mit vier Haken | „Prüfen und bestellen“: Zusammenfassung, offene Punkte (der Server prüft, ob alles Nötige vorliegt, darunter die Adressen von Impressum und Datenschutzerklärung der Website), volle Widerrufsbelehrung mit Muster-Widerrufsformular, vier Haken und der Knopf „Zahlungspflichtig bestellen · 149 €“ (Abschnitte 3.1 und 3.2). Alle Besteller werden wie Verbraucher behandelt. | ⚖ Q1, Q3, Q19, Q20 |
|
||||
| 4. Nachweis | Der Server legt zuerst die Zahlungssitzung bei Stripe an und speichert dann den Nachweis: die gezeigten Texte, ihre Fassungen, einen Hash, den Zeitpunkt des Klicks, IP-Adresse und Browser-Kennzeichen, Kundenkonto und angemeldete Person. Erst danach erhält der Browser die Adresse der Zahlungsseite. Der Zeitpunkt des Klicks ist der Anker der Widerrufsfrist. | ⚖ Q2, Q23 |
|
||||
| 5. Zahlung | Stripe Checkout (Karte oder SEPA-Lastschrift); Stripe erstellt die Rechnung. | ⚖ Q19 |
|
||||
| 6. Ticket und Bestätigung | Nach dem Zahlungseingang legt das Portal ein Einrichtungs-Ticket mit der Einbauanleitung an. Der Kunde erhält die Bestellbestätigung K15 mit den gezeigten Zustimmungen und, als PDF-Anhang auf einem dauerhaften Datenträger, Leistungsbeschreibung, AVV und Widerrufsbelehrung mit Muster-Widerrufsformular in der Fassung, der er zugestimmt hat. Das Team erhält die Meldung T16. | ⚖ Q3, Q20 |
|
||||
| 7. Einbau und Freischaltung | WEBklar baut das Widget ein, der Kunde nimmt es ab, WEBklar schaltet es frei; der Kunde erhält die Mail K16 („bereit“). | |
|
||||
| 8. Betrieb | Gäste buchen im Widget (Abschnitt 2.2), der Kunde verwaltet seine Termine im Kundenportal. | |
|
||||
|
||||
**Freischaltung ohne Bestellung (Pilot, Kulanz).** Das Team kann einen Terminplaner ohne Zahlung aktivieren. Sobald die Betriebsart `on` gilt (alle Kunden mit Wartungs-Abo), verlangt das Portal dafür einen Nachweis der Zustimmung des Kunden in Textform (Datum, Weg Chat oder E-Mail, Name des Mitarbeiters), außer für Pilotadressen; danach erhält der Kunde die Mail K16 mit Leistungsbeschreibung und AVV als PDF. Solche Dokumente gelten nicht als durch Kauf freigeschaltet; der tägliche Abo-Abgleich nimmt sie aus (⚖ Q7, Q21).
|
||||
|
||||
### 2.2 Gast: Buchung, Widerruf, Daten
|
||||
|
||||
1. Der Gast wählt Leistung, Termin und gegebenenfalls Person oder Platz und gibt Name und E-Mail-Adresse an (je nach Einstellung des Kunden auch Telefon, Nachricht, Zusatzfelder). Er sieht den Datenschutz-Hinweis (Abschnitt 3.8), die Haken (Abschnitt 3.7) und, bei Verbrauchern, die Widerrufsbelehrung zum Aufklappen (Abschnitt 3.6). ⚖ Q12, Q13, Q17
|
||||
2. Die Buchung wird 30 Minuten gehalten. In dieser Zeit bestätigt der Gast per Link in einer E-Mail; erst die Bestätigung macht die Buchung verbindlich, bei einer Terminanfrage nimmt der Kunde später an. Nicht bestätigte Buchungen löscht WEBklar 48 Stunden nach Ablauf der Bestätigungsfrist. ⚖ Q14
|
||||
3. Die Bestätigungs-E-Mail (G02) enthält bei Verbrauchern Widerrufshinweis, Widerrufsbelehrung und Muster-Widerrufsformular; bei einer Freizeit-Dienstleistung mit festem Termin, die der Kunde so einstuft, steht stattdessen der Hinweis, dass kein Widerrufsrecht besteht (Abschnitt 3.9). ⚖ Q15, Q16
|
||||
4. Widerrufen kann der Gast über den Knopf „Vertrag widerrufen“ im Terminbereich der Website oder über den Link „Termin verwalten“ in der Bestätigungs-E-Mail; er erhält eine Eingangsbestätigung mit Datum und Uhrzeit (G08). Die Widerrufsfrist beträgt 14 Tage.
|
||||
5. Der Terminplaner setzt keine Cookies und speichert nichts im Browser des Gastes; die IP-Adresse dient dem Missbrauchsschutz nur im Arbeitsspeicher des Servers. Die Buchung speichert als Nachweis die Fassung der Gästetexte, den Zeitpunkt, den Text des Buchungsknopfs, die Anrede und Hashes der gezeigten Texte; WEBklar archiviert jede Fassung der Gästetexte. ⚖ Q26
|
||||
6. Löschfristen: bestätigte und erledigte Termine 90 Tage nach dem Terminende (einstellbar 14 bis 365 Tage), abgesagte oder abgelehnte Termine 30 Tage nach der Absage, nicht bestätigte Buchungen 48 Stunden nach Ablauf der Bestätigungsfrist; in der Versandwarteschlange für E-Mails werden Empfängeradresse und Inhalt nach dem Versand geleert und der Eintrag nach 7 Tagen gelöscht; Sicherungen halten gelöschte Daten bis zu sieben Tage. ⚖ Q9
|
||||
|
||||
### 2.3 Ende des Abos
|
||||
|
||||
1. Ein täglicher Abgleich prüft das Wartungs-Abo. Endet es (auch durch Wechsel ins Hosting-Abo oder durch Widerruf des Abschlusses oder eines Wechsels), schaltet WEBklar den Terminplaner spätestens am folgenden Tag ab und informiert den Kunden mit der Mail K22. Ist etwas nicht lesbar, schaltet der Abgleich nichts ab und versucht es am nächsten Tag erneut. ⚖ Q7
|
||||
2. Nach dem Abschalten nimmt der Terminplaner keine neuen Buchungen mehr an. Die Termine bleiben im Kundenportal sichtbar und als CSV-Datei exportierbar, Gäste können über ihren Link absagen, bereits angeforderte Erinnerungen laufen weiter, der Kunde informiert seine Gäste.
|
||||
3. 30 Tage nach dem Abschalten löscht WEBklar alle Termindaten und die Einstellungen; das Einrichtungs-Ticket des Teams bleibt als Arbeitsnachweis mit einer Kurzfassung der Einstellungen bestehen (keine Daten von Gästen). Beginnt innerhalb dieser 30 Tage wieder ein Wartungs-Abo, schaltet WEBklar den Terminplaner mit den vorhandenen Daten und Einstellungen ohne weitere Kosten wieder ein (Rückkehr, Mail K23). Nach der Löschung sind ein neuer Fragebogen und die Einrichtung für 149 € noch einmal nötig. ⚖ Q9
|
||||
4. Endet das Wartungs-Abo, bevor der Terminplaner freigeschaltet ist, erstattet WEBklar die 149 € vollständig. Der Abgleich schaltet den Terminplaner mit dem Grund „Abo endete vor Einrichtung“ ab und meldet dem Team die Erstattung (T19); die Erstattung löst der Inhaber in Stripe aus, es gibt keine automatische Rückzahlung. In diesem Fall gibt es keine automatische Rückkehr. ⚖ Q6
|
||||
|
||||
### 2.4 Widerruf der Bestellung durch den Kunden
|
||||
|
||||
Der Kunde widerruft auf der Seite „Vertrag widerrufen“ mit der Auswahl „Terminplaner (Einrichtung)“. Jede Bestellung hat ihre eigene Frist von 14 Tagen ab dem Zeitpunkt des Klicks auf „Zahlungspflichtig bestellen“ (bei mehreren Website-Projekten also mehrere Fristen). Die Widerrufsseite ordnet den Widerruf keinem Projekt zu: Sie bewertet den Eingang gegen das späteste Fristende aller bezahlten Terminplaner-Bestellungen des Kunden; das Team erhält in der Meldung T09 jede Bestellung mit Projekt, Zustand und eigenem Fristende und entscheidet. Der Widerruf erzeugt die Mail K04 an den Kunden und die Meldung T09 an das Team; die Abwicklung (Erstattung in Stripe abzüglich Wertersatz für einen schon begonnenen Einbau, Abschalten des Terminplaners) erledigt das Team von Hand. ⚖ Q2, Q5, Q7
|
||||
|
||||
### 2.5 Zahlen
|
||||
|
||||
| Wert | Bedeutung | Quelle im Code |
|
||||
|---|---|---|
|
||||
| 149 € | Einrichtung je Website-Projekt, einmalig, ohne Umsatzsteuer gemäß § 19 UStG | `priceLabel` des Terminplaners, `amountCents` 14900 |
|
||||
| 14 Tage | Widerrufsfrist | `WITHDRAWAL_DAYS` |
|
||||
| 30 Minuten | Haltezeit einer Buchung, Gültigkeit des Bestätigungslinks | `holdMinutes` |
|
||||
| 48 Stunden | Löschung nicht bestätigter Buchungen nach Ablauf der Bestätigungsfrist | `VERFALLEN_BEHALTEN_MS` |
|
||||
| 90 Tage | Aufbewahrung bestätigter Termine nach dem Terminende (Vorgabe) und Aufbewahrung von Entwürfen ohne Bestellung | `AUFBEWAHRUNG_VORGABE`, `ENTWURF_TAGE` |
|
||||
| 7 Tage | Eintrag in der Versandwarteschlange nach dem Versand; Sicherungen des Servers | `GESENDET_BEHALTEN_MS` |
|
||||
| 30 Tage | Löschung nach dem Abschalten; Löschung abgesagter oder abgelehnter Termine | `LOESCHFRIST_TAGE`, `ABGESAGT_BEHALTEN_MS` |
|
||||
|
||||
## 3. Texte in Endfassung
|
||||
|
||||
Alle Zitate stehen wörtlich so im Programm oder in den Dokumenten (Abschnitt 5 nennt die Fundstellen). Platzhalter in doppelten geschweiften Klammern stehen für Angaben des jeweiligen Betriebs. In den Zitaten steht vor dem Sternchen in Klammern (\*) und vor spitzen Klammern ein Rückstrich, damit die Darstellung sie nicht als Hervorhebung oder Markup deutet; gemeint sind die Zeichen selbst.
|
||||
|
||||
### 3.1 Bestellung des Terminplaners: vier Haken und Knopf
|
||||
|
||||
Auf der Bestellseite stehen vor dem Knopf die volle Widerrufsbelehrung mit Muster-Widerrufsformular (Abschnitt 3.2) und diese vier Haken, keiner ist vorangekreuzt. Die Links auf Leistungsbeschreibung, AVV und Widerrufsbelehrung in der Fassung stehen daneben, nicht im Text der Haken. Gespeichert wird genau der gezeigte Text mit Fassung und Zeitpunkt.
|
||||
|
||||
**Haken 1 (`unterlagen`): Zustimmung zu Leistungsbeschreibung und AVV**
|
||||
|
||||
> Ich stimme der Leistungsbeschreibung Terminplaner und dem Auftragsverarbeitungsvertrag Terminplaner in der Fassung 2026-10-05 zu.
|
||||
|
||||
⚖ Q1, Q10
|
||||
|
||||
**Haken 2 (`gesundheit`): kein Gesundheitsbereich**
|
||||
|
||||
> Mein Angebot liegt nicht im Gesundheitsbereich (zum Beispiel keine Arzt-, Zahnarzt-, Psychotherapie-, Heilpraktiker- oder Physiotherapiepraxis).
|
||||
|
||||
⚖ Q11
|
||||
|
||||
**Haken 3 (`beginn`): vorzeitiger Beginn, Erlöschen, Wertersatz**
|
||||
|
||||
> Ich verlange ausdrücklich, dass WEBklar vor Ablauf der Widerrufsfrist mit der Einrichtung des Terminplaners beginnt. Mir ist bekannt, dass mein Widerrufsrecht mit der vollständigen Erbringung der Einrichtung erlischt (§ 356 Abs. 5 Nr. 2 BGB). Widerrufe ich vor der vollständigen Erbringung, schulde ich einen angemessenen Betrag für die bis dahin erbrachte Leistung (§ 357a Abs. 2 BGB).
|
||||
|
||||
⚖ Q3, Q5
|
||||
|
||||
**Haken 4 (`information`): Pflichtinformationen**
|
||||
|
||||
> Ich habe die Pflichtinformationen zu dieser Bestellung gelesen: Leistung (Einrichtung des Terminplaners für diese Website: ein Widget an einer Stelle, gestaltet nach dem Fragebogen, mit Einbau und Datenschutz-Absatz), Gesamtpreis 149 € einmalig (ohne Umsatzsteuer gemäß § 19 UStG), voraussichtliche Dauer meist innerhalb von 1–2 Wochen, der Terminplaner endet mit dem Wartungs-Abo. Die Widerrufsbelehrung mit Muster-Widerrufsformular habe ich gelesen.
|
||||
|
||||
⚖ Q7, Q19, Q20
|
||||
|
||||
**Knopf**
|
||||
|
||||
> Zahlungspflichtig bestellen · 149 €
|
||||
|
||||
⚖ Q2, Q19
|
||||
|
||||
### 3.2 Widerrufsbelehrung und Muster-Widerrufsformular der Funktionen
|
||||
|
||||
WEBklar ist hier der Unternehmer. Die Belehrung steht auf der Bestellseite, im PDF-Anhang der Bestellbestätigung K15 und in der Datei `docs/rechtliches/widerrufsbelehrung-funktionen.md` (Fassung 2026-10-05).
|
||||
|
||||
**Widerrufsbelehrung und Muster-Widerrufsformular (Bestellung der Einrichtung)**
|
||||
|
||||
> Widerrufsbelehrung
|
||||
>
|
||||
> Widerrufsrecht
|
||||
>
|
||||
> Sie haben das Recht, binnen vierzehn Tagen ohne Angabe von Gründen diesen Vertrag zu widerrufen.
|
||||
>
|
||||
> Die Widerrufsfrist beträgt vierzehn Tage ab dem Tag des Vertragsabschlusses.
|
||||
>
|
||||
> Um Ihr Widerrufsrecht auszuüben, müssen Sie uns (WEBklar GbR, Am Schwimmbad 10, 67722 Winnweiler, Telefon: 0176 23726355, E-Mail: support@webklar.com) mittels einer eindeutigen Erklärung (z. B. ein mit der Post versandter Brief oder eine E-Mail) über Ihren Entschluss, diesen Vertrag zu widerrufen, informieren. Sie können dafür das beigefügte Muster-Widerrufsformular verwenden, das jedoch nicht vorgeschrieben ist.
|
||||
>
|
||||
> Sie können Ihr Widerrufsrecht auch online unter https://project.webklar.com/widerrufen.html ausüben. Wenn Sie diese Online-Funktion nutzen, übermitteln wir Ihnen auf einem dauerhaften Datenträger (z. B. durch eine E-Mail) unverzüglich eine Eingangsbestätigung mit Informationen zum Inhalt der Widerrufserklärung sowie dem Datum und der Uhrzeit ihres Eingangs.
|
||||
>
|
||||
> Zur Wahrung der Widerrufsfrist reicht es aus, dass Sie die Mitteilung über die Ausübung des Widerrufsrechts vor Ablauf der Widerrufsfrist absenden.
|
||||
>
|
||||
> Folgen des Widerrufs
|
||||
>
|
||||
> Wenn Sie diesen Vertrag widerrufen, haben wir Ihnen alle Zahlungen, die wir von Ihnen erhalten haben, einschließlich der Lieferkosten (mit Ausnahme der zusätzlichen Kosten, die sich daraus ergeben, dass Sie eine andere Art der Lieferung als die von uns angebotene, günstigste Standardlieferung gewählt haben), unverzüglich und spätestens binnen vierzehn Tagen ab dem Tag zurückzuzahlen, an dem die Mitteilung über Ihren Widerruf dieses Vertrags bei uns eingegangen ist. Für diese Rückzahlung verwenden wir dasselbe Zahlungsmittel, das Sie bei der ursprünglichen Transaktion eingesetzt haben, es sei denn, mit Ihnen wurde ausdrücklich etwas anderes vereinbart; in keinem Fall werden Ihnen wegen dieser Rückzahlung Entgelte berechnet.
|
||||
>
|
||||
> Haben Sie verlangt, dass die Dienstleistungen während der Widerrufsfrist beginnen soll, so haben Sie uns einen angemessenen Betrag zu zahlen, der dem Anteil der bis zu dem Zeitpunkt, zu dem Sie uns von der Ausübung des Widerrufsrechts hinsichtlich dieses Vertrags unterrichten, bereits erbrachten Dienstleistungen im Vergleich zum Gesamtumfang der im Vertrag vorgesehenen Dienstleistungen entspricht.
|
||||
>
|
||||
> Muster-Widerrufsformular
|
||||
>
|
||||
> (Wenn Sie den Vertrag widerrufen wollen, dann füllen Sie bitte dieses Formular aus und senden Sie es zurück.)
|
||||
>
|
||||
> – An WEBklar GbR, Am Schwimmbad 10, 67722 Winnweiler, E-Mail: support@webklar.com:
|
||||
> – Hiermit widerrufe(n) ich/wir (\*) den von mir/uns (\*) abgeschlossenen Vertrag über den Kauf der folgenden Waren (\*)/die Erbringung der folgenden Dienstleistung (\*)
|
||||
> – Bestellt am (\*)/erhalten am (\*)
|
||||
> – Name des/der Verbraucher(s)
|
||||
> – Anschrift des/der Verbraucher(s)
|
||||
> – Unterschrift des/der Verbraucher(s) (nur bei Mitteilung auf Papier)
|
||||
> – Datum
|
||||
>
|
||||
> (\*) Unzutreffendes streichen.
|
||||
|
||||
⚖ Q1, Q2, Q12
|
||||
|
||||
Das Dokument enthält vor der Belehrung einen Einleitungssatz und nach dem Muster-Widerrufsformular „Hinweise zum Widerruf“, die nicht zur gesetzlichen Belehrung gehören:
|
||||
|
||||
**Einleitung des Dokuments**
|
||||
|
||||
> Das Widerrufsrecht gilt nur für Verbraucher. Wir zeigen die Belehrung und das Muster-Widerrufsformular trotzdem allen, die eine Funktion bestellen. Die Belehrung gilt für die Bestellung der Einrichtung einer Funktion im Kundenportal, zum Beispiel des Terminplaners. Hinweise dazu stehen nach dem Muster-Widerrufsformular.
|
||||
|
||||
⚖ Q1
|
||||
|
||||
**Hinweise zum Widerruf**
|
||||
|
||||
> Diese Hinweise gehören nicht zur gesetzlichen Widerrufsbelehrung. Sie erklären, wie sich das Widerrufsrecht auf die Bestellung einer Funktion auswirkt.
|
||||
>
|
||||
> - Das Widerrufsrecht gilt für jede Bestellung einer Funktion gesondert, also für jedes Website-Projekt, für das der Kunde eine Funktion bestellt. Die Frist von vierzehn Tagen beginnt jeweils mit dem Vertragsschluss.
|
||||
> - Widerruft der Kunde die Einrichtung einer Funktion, endet diese Funktion. Das Wartungs-Abo bleibt davon unberührt.
|
||||
> - Hat WEBklar auf ausdrücklichen Wunsch des Kunden vor Ablauf der Widerrufsfrist mit der Einrichtung begonnen, schuldet der Kunde bei einem Widerruf einen angemessenen Betrag für die bis dahin erbrachte Leistung. Der Betrag bemisst sich als Anteil am Gesamtpreis; Maß ist der im Arbeitsblatt erfasste Aufwand für die Einrichtung.
|
||||
> - Die Rückzahlung erfolgt über das ursprüngliche Zahlungsmittel; die Zahlung lief über Stripe.
|
||||
|
||||
⚖ Q5, Q7
|
||||
|
||||
### 3.3 Zustimmung bei der Bestellung der News-Pinnwand (Altbestand)
|
||||
|
||||
Die News-Pinnwand (30 € einmalig) wird seit 2026-09-30 über denselben Bestellweg verkauft, bisher mit einem Haken und ohne Widerrufsbelehrung. Zum Vergleich der Text, den neue Bestellungen jetzt sehen, und der Haken, der bis zur Korrektur galt (Programmstand 3a0b008 vor der Korrektur). Die Leistungsbeschreibung der News-Pinnwand aus dieser Zeit steht in Frage Q4.
|
||||
|
||||
**Neuer Haken der News-Pinnwand**
|
||||
|
||||
> Ich verlange ausdrücklich, dass WEBklar vor Ablauf der Widerrufsfrist mit dem Einbau beginnt. Mir ist bekannt, dass mein Widerrufsrecht mit der vollständigen Erbringung des Einbaus erlischt (§ 356 Abs. 5 Nr. 2 BGB). Widerrufe ich vor der vollständigen Erbringung, schulde ich einen angemessenen Betrag für die bis dahin erbrachte Leistung (§ 357a Abs. 2 BGB).
|
||||
|
||||
⚖ Q4, Q3
|
||||
|
||||
**Alte Fassung (Stand 3a0b008): früherer Haken der News-Bestellung**
|
||||
|
||||
> Ich verlange ausdrücklich, dass WEBklar vor Ablauf der Widerrufsfrist mit dem Einbau beginnt. Mir ist bekannt, dass mein Widerrufsrecht erlischt, sobald der Einbau vollständig erbracht ist (§ 356 Abs. 4 BGB).
|
||||
|
||||
⚖ Q4
|
||||
|
||||
### 3.4 Leistungsbeschreibung Terminplaner (Anlage B), Auszüge
|
||||
|
||||
Die Datei ist `docs/rechtliches/leistungsbeschreibung-terminplaner.md`; sie ist als Anlage B vollständig beigefügt. Zitiert sind die Nummern, auf die sich unsere Fragen beziehen; weitere Nummern behandeln Geltung, Leistung, Voraussetzungen und Verfügbarkeit.
|
||||
|
||||
**Nr. 4 Abs. 3: Gesundheitsbereich**
|
||||
|
||||
> (3) Der Terminplaner ist nicht für Angebote im Gesundheitsbereich bestimmt, weil dort besondere Kategorien personenbezogener Daten anfallen können (Art. 9 DSGVO). Der Kunde setzt ihn dort nicht ein und fordert von seinen Gästen keine Gesundheitsangaben. WEBklar darf die Einrichtung für eine solche Website ablehnen oder den Terminplaner dort abschalten.
|
||||
|
||||
⚖ Q11
|
||||
|
||||
**Nr. 4 Abs. 9: Datenschutz-Absatz auf der Website des Kunden**
|
||||
|
||||
> (9) Liegt die Datenschutzerklärung in der von WEBklar betreuten Website, fügt WEBklar beim Einbau dort den Datenschutz-Absatz für die Online-Terminbuchung ein; das gehört zur Einrichtung und wird nicht auf das Stundenkontingent angerechnet. Liegt sie woanders, etwa bei einem externen Generator, erhält der Kunde den Absatz als Textbaustein und fügt ihn selbst ein. Für den übrigen Inhalt seiner Datenschutzerklärung bleibt der Kunde verantwortlich (§ 6 Abs. 3 der Abo-Bedingungen). Fällt WEBklar beim Einfügen ein offensichtlicher Widerspruch auf, weist WEBklar den Kunden darauf hin; eine Pflicht zur Prüfung der Datenschutzerklärung entsteht daraus nicht.
|
||||
|
||||
⚖ Q18
|
||||
|
||||
**Nr. 5: Einrichtung und Preis**
|
||||
|
||||
> (1) Die Einrichtung umfasst die Einrichtung nach dem Fragebogen, den Einbau des Widgets in die Website, den Datenschutz-Absatz nach Nr. 4 Abs. 9, die Abnahme durch den Kunden und die Freischaltung durch WEBklar.
|
||||
>
|
||||
> (2) Die Einrichtung kostet 149 € einmalig je Website-Projekt, ohne Umsatzsteuer gemäß § 19 UStG.
|
||||
>
|
||||
> (3) Die voraussichtliche Dauer der Einrichtung nennt die Bestellseite vor der Bestellung. Sie ist eine Einschätzung und keine verbindliche Zusage eines Termins.
|
||||
>
|
||||
> (4) Der Kunde bezahlt bei der Bestellung im Kundenportal über Stripe und erhält eine Rechnung.
|
||||
>
|
||||
> (5) Der Preis steht neben dem Stundenkontingent des Wartungs-Abos: Die Einrichtung wird weder auf das Kontingent angerechnet noch damit bezahlt. Für den Betrieb des Terminplaners berechnet WEBklar neben dem Wartungs-Abo nichts weiter; er läuft, solange das Wartungs-Abo läuft (Nr. 8).
|
||||
|
||||
⚖ Q19, Q20
|
||||
|
||||
**Nr. 7: Haftung für verpasste Termine**
|
||||
|
||||
> (1) Der Terminplaner ist ein Hilfsmittel. Die Einhaltung der Termine bleibt Sache des Kunden.
|
||||
>
|
||||
> (2) WEBklar übernimmt keine Gewähr für die Zustellung der E-Mails (Nr. 6 Abs. 4).
|
||||
>
|
||||
> (3) Für entgangenen Gewinn und Folgeschäden aus verpassten Terminen haftet WEBklar nur im Rahmen von § 13 Abs. 1 und 2 der Abo-Bedingungen.
|
||||
>
|
||||
> (4) Der Haftungsrahmen des Kunden gegenüber seinen Gästen bleibt unberührt.
|
||||
|
||||
⚖ Q8
|
||||
|
||||
**Nr. 8: Ende der Leistung**
|
||||
|
||||
> (1) Mit dem Ende des Wartungs-Abos endet der Terminplaner. WEBklar schaltet ihn spätestens am folgenden Tag ab und informiert den Kunden per E-Mail. Das gilt auch bei einem Wechsel ins Hosting-Abo und beim Widerruf des Abschlusses oder eines Wechsels ins Wartungs-Abo.
|
||||
>
|
||||
> (2) Mit dem Abschalten nimmt der Terminplaner keine neuen Buchungen mehr an, und das Widget zeigt den Ausweichtext. Die Termine bleiben im Kundenportal sichtbar, und der Kunde kann sie als CSV-Datei exportieren. Gäste können über ihren Link absagen. Bereits angeforderte Erinnerungen laufen weiter. Der Kunde informiert seine Gäste.
|
||||
>
|
||||
> (3) WEBklar löscht alle Termindaten und die Einstellungen 30 Tage nach dem Abschalten. Eine Ausnahme ist das Einrichtungs-Ticket, in dem WEBklar die Einrichtung als Arbeitsnachweis festhält: Es bleibt mit einer Kurzfassung der Einstellungen bestehen und enthält keine Daten von Gästen (Nummer 9 Abs. 6 des AVV Terminplaner).
|
||||
>
|
||||
> (4) Beginnt innerhalb dieser 30 Tage wieder ein Wartungs-Abo, schaltet WEBklar den Terminplaner mit den vorhandenen Daten und Einstellungen ohne weitere Kosten wieder ein. Nach der Löschung ist eine neue Einrichtung nötig (neuer Fragebogen, neuer Preis nach Nr. 5).
|
||||
>
|
||||
> (5) Endet das Wartungs-Abo, bevor WEBklar den Terminplaner freigeschaltet hat, erstattet WEBklar den Einrichtungspreis vollständig.
|
||||
>
|
||||
> (6) Auf Wunsch des Kunden in Textform schaltet WEBklar den Terminplaner jederzeit ab. Eine Erstattung des Einrichtungspreises folgt daraus nicht; für den Widerruf gilt Nr. 10.
|
||||
|
||||
⚖ Q6, Q7, Q9
|
||||
|
||||
**Nr. 9 Abs. 4: Einstellung ohne Grund**
|
||||
|
||||
> (4) Eine Einstellung ohne einen Grund nach Absatz 2 ist ausgeschlossen. Gesetzliche Rechte von Verbrauchern bei Änderungen digitaler Produkte (§ 327r BGB) bleiben unberührt.
|
||||
|
||||
⚖ Q8
|
||||
|
||||
**Nr. 10: Widerruf**
|
||||
|
||||
> (1) Verbraucher können die Bestellung der Einrichtung nach der Widerrufsbelehrung widerrufen. Belehrung und Muster-Widerrufsformular stehen in der Anlage „Widerrufsbelehrung und Muster-Widerrufsformular (Einrichtung von Funktionen)“.
|
||||
>
|
||||
> (2) Ob das Widerrufsrecht erlischt und ob der Kunde einen angemessenen Betrag für die bis dahin erbrachte Leistung schuldet, richtet sich nach der Zustimmung, die der Kunde bei der Bestellung gibt, und nach den Hinweisen zum Widerruf in der Anlage.
|
||||
>
|
||||
> (3) Mit dem Widerruf endet der Terminplaner.
|
||||
|
||||
⚖ Q3, Q5, Q7
|
||||
|
||||
**Nr. 11: Hinweise**
|
||||
|
||||
> (1) **Keine Rechtsberatung.** Der Hinweis am Buchungsformular, die Texte der Haken, der Datenschutz-Absatz und die Widerrufsbelehrung für Gäste sind Mustertexte für die Standardkonfiguration. WEBklar sorgt dafür, dass sie die Technik richtig beschreiben, und passt sie an, wenn sich die Technik ändert. Die Einstufung seiner Leistungen als Leistung für Verbraucher oder Unternehmer und als Freizeitleistung trägt der Kunde. Eine rechtliche Beratung im Einzelfall schuldet WEBklar nicht.
|
||||
>
|
||||
> (2) **Barrierefreiheit.** Das Widget ist für die Bedienung mit Tastatur und Hilfstechniken gestaltet (Beschriftungen, Fokusführung, Fehlermeldungen als Live-Region). Eine Konformität nach WCAG 2.1 AA oder EN 301 549 sagt WEBklar nicht zu. Ob der Kunde als Dienstleistungserbringer unter das BFSG fällt (Ausnahme für Kleinstunternehmen, § 3 Abs. 3 BFSG), entscheidet und prüft der Kunde.
|
||||
|
||||
⚖ Q15, Q18, Q22
|
||||
|
||||
### 3.5 Auftragsverarbeitungsvertrag Terminplaner (Anlage A), Auszüge
|
||||
|
||||
Die Datei ist `docs/rechtliches/avv-terminplaner.md`; sie ist als Anlage A vollständig beigefügt (mit der Anlage der technischen und organisatorischen Maßnahmen).
|
||||
|
||||
**Vorbemerkung Abs. 1 und 2: Abschluss**
|
||||
|
||||
> (1) Der Kunde schließt diesen Vertrag, wenn er im Kundenportal die Einrichtung des Terminplaners bestellt und dabei per Haken diesem Vertrag und der Leistungsbeschreibung „Terminplaner“ („Leistungsbeschreibung“) in der angezeigten Fassung zustimmt. Das Portal speichert die Fassung, den Zeitpunkt, das Kundenkonto und die angemeldete Person. Anschließend sendet WEBklar dem Kunden eine E-Mail, die beide Texte und die Widerrufsbelehrung in dieser Fassung als PDF enthält und auf sie verlinkt.
|
||||
>
|
||||
> (2) Ein Mitarbeiter von WEBklar darf die Funktion nur einschalten, wenn der Kunde vorher in Textform (etwa im Chat des Kundenportals oder per E-Mail) erklärt hat, dass er diesem Vertrag und der Leistungsbeschreibung in der aktuellen Fassung zustimmt. WEBklar speichert die Fassung, das Datum und den Weg dieser Erklärung, den Zeitpunkt des Einschaltens und den Namen des Mitarbeiters. Der Kunde erhält anschließend eine E-Mail mit diesen beiden Texten als PDF.
|
||||
|
||||
⚖ Q10, Q21
|
||||
|
||||
**Nr. 3 Abs. 1: Daten der Gäste**
|
||||
|
||||
> (1) Daten der Gäste: Name und E-Mail-Adresse; auf Einstellung des Kunden außerdem Telefonnummer, eine Nachricht und Zusatzfelder, die der Kunde im Formular festlegt; die gebuchte Leistung, die Person oder der Platz und der Termin; der Status der Buchung mit den Zeitpunkten (Anlegen, Bestätigung, Annahme, Absage, Widerruf); Absagegrund und Vermerke zu einer Absage oder einem Widerruf (Freitext, auch mit dem Namen aus einer Widerrufserklärung); Preis, Zahlart, Zahlungsstatus und die Angabe, ob als Verbraucher gebucht wurde; der Nachweis der Zustimmungen (Fassung der Gästetexte, Zeitpunkt, Text des Buchungsknopfs, Anrede und Hashes (Prüfsummen) der gezeigten Texte); der Wunsch nach einer Erinnerung; der Zustellstatus der E-Mails an den Gast.
|
||||
|
||||
⚖ Q10
|
||||
|
||||
**Nr. 4 Abs. 2: Weisungen**
|
||||
|
||||
> (2) Mit der Bestellung weist der Kunde WEBklar an, den Terminplaner nach seinen Einstellungen und der Leistungsbeschreibung zu betreiben. Weitere Weisungen ergeben sich aus den Einstellungen im Kundenportal (Leistungen, Personen oder Plätze, Zeiten, Felder des Formulars, Aufbewahrungsdauer, Erinnerungen, Einschalten und Ausschalten). Sonstige Weisungen erteilt der Kunde in Textform, etwa im Chat des Kundenportals. Weisungen, die über diese Einstellungen hinausgehen, setzt WEBklar nur um, wenn das technisch vorgesehen ist; sonst kann der Kunde den Terminplaner abschalten lassen (Nr. 8 Abs. 6 der Leistungsbeschreibung). Die Löschung auf Weisung nach Nummer 9 Abs. 6 setzt WEBklar immer um.
|
||||
|
||||
⚖ Q10
|
||||
|
||||
**Nr. 7: Unterauftragsverarbeiter**
|
||||
|
||||
> (1) Der Kunde genehmigt den Einsatz der Hetzner Online GmbH, Industriestraße 25, 91710 Gunzenhausen, für den Betrieb des Servers, auf dem der Terminplaner läuft, und für dessen Sicherung (Rechenzentrum Falkenstein, Deutschland) sowie für den Versand der Buchungs-E-Mails über den SMTP-Dienst des Webhostings (Rechenzentrum Nürnberg, Deutschland). Eine Übermittlung in Drittländer findet nicht statt.
|
||||
>
|
||||
> (2) Will WEBklar einen weiteren Unterauftragsverarbeiter hinzuziehen oder einen ersetzen, teilt es das dem Kunden mindestens vier Wochen vorher in Textform mit. Der Kunde kann innerhalb von zwei Wochen nach Zugang der Mitteilung aus wichtigem datenschutzrechtlichem Grund in Textform widersprechen. WEBklar setzt den neuen Unterauftragsverarbeiter nicht vor Ablauf dieser Frist ein. Kündigt ein Unterauftragsverarbeiter von WEBklar eine eigene Änderung mit kürzerer Frist an, informiert WEBklar den Kunden unverzüglich, nachdem die Ankündigung eingegangen ist; der Kunde kann dann unverzüglich widersprechen. Einigen sich die Parteien nicht, kann der Kunde den Terminplaner abschalten lassen und WEBklar ihn für diese Website zu dem Zeitpunkt beenden, zu dem der neue Unterauftragsverarbeiter eingesetzt wird. Die Daten werden dann nach Nummer 9 gelöscht, auf Weisung des Kunden früher (Nummer 9 Abs. 6).
|
||||
>
|
||||
> (3) WEBklar verpflichtet jeden Unterauftragsverarbeiter vertraglich auf die Datenschutzpflichten nach Art. 28 Abs. 4 DSGVO. Bei der Hetzner Online GmbH ist das deren Vertrag zur Auftragsverarbeitung, den WEBklar abgeschlossen hat. Kommt ein Unterauftragsverarbeiter seinen Datenschutzpflichten nicht nach, haftet WEBklar dem Kunden für die Einhaltung dieser Pflichten (Art. 28 Abs. 4 Satz 2 DSGVO).
|
||||
>
|
||||
> (4) Die Zahlung der Einrichtung über Stripe betrifft nur den Vertrag zwischen dem Kunden und WEBklar. Daten von Gästen oder Beschäftigten des Kunden gehen nicht an Stripe. Stripe ist deshalb kein Unterauftragsverarbeiter dieses Vertrags.
|
||||
|
||||
⚖ Q10
|
||||
|
||||
**Nr. 8 Abs. 5: Nachweis und Archiv der Gästetexte**
|
||||
|
||||
> (5) Die Buchung speichert als Nachweis der Zustimmungen die Fassung der Gästetexte, den Zeitpunkt, den Text des Buchungsknopfs, die Anrede und Hashes der übrigen Texte, die der Gast gesehen hat, nicht diese Texte selbst. Damit sich der Wortlaut nachweisen lässt, bewahrt WEBklar jede Fassung der Gästetexte (Hinweis am Formular, Texte der Haken, Datenschutz-Absatz und Widerrufsbelehrung für Gäste) mit dem Zeitraum ihrer Verwendung auf, bis drei Jahre nach dem Ende des Terminplaners. WEBklar stellt sie dem Kunden auf Verlangen zur Verfügung.
|
||||
|
||||
⚖ Q26
|
||||
|
||||
**Nr. 9: Löschung und Rückgabe**
|
||||
|
||||
> (1) Nicht bestätigte Buchungen löscht WEBklar 48 Stunden nach Ablauf der Bestätigungsfrist von 30 Minuten.
|
||||
>
|
||||
> (2) Abgesagte und abgelehnte Termine löscht WEBklar 30 Tage nach der Absage oder Ablehnung. Terminanfragen, die der Kunde bis zum angefragten Terminbeginn nicht beantwortet, behandelt der Terminplaner als abgelehnt; die 30 Tage laufen dann ab diesem Zeitpunkt.
|
||||
>
|
||||
> (3) Bestätigte und erledigte Termine löscht WEBklar nach der Aufbewahrungsdauer, gerechnet ab dem Ende des Termins. Der Kunde stellt sie im Kundenportal ein; die Vorgabe sind 90 Tage, einstellbar sind 14 bis 365 Tage.
|
||||
>
|
||||
> (4) In der Versandwarteschlange für E-Mails leert WEBklar die Empfängeradresse und den Inhalt einer E-Mail nach dem Versand und löscht den Eintrag 7 Tage nach dem Versand. E-Mails, die nicht zugestellt werden können, löscht WEBklar mit Empfängeradresse und Inhalt 48 Stunden nach dem endgültigen Fehlschlag.
|
||||
>
|
||||
> (5) Der Löschlauf läuft täglich; gelöscht wird frühestens nach Ablauf der jeweiligen Frist. Ist an einem Tag mehr zu löschen, als ein Lauf schafft, folgt der Rest im nächsten Lauf.
|
||||
>
|
||||
> (6) Ist der Terminplaner abgeschaltet oder das Wartungs-Abo beendet, löscht WEBklar alle Termindaten und die Einstellungen 30 Tage danach. Eine Ausnahme ist das Einrichtungs-Ticket, in dem WEBklar die Einrichtung als Arbeitsnachweis festhält: Es bleibt mit einer Kurzfassung der Einstellungen bestehen (zum Beispiel Betriebsangaben, Namen der Leistungen, die ersten acht Namen der Personen oder Plätze und die Wünsche des Kunden zum Einbau) und enthält keine Daten von Gästen; dafür gilt die Datenschutzerklärung von WEBklar (Abschnitt 6). Bis zur Löschung kann der Kunde die Termine im Kundenportal als CSV-Datei exportieren. Beginnt innerhalb dieser Frist wieder ein Wartungs-Abo, schaltet WEBklar den Terminplaner mit den vorhandenen Daten und Einstellungen wieder ein (Nr. 8 Abs. 4 der Leistungsbeschreibung). Früher löscht der Kunde einzelne Gastdaten selbst mit der Aktion „Gastdaten löschen“ (Nummer 8 Abs. 2); alle Daten löscht WEBklar auf Weisung des Kunden in Textform unverzüglich.
|
||||
>
|
||||
> (7) Entwürfe aus dem Fragebogen, aus denen keine Bestellung geworden ist, löscht WEBklar 90 Tage nach der letzten Änderung.
|
||||
>
|
||||
> (8) Die Daten sind in der täglichen Vollsicherung des Servers enthalten (§ 3 Abs. 5 der Abo-Bedingungen). Gelöschte Daten bleiben dort bis zu sieben Tage erhalten und werden dann überschrieben. Wird eine Sicherung zurückgespielt, holt der tägliche Löschlauf die fälligen Löschungen nach; Löschungen auf Weisung des Kunden wiederholt WEBklar.
|
||||
>
|
||||
> (9) Gesetzliche Aufbewahrungspflichten bleiben unberührt.
|
||||
|
||||
⚖ Q7, Q9, Q10
|
||||
|
||||
### 3.6 Gäste: Widerrufsbelehrung und Muster-Widerrufsformular
|
||||
|
||||
Der Terminplaner erzeugt diese Belehrung mit den Angaben des Betriebs (alle vier Angaben sind Pflicht) und zeigt sie Verbrauchern beim Buchen und in der Bestätigungs-E-Mail. Grundlage ist das Muster der Anlage 1 und das Muster-Widerrufsformular der Anlage 2 zu Art. 246a § 1 Abs. 2 Satz 2 EGBGB (Dateien `docs/rechtliches/quellen/anlage-1.txt` und `anlage-2.txt`, abgerufen 2026-10-02). Gestaltungshinweise für einen Dienstleistungsvertrag sind angewendet: Fristbeginn „des Vertragsabschlusses“, Wertersatz für Dienstleistungen; die Widerrufsfunktion nennt keine Internetadresse, sondern einen anderen geeigneten Hinweis. Ausgelassen sind die Textbausteine zu Waren und der Absatz zum elektronischen Webformular. Die Anrede ist immer „Sie“.
|
||||
|
||||
**Widerrufsbelehrung und Muster-Widerrufsformular für Gäste (Platzhalter für die Betriebsangaben)**
|
||||
|
||||
> Widerrufsbelehrung
|
||||
>
|
||||
> Widerrufsrecht
|
||||
>
|
||||
> Sie haben das Recht, binnen vierzehn Tagen ohne Angabe von Gründen diesen Vertrag zu widerrufen.
|
||||
>
|
||||
> Die Widerrufsfrist beträgt vierzehn Tage ab dem Tag des Vertragsabschlusses.
|
||||
>
|
||||
> Um Ihr Widerrufsrecht auszuüben, müssen Sie uns ({{Name des Betriebs}}, {{Anschrift}}, Telefon: {{Telefon}}, E-Mail: {{E-Mail}}) mittels einer eindeutigen Erklärung (z. B. ein mit der Post versandter Brief oder eine E-Mail) über Ihren Entschluss, diesen Vertrag zu widerrufen, informieren. Sie können dafür das beigefügte Muster-Widerrufsformular verwenden, das jedoch nicht vorgeschrieben ist.
|
||||
>
|
||||
> Sie können Ihr Widerrufsrecht auch online unter „Vertrag widerrufen“ (Schaltfläche im Terminbereich auf unserer Website sowie Link „Termin verwalten“ in Ihrer Bestätigungs-E-Mail) ausüben. Wenn Sie diese Online-Funktion nutzen, übermitteln wir Ihnen auf einem dauerhaften Datenträger (z. B. durch eine E-Mail) unverzüglich eine Eingangsbestätigung mit Informationen zum Inhalt der Widerrufserklärung sowie dem Datum und der Uhrzeit ihres Eingangs.
|
||||
>
|
||||
> Zur Wahrung der Widerrufsfrist reicht es aus, dass Sie die Mitteilung über die Ausübung des Widerrufsrechts vor Ablauf der Widerrufsfrist absenden.
|
||||
>
|
||||
> Folgen des Widerrufs
|
||||
>
|
||||
> Wenn Sie diesen Vertrag widerrufen, haben wir Ihnen alle Zahlungen, die wir von Ihnen erhalten haben, einschließlich der Lieferkosten (mit Ausnahme der zusätzlichen Kosten, die sich daraus ergeben, dass Sie eine andere Art der Lieferung als die von uns angebotene, günstigste Standardlieferung gewählt haben), unverzüglich und spätestens binnen vierzehn Tagen ab dem Tag zurückzuzahlen, an dem die Mitteilung über Ihren Widerruf dieses Vertrags bei uns eingegangen ist. Für diese Rückzahlung verwenden wir dasselbe Zahlungsmittel, das Sie bei der ursprünglichen Transaktion eingesetzt haben, es sei denn, mit Ihnen wurde ausdrücklich etwas anderes vereinbart; in keinem Fall werden Ihnen wegen dieser Rückzahlung Entgelte berechnet.
|
||||
>
|
||||
> Haben Sie verlangt, dass die Dienstleistungen während der Widerrufsfrist beginnen soll, so haben Sie uns einen angemessenen Betrag zu zahlen, der dem Anteil der bis zu dem Zeitpunkt, zu dem Sie uns von der Ausübung des Widerrufsrechts hinsichtlich dieses Vertrags unterrichten, bereits erbrachten Dienstleistungen im Vergleich zum Gesamtumfang der im Vertrag vorgesehenen Dienstleistungen entspricht.
|
||||
>
|
||||
> Muster-Widerrufsformular
|
||||
>
|
||||
> (Wenn Sie den Vertrag widerrufen wollen, dann füllen Sie bitte dieses Formular aus und senden Sie es zurück.)
|
||||
>
|
||||
> – An {{Name des Betriebs}}, {{Anschrift}}, {{E-Mail}}:
|
||||
> – Hiermit widerrufe(n) ich/wir (\*) den von mir/uns (\*) abgeschlossenen Vertrag über den Kauf der folgenden Waren (\*)/die Erbringung der folgenden Dienstleistung (\*)
|
||||
> – Bestellt am (\*)/erhalten am (\*)
|
||||
> – Name des/der Verbraucher(s)
|
||||
> – Anschrift des/der Verbraucher(s)
|
||||
> – Unterschrift des/der Verbraucher(s) (nur bei Mitteilung auf Papier)
|
||||
> – Datum
|
||||
>
|
||||
> (\*) Unzutreffendes streichen.
|
||||
|
||||
⚖ Q12, Q13
|
||||
|
||||
### 3.7 Gäste: Haken und Hinweis zu Gesundheitsangaben
|
||||
|
||||
Die Haken stehen immer in der Sie-Form. Keiner ist vorangekreuzt.
|
||||
|
||||
**Haken „Vorzeitiger Beginn“** (Pflicht, nur für Verbraucher bei Leistungen mit Preis, wenn der Termin vor Ablauf der Widerrufsfrist liegt; bei Anfragen mit Preis immer)
|
||||
|
||||
> Ich verlange ausdrücklich, dass {{Firma}} mit der Leistung vor Ablauf der Widerrufsfrist beginnt. Mir ist bekannt, dass mein Widerrufsrecht mit der vollständigen Erbringung der Leistung erlischt (§ 356 Abs. 5 Nr. 2 BGB). Widerrufe ich vor der vollständigen Erbringung, schulde ich einen angemessenen Betrag für die bis dahin erbrachte Leistung (§ 357a Abs. 2 BGB).
|
||||
|
||||
⚖ Q13, Q3
|
||||
|
||||
**Haken „Stornobedingungen“** (Pflicht, wenn der Betrieb Stornobedingungen hinterlegt hat und die Leistung einen Preis hat)
|
||||
|
||||
> Ich habe die Stornobedingungen gelesen.
|
||||
|
||||
⚖ Q16
|
||||
|
||||
**Haken „Erinnerung“** (freiwillig; nur wenn der Betrieb Erinnerungen anbietet; `{{n}}` ist 2, 12, 24 oder 48)
|
||||
|
||||
> Ich möchte {{n}} Stunden vor dem Termin eine Erinnerung per E-Mail erhalten. Diese Einwilligung kann ich jederzeit widerrufen.
|
||||
|
||||
⚖ Q17
|
||||
|
||||
**Hinweis über jedem Freitextfeld**
|
||||
|
||||
> Bitte keine Gesundheitsangaben.
|
||||
|
||||
⚖ Q11
|
||||
|
||||
### 3.8 Gäste: Datenschutz-Hinweis am Buchungsformular
|
||||
|
||||
Der Hinweis steht in der Zusammenfassung vor dem Absenden, zusammen mit einem Link auf die Datenschutzerklärung des Betriebs; es gibt keinen Haken dafür. Sie- oder Du-Fassung nach der Anrede des Betriebs; die Löschfristen sind die des Löschlaufs.
|
||||
|
||||
**Fassung Sie**
|
||||
|
||||
> Datenschutz. Wir verwenden Ihre Angaben, um Ihren Termin zu verwalten und zu bestätigen (Art. 6 Abs. 1 lit. b DSGVO). Betrieben wird der Terminkalender in unserem Auftrag von der WEBklar GbR (Server: Hetzner, Deutschland). Wir speichern Ihre Daten bis 90 Tage nach dem Termin, abgesagte Termine 30 Tage, nicht bestätigte Buchungen 48 Stunden nach Ablauf der Bestätigungsfrist. Es werden keine Cookies gesetzt. Bitte machen Sie keine Angaben zu Ihrer Gesundheit. Mehr erfahren Sie in unserer Datenschutzerklärung. Verantwortlich: {{Firma}}, {{Anschrift}}.
|
||||
|
||||
⚖ Q17, Q9
|
||||
|
||||
**Fassung Du**
|
||||
|
||||
> Datenschutz. Wir verwenden deine Angaben, um deinen Termin zu verwalten und zu bestätigen (Art. 6 Abs. 1 lit. b DSGVO). Betrieben wird der Terminkalender in unserem Auftrag von der WEBklar GbR (Server: Hetzner, Deutschland). Wir speichern deine Daten bis 90 Tage nach dem Termin, abgesagte Termine 30 Tage, nicht bestätigte Buchungen 48 Stunden nach Ablauf der Bestätigungsfrist. Es werden keine Cookies gesetzt. Bitte mach keine Angaben zu deiner Gesundheit. Mehr erfährst du in unserer Datenschutzerklärung. Verantwortlich: {{Firma}}, {{Anschrift}}.
|
||||
|
||||
⚖ Q17, Q9
|
||||
|
||||
### 3.9 Gäste: Hinweise in der Bestätigungs-E-Mail (G02)
|
||||
|
||||
Die Bestätigungs-E-Mail (Vorlage `server/mail-templates/G02 Termin bestaetigt.html`) enthält je nach Buchung einen der beiden folgenden Hinweise (hier die Sie-Fassung); danach folgt bei Widerrufsrecht die Belehrung aus Abschnitt 3.6. Die Du-Fassung ist inhaltlich gleich.
|
||||
|
||||
**Widerrufsrecht besteht (Verbraucher)**
|
||||
|
||||
> Widerrufsrecht. Wenn Sie Verbraucher sind, können Sie diesen Vertrag innerhalb von 14 Tagen ab Vertragsschluss ohne Angabe von Gründen widerrufen. Bis zum Beginn des Termins geht das über den Knopf „Vertrag widerrufen“; danach über das Formular „Vertrag widerrufen“ auf der Website von {{betrieb_name}} oder per E-Mail an {{betrieb_email}}. Die ausführliche Widerrufsbelehrung und das Muster-Widerrufsformular finden Sie weiter unten in dieser E-Mail.
|
||||
|
||||
⚖ Q2, Q12
|
||||
|
||||
**Kein Widerrufsrecht (Freizeit-Dienstleistung mit festem Termin)**
|
||||
|
||||
> Für diese Buchung besteht kein Widerrufsrecht (§ 312g Abs. 2 Nr. 9 BGB). Absagen richten sich nach den Stornobedingungen.
|
||||
|
||||
⚖ Q15, Q16
|
||||
|
||||
### 3.10 Gäste: Knopftexte
|
||||
|
||||
Die Knopftexte hängen davon ab, ob die Buchung verbindlich oder eine Anfrage ist und ob die Leistung einen Preis hat. Im Widget und auf der Bestätigungsseite heißen die Knöpfe:
|
||||
|
||||
**Verbindliche Buchung mit Preis: Widget, dann Bestätigungsseite**
|
||||
|
||||
> Zahlungspflichtig buchen
|
||||
>
|
||||
> Zahlungspflichtig bestätigen
|
||||
|
||||
⚖ Q14
|
||||
|
||||
**Verbindliche Buchung ohne Preis: Widget, dann Bestätigungsseite**
|
||||
|
||||
> Termin buchen
|
||||
>
|
||||
> Termin bestätigen
|
||||
|
||||
⚖ Q14
|
||||
|
||||
**Anfrage: Widget, dann Bestätigungsseite**
|
||||
|
||||
> Termin anfragen
|
||||
>
|
||||
> Anfrage absenden
|
||||
|
||||
⚖ Q14
|
||||
|
||||
**Widerrufsfunktion: Knopf im Terminbereich, dann Bestätigung des Formulars**
|
||||
|
||||
> Vertrag widerrufen
|
||||
>
|
||||
> Widerruf bestätigen
|
||||
|
||||
⚖ Q12, Q14
|
||||
|
||||
### 3.11 Anlagen A und B
|
||||
|
||||
- **Anlage A:** Auftragsverarbeitungsvertrag Terminplaner, Fassung 2026-10-05, als PDF `public/rechtliches/avv-terminplaner-2026-10-05.pdf` (Quelle `docs/rechtliches/avv-terminplaner.md`).
|
||||
- **Anlage B:** Leistungsbeschreibung Terminplaner, Fassung 2026-10-05, als PDF `public/rechtliches/leistungsbeschreibung-terminplaner-2026-10-05.pdf` (Quelle `docs/rechtliches/leistungsbeschreibung-terminplaner.md`).
|
||||
- Die Widerrufsbelehrung mit Muster-Widerrufsformular der Funktionen steht in Abschnitt 3.2 vollständig; ihr PDF ist `public/rechtliches/widerrufsbelehrung-funktionen-2026-10-05.pdf`.
|
||||
- **Anlage C** (gerenderte Mails K15 und K16) wird nach der Mail-Überarbeitung des Inhabers nachgereicht (Abschnitt 7.4).
|
||||
|
||||
## 4. Fragen an die Kanzlei
|
||||
|
||||
Jede Frage hat vier Teile. **Frage:** was wir wissen möchten. **Fundstelle im Text:** wo der Text steht (Abschnitt 3, Anlage A oder B, Programm) und, als Kennung H1 bis C8, welche Punkte der früheren Listen offener Rechtsfragen die Frage mit abdeckt (Abschnitt 6.3 ordnet alle Kennungen zu). **Unsere Annahme:** der Stand, mit dem wir arbeiten; bitte bestätigen oder korrigieren. **Alternative:** was wir stattdessen täten. Die Zählung von § 356 BGB haben wir am 2026-10-05 auf gesetze-im-internet.de abgeglichen; für die übrigen Normen bitten wir um Gegenprüfung (Q3). Einordnungen wie „Annahme“ sind keine Rechtsurteile.
|
||||
|
||||
### Q1: Alle Besteller wie Verbraucher
|
||||
|
||||
**Frage.** Der Inhaber hat entschieden, alle Besteller der Einrichtung (149 €) wie Verbraucher zu behandeln: Alle sehen die volle Widerrufsbelehrung mit Muster-Widerrufsformular und alle setzen die Haken zum vorzeitigen Beginn. Die Besteller sind meist Unternehmer (Betriebe mit eigener Website); ob jemand als Verbraucher (§ 13 BGB) oder Unternehmer (§ 14 BGB) bestellt, kann WEBklar nicht verlässlich prüfen (Existenzgründer, Nebenerwerb). Genügt der Satz am Anfang des Dokuments „Das Widerrufsrecht gilt nur für Verbraucher. Wir zeigen die Belehrung und das Muster-Widerrufsformular trotzdem allen, die eine Funktion bestellen.“, damit Unternehmern kein vertragliches Widerrufsrecht entsteht und die Haken für sie nichts Falsches sagen?
|
||||
|
||||
**Fundstelle im Text.** Abschnitt 3.1 (Haken 1 bis 4), Abschnitt 3.2 (Einleitung des Dokuments und Belehrung), Leistungsbeschreibung Nr. 10 Abs. 1 („Verbraucher können die Bestellung der Einrichtung nach der Widerrufsbelehrung widerrufen“).
|
||||
|
||||
**Unsere Annahme.** Ja. Verbraucher haben das gesetzliche Widerrufsrecht, die Haken wirken bei ihnen; Unternehmern räumt die Einleitung kein Widerrufsrecht ein, die Haken schaden ihnen nicht. Die Abwicklung eines Widerrufs ist Handarbeit des Teams (Meldung T09); ob ein Unternehmer erstattet bekommt, entscheidet das Team im Einzelfall.
|
||||
|
||||
**Alternative.** Vor den Haken fragen, ob als Verbraucher oder als Unternehmer bestellt wird (Haken 3 und Belehrung nur für Verbraucher), oder den Terminplaner nur an Unternehmer verkaufen (Bestätigung nach § 14 BGB) und die Belehrung als freiwillige Zusage geben.
|
||||
|
||||
### Q2: Beginn der Widerrufsfrist
|
||||
|
||||
**Frage.** Wann beginnt die Widerrufsfrist der Bestellung? Wir rechnen ab dem Zeitpunkt des Klicks auf „Zahlungspflichtig bestellen · 149 €“ (gespeichert im Nachweis); das Fristende wird nach § 193 BGB auf einen Werktag verschoben. Die Belehrung steht vor dem Klick auf der Bestellseite, auf einem dauerhaften Datenträger (PDF-Anhang der Bestellbestätigung K15) erhält der Kunde sie erst nach dem Zahlungseingang. Beginnt die Frist mit dem Klick (Annahme durch WEBklar), erst mit dem Zahlungseingang (bei SEPA-Lastschrift Tage später) oder erst mit dem Zugang von K15 (§ 356 Abs. 3 BGB)? Gilt dasselbe für die Gäste (Vertragsschluss bei verbindlicher Buchung mit der Bestätigung per Link, bei einer Anfrage mit der Annahme durch den Betrieb)?
|
||||
|
||||
**Fundstelle im Text.** Abschnitt 3.1 (Knopf), Abschnitt 3.2 („ab dem Tag des Vertragsabschlusses“), Abschnitt 3.9 (G02), Abschnitt 2.4; Programm: `withdrawFeature` rechnet mit dem Zeitpunkt des Klicks aus dem Nachweis (ersatzweise mit dem Bestellzeitpunkt), `withdrawalDeadline` mit 14 Tagen.
|
||||
|
||||
**Unsere Annahme.** Der Klick ist der Vertragsschluss: Mit ihm löst der Kunde die Zahlung aus; danach gibt es keine gesonderte Annahmeerklärung von WEBklar. WEBklar kann die Einrichtung nur aus den Gründen von Leistungsbeschreibung Nr. 4 Abs. 3 (Gesundheitsbereich, Q11) ablehnen oder abschalten. Die Belehrung ist vor dem Klick lesbar; K15 bestätigt sie mit den PDFs auf einem dauerhaften Datenträger.
|
||||
|
||||
**Alternative.** Die Frist ab Zahlungseingang oder ab Zugang von K15 rechnen und die Belehrung entsprechend formulieren („ab dem Tag, an dem Sie diese Bestellbestätigung erhalten“).
|
||||
|
||||
### Q3: Zustimmungssatz: Erlöschen, Wertersatz, Kenntnis, dauerhafter Datenträger
|
||||
|
||||
**Frage.** Genügt Haken 3 („Ich verlange ausdrücklich, dass WEBklar vor Ablauf der Widerrufsfrist mit der Einrichtung des Terminplaners beginnt. Mir ist bekannt, dass mein Widerrufsrecht mit der vollständigen Erbringung der Einrichtung erlischt (§ 356 Abs. 5 Nr. 2 BGB). Widerrufe ich … schulde ich einen angemessenen Betrag … (§ 357a Abs. 2 BGB).“)? Bitte prüfen: (a) die Zählung der Norm für das Erlöschen bei Dienstleistungen (§ 356 Abs. 5 Nr. 2 BGB) und für den Wertersatz (§ 357a Abs. 2 BGB); (b) ob das ausdrückliche Verlangen und die Bestätigung der Kenntnis im selben Haken stehen dürfen; (c) ob der Wertersatz-Hinweis im Haken ausreicht oder auch in die Belehrung gehört; (d) ob K15 mit den Texten der Zustimmung, dem Zeitpunkt und den PDFs die nötige Bestätigung auf einem dauerhaften Datenträger ist (die gerenderte Mail folgt als Anlage C nach der Mail-Überarbeitung des Inhabers).
|
||||
|
||||
**Fundstelle im Text.** Abschnitt 3.1 (Haken 3), Abschnitt 3.2 („Hinweise zum Widerruf“), Abschnitt 3.3 (Haken der News), Leistungsbeschreibung Nr. 10 Abs. 2; Programm: `termineConsent` (Register), `onFeaturePaid` (K15, Block „Zustimmungen“ und Anhänge).
|
||||
|
||||
**Unsere Annahme.** Der Haken erfüllt beides: Verlangen, Kenntnis vom Erlöschen und Hinweis auf den Wertersatz in einem Satz mit Zeitpunkt im Nachweis; K15 gibt den Satz zusammen mit der Belehrung auf einem dauerhaften Datenträger wieder. Die Zählung entspricht der Fassung auf gesetze-im-internet.de vom 2026-10-05.
|
||||
|
||||
**Alternative.** Zwei Haken (Verlangen und Kenntnis getrennt); die Bestätigung als eigener, ausdrücklich so benannter Absatz in K15.
|
||||
|
||||
### Q4: News-Altkunden
|
||||
|
||||
**Frage.** Seit 2026-09-30 verkauft WEBklar die News-Pinnwand (30 € einmalig) über denselben Bestellweg wie jetzt den Terminplaner. Bis zur Korrektur sah die Bestellung so aus („3a0b008“ ist die Kennung des Programmstands vor der Korrektur). Alte Fassung (Stand 3a0b008), der Haken:
|
||||
|
||||
> Ich verlange ausdrücklich, dass WEBklar vor Ablauf der Widerrufsfrist mit dem Einbau beginnt. Mir ist bekannt, dass mein Widerrufsrecht erlischt, sobald der Einbau vollständig erbracht ist (§ 356 Abs. 4 BGB).
|
||||
|
||||
Alte Fassung (Stand 3a0b008), Leistungsbeschreibung der News-Pinnwand, Abschnitt 6 „Widerrufsrecht“ (nur diese zwei Absätze, keine gesetzliche Musterbelehrung, kein Muster-Widerrufsformular):
|
||||
|
||||
> Sind Sie Verbraucher, können Sie diesen Vertrag innerhalb von 14 Tagen ohne Angabe von Gründen widerrufen. Die Frist beginnt mit dem Vertragsschluss. Zum Widerruf genügt eine eindeutige Erklärung an uns, zum Beispiel über project.webklar.com/widerrufen.html.
|
||||
>
|
||||
> Verlangen Sie ausdrücklich, dass wir vor Ablauf der Widerrufsfrist mit dem Einbau beginnen, müssen Sie uns bei einem Widerruf einen angemessenen Betrag für die bis dahin erbrachte Leistung zahlen. Das Widerrufsrecht erlischt nach § 356 Abs. 4 BGB, wenn wir die Leistung vollständig erbracht haben und Sie vorher ausdrücklich zugestimmt haben, dass wir vor Ablauf der Widerrufsfrist beginnen, und Ihre Kenntnis bestätigt haben, dass Sie Ihr Widerrufsrecht bei vollständiger Vertragserfüllung verlieren.
|
||||
|
||||
Alte Fassung (Stand 3a0b008), Abschnitt 2 derselben Leistungsbeschreibung zum Preis:
|
||||
|
||||
> Der Einbau kostet 30,00 € einmalig, inklusive Umsatzsteuer. Die Nutzung der Pinnwand danach kostet nichts; es entstehen keine laufenden Kosten und kein Abo. Sie bezahlen bei der Bestellung im Kundenportal über Stripe und erhalten eine Rechnung.
|
||||
|
||||
Die Bestellbestätigung K15 enthielt nach unserem Stand weder eine Belehrung noch Anhänge. Der Haken zitierte also § 356 Abs. 4 BGB und enthielt keinen Hinweis auf den Wertersatz (§ 357a Abs. 2 BGB), den die Leistungsbeschreibung nur allgemein nannte; der Preis war „inklusive Umsatzsteuer“ angegeben (richtig ist § 19 UStG, Q19). Lief bei diesen News-Altkunden, soweit sie Verbraucher sind, die Widerrufsfrist nach § 356 Abs. 3 BGB nicht an? Wenn ja: Sollen wir die Belehrung per Mail nachholen (Form, Inhalt, Zeitpunkt, Hinweis zum Wertersatz), oder genügt es, nur künftig die neuen Texte zu verwenden? Was gilt für bereits eingebaute und freigeschaltete News-Pinnwände (Erlöschen des Widerrufsrechts, Wertersatz)? Die Zahl der betroffenen Fälle liefert der Inhaber vor der Übergabe: [Anzahl der Bestellungen seit 2026-09-30 mit dem alten Text].
|
||||
|
||||
**Fundstelle im Text.** Abschnitt 3.3 (neuer und früherer Haken), Abschnitt 3.2 (Belehrung der Funktionen, die die News künftig ebenfalls nutzt); Programm: `config.features.news` (neuer Haken; bestehende Bestellungen behalten den damals gespeicherten Text und die Fassung). Die alten Texte stammen aus dem Programmstand 3a0b008 (`server/config.js`, `public/vertragsunterlagen/funktionen-leistungsbeschreibung.html`). Die Leistungsbeschreibung der News-Pinnwand vom 30.09.2026 liegt unverändert unter `public/vertragsunterlagen/funktionen-leistungsbeschreibung-2026-09-30.html` (unveränderte Kopie des Standes 3a0b008, durch eine Prüfsumme gesichert), weil Bestellungen seit dem 30.09.2026 auf ihr beruhen; die geltende Datei (Stand 05.10.2026) verweist am Anfang auf sie.
|
||||
|
||||
**Unsere Annahme.** Nach unserer Lesart (kein Rechtsurteil) kann die Frist bei fehlender ordnungsgemäßer Belehrung nach § 356 Abs. 3 BGB nicht angelaufen sein. Der Inhaber entscheidet nach Ihrem Befund: Belehrung nur nachholen, wenn die Frist danach nicht lief; sonst gelten die neuen Texte nur für künftige Bestellungen.
|
||||
|
||||
**Alternative.** Allen News-Altkunden vorsorglich die Belehrung samt Muster-Formular per Mail schicken (mit dem Hinweis, dass die Frist damit beginnt), unabhängig vom Befund; oder, wenn die Kanzlei keinen Handlungsbedarf sieht, nichts tun.
|
||||
|
||||
### Q5: Wertersatz: Anteil am Gesamtpreis oder Stundenaufwand
|
||||
|
||||
**Frage.** Die Muster-Belehrung verlangt beim Widerruf einen angemessenen Betrag, der dem Anteil der bis zur Unterrichtung erbrachten Dienstleistungen im Vergleich zum Gesamtumfang entspricht (§ 357a Abs. 2 BGB). Unsere „Hinweise zum Widerruf“ sagen: „Der Betrag bemisst sich als Anteil am Gesamtpreis; Maß ist der im Arbeitsblatt erfasste Aufwand für die Einrichtung.“ Intern war ursprünglich eine Berechnung „nach Stundenaufwand“ vorgesehen. Ist diese Berechnung (Anteil an 149 € nach dem erfassten Aufwand im Verhältnis zum Gesamtaufwand der Einrichtung dieses Projekts) zulässig, oder muss der Gesamtumfang vorab festgelegt werden, damit der Anteil bestimmbar ist?
|
||||
|
||||
**Fundstelle im Text.** Abschnitt 3.1 (Haken 3), Abschnitt 3.2 (Muster-Belehrung, Absatz „Haben Sie verlangt …“, und „Hinweise zum Widerruf“, dritter Punkt), Leistungsbeschreibung Nr. 10 Abs. 2; Mails K04 und T09; Programm `withdrawFeature`.
|
||||
|
||||
**Unsere Annahme.** Anteilig am Preis (nicht Stundensatz mal Zeit). Das Maß der erbrachten Leistung ist der im Arbeitsblatt des Einrichtungs-Tickets erfasste Aufwand, der Gesamtumfang der Aufwand der Einrichtung dieses Projekts nach dem Ticket. Eine feste Gesamtstundenzahl steht nicht im Vertrag.
|
||||
|
||||
**Alternative.** Den Gesamtumfang in der Leistungsbeschreibung festlegen (zum Beispiel feste Schritte oder eine Stundenzahl) und den Wertersatz nach erledigten Schritten bemessen; oder bei Widerruf nach Einbaubeginn pauschal einen festen Anteil ansetzen.
|
||||
|
||||
### Q6: Volle Erstattung, wenn das Abo vor der Freischaltung endet
|
||||
|
||||
**Frage.** Leistungsbeschreibung Nr. 8 Abs. 5 lautet: „Endet das Wartungs-Abo, bevor WEBklar den Terminplaner freigeschaltet hat, erstattet WEBklar den Einrichtungspreis vollständig.“ Ist die Klausel wirksam und klar (§ 307 BGB), auch wenn WEBklar den Einbau schon begonnen oder fertiggestellt hat und auch wenn der Kunde das Abo selbst beendet? Außerdem: Nr. 8 Abs. 4 sagt, dass WEBklar den Terminplaner bei einem neuen Wartungs-Abo innerhalb der 30 Tage wieder einschaltet. Das Programm tut das nur für den Abschaltgrund „Abo beendet“ (`abo_ende`, nach Freischaltung), nie für `abo_ende_vor_einrichtung`: Nach der Erstattung bleibt der Terminplaner aus; ob und wie er wieder eingerichtet wird, entscheidet das Team im Einzelfall. Muss der Text das sagen?
|
||||
|
||||
**Fundstelle im Text.** Abschnitt 3.4 (Nr. 8 Abs. 4 bis 6), Abschnitt 2.3 Punkt 4; Programm: `gleicheAboAb` (Grund `abo_ende_vor_einrichtung`, Meldung T19 an das Team, keine automatische Rückkehr), Mail K22 Block „vor_einrichtung“.
|
||||
|
||||
**Unsere Annahme.** Die Klausel begünstigt den Kunden und ist unproblematisch. Die Rückkehr gilt nur für den Fall nach der Freischaltung; für den Erstattungsfall ist sie nicht gewollt, weil die Erstattung voll geschuldet bleibt.
|
||||
|
||||
**Alternative.** Die Erstattung auf den Fall beschränken, dass WEBklar noch nicht mit dem Einbau begonnen hat (sonst Wertersatz); Nr. 8 Abs. 4 um den Satz ergänzen, dass die Rückkehr nicht gilt, wenn der Einrichtungspreis erstattet wurde.
|
||||
|
||||
### Q7: Ende mit dem Abo, auch bei Wechsel und Widerruf; Ausnahme für Pilot und Kulanz
|
||||
|
||||
**Frage.** (a) Leistungsbeschreibung Nr. 8 Abs. 1 lässt den Terminplaner mit dem Wartungs-Abo enden, auch beim Wechsel ins Hosting-Abo und beim Widerruf des Abschlusses oder eines Wechsels ins Wartungs-Abo. Ist diese Kopplung zulässig, obwohl der Kunde die Einrichtung einmalig bezahlt hat und sie ein eigener Vertrag ist? Wie verhält sich der Widerruf des Abos zum Einrichtungsvertrag, insbesondere wenn der Terminplaner schon freigeschaltet ist und deshalb nichts erstattet wird? (b) Ausnahme: Terminplaner, die das Team ohne Bestellung freigeschaltet hat (Pilot, Kulanz; im Programm `unlockedByPurchase` nicht wahr), nimmt der Abgleich aus und zählt sie im Tagesbericht als „ausgenommen“; sie laufen weiter, bis WEBklar sie abschaltet. Genügt der Satz „WEBklar schaltet ihn spätestens am folgenden Tag ab“ trotzdem, oder braucht es eine Klausel, die diese Kulanzfälle nennt? (c) Der Abgleich schaltet bei Lesefehlern nichts ab und versucht es am nächsten Tag; ein Abo im Status „Zahlungsverzug“ gilt als laufend. Ist „spätestens am folgenden Tag“ dann zu streng gesagt?
|
||||
|
||||
**Fundstelle im Text.** Abschnitt 3.4 (Nr. 8 Abs. 1 und 6, Nr. 10), Abschnitt 3.1 (Haken 4: „der Terminplaner endet mit dem Wartungs-Abo“), Abschnitt 2.3, Abschnitt 2.1 (Freischaltung ohne Bestellung); Programm: `gleicheAboAb`, `endeTermineFuerKunde`.
|
||||
|
||||
**Unsere Annahme.** Die Kopplung ist zulässig, weil der Terminplaner nur mit einem Wartungs-Abo betrieben wird und der Kunde das in Haken 4 liest. Kulanzfälle sind keine Verträge über die Einrichtung; ihre Ausnahme braucht keine Klausel. „Spätestens am folgenden Tag“ beschreibt den Normalfall.
|
||||
|
||||
**Alternative.** Nr. 8 Abs. 1 auf „in der Regel am folgenden Tag“ abschwächen; eine Kulanzklausel („Terminplaner, die WEBklar ohne Bestellung freigeschaltet hat, schaltet WEBklar nach eigenem Ermessen ab“) aufnehmen; beim Widerruf des Abos nach Freischaltung eine anteilige Erstattung prüfen.
|
||||
|
||||
### Q8: Haftung für verpasste Termine; Erstattung bei Einstellung
|
||||
|
||||
**Frage.** (a) Leistungsbeschreibung Nr. 7 Abs. 3 (Abschnitt 3.4) beschränkt die Haftung für entgangenen Gewinn und Folgeschäden aus verpassten Terminen auf den Rahmen von § 13 Abs. 1 und 2 der Abo-Bedingungen (unbeschränkt bei Vorsatz, grober Fahrlässigkeit, Verletzung von Leben, Körper oder Gesundheit; bei leicht fahrlässiger Verletzung wesentlicher Pflichten begrenzt auf den vertragstypischen, vorhersehbaren Schaden; im Übrigen ausgeschlossen). Ist die Klausel gegenüber Verbrauchern mit § 309 Nr. 7 BGB vereinbar, und ist die Verweisung hinreichend transparent? (b) Braucht es gegenüber Unternehmern einen Höchstbetrag (etwa den Jahresbetrag des Wartungs-Abos)? (c) Nr. 9 Abs. 4 schließt eine Einstellung ohne Grund aus. Wann WEBklar den Terminplaner beenden oder aussetzen darf, regeln Nr. 9 Abs. 2 und 3:
|
||||
|
||||
> (2) WEBklar darf den Terminplaner für eine Website mit einer Frist von vier Wochen beenden oder für alle Kunden mit einer Frist von drei Monaten einstellen, wenn
|
||||
> - ein Gesetz, eine Gerichtsentscheidung oder eine Vorgabe einer Aufsichtsbehörde den Betrieb in der bisherigen Form unzulässig macht oder nur mit wesentlich anderer Technik erlaubt und der Kunde einer dafür nötigen neuen Fassung nicht zustimmt (AVV Terminplaner Nummer 12), oder
|
||||
> - eine Sicherheitslücke sich nicht anders beheben lässt.
|
||||
>
|
||||
> Die Mitteilung erfolgt in Textform und nennt den Grund, die Merkmale der Änderung, den Zeitpunkt und bei Verbrauchern das Recht, den Vertrag nach § 327r Abs. 3 BGB zu beenden, mit seiner Frist.
|
||||
>
|
||||
> (3) Ist der Weiterbetrieb aus einem Grund nach Absatz 2 schon vor Ablauf der Frist nicht zulässig, darf WEBklar den Terminplaner ohne Vorankündigung aussetzen, solange das nötig ist. WEBklar informiert den Kunden darüber unverzüglich in Textform mit dem Grund und nimmt den Betrieb wieder auf, sobald das zulässig und technisch möglich ist.
|
||||
|
||||
Zur Erstattung des Einrichtungspreises, wenn WEBklar den Terminplaner danach beendet, schweigt der Text. Braucht es eine (anteilige) Erstattung?
|
||||
|
||||
**Fundstelle im Text.** Abschnitt 3.4 (Nr. 7 und Nr. 9 Abs. 4), Leistungsbeschreibung Nr. 9 Abs. 2 und 3 (oben zitiert), Abo-Bedingungen § 13 (`public/abo-bedingungen.html`).
|
||||
|
||||
**Unsere Annahme.** Die Verweisung auf § 13 genügt; ein Höchstbetrag ist nicht nötig, weil § 13 Abs. 2 die Haftung schon auf den vorhersehbaren Schaden begrenzt. Bei Einstellung aus den Gründen von Nr. 9 Abs. 2 erstattet WEBklar nichts, weil der Terminplaner bis dahin lief; die Rechte aus § 327r BGB bleiben.
|
||||
|
||||
**Alternative.** Einen Höchstbetrag gegenüber Unternehmern festlegen; bei Einstellung durch WEBklar den Einrichtungspreis anteilig für die Restlaufzeit erstatten.
|
||||
|
||||
### Q9: 30 Tage nach dem Abschalten: Anker, Rückkehr, Sicherungen
|
||||
|
||||
**Frage.** Der Terminplaner endet mit dem Wartungs-Abo; WEBklar schaltet ihn spätestens am folgenden Tag ab und löscht alle Termindaten und Einstellungen 30 Tage nach dem Abschalten. Bis dahin kann der Kunde die Termine im Kundenportal ansehen und als CSV-Datei exportieren; beginnt ein neues Wartungs-Abo, schaltet WEBklar ohne Kosten mit den vorhandenen Daten wieder ein (Rückkehr, nur beim Abschaltgrund „Abo beendet“, `abo_ende`). Fragen: (a) Ist der Anker (Zeitpunkt des Abschaltens, `disabledAt`, nicht der Tag des Abo-Endes) und die Frist von 30 Tagen mit Art. 28 Abs. 3 lit. g DSGVO vereinbar? (b) Darf die Rückkehr ohne neue Zustimmung zur Fassung automatisch erfolgen? (c) Gelöschte Daten bleiben in der täglichen Vollsicherung bis zu sieben Tage (Rückspielen holt der Löschlauf nach): genügt das? (d) Der tägliche Löschlauf löscht frühestens nach Ablauf der jeweiligen Frist, nicht sekundengenau; „48 Stunden“ und „30 Tage“ sind frühestmögliche Zeitpunkte (H4, A3). (e) Terminanfragen, die der Kunde bis zum angefragten Terminbeginn nicht beantwortet, gelten ab dann als abgelehnt, und die 30 Tage laufen ab diesem Zeitpunkt (AVV Nr. 9 Abs. 2). (f) Der Satz im Datenschutz-Absatz der Kunden-Website („Wird die Online-Terminbuchung abgeschaltet, zum Beispiel weil unser Vertrag mit WEBklar endet, werden alle Termindaten 30 Tage nach dem Abschalten gelöscht.“) und der Satz in Abschnitt 6c stimmen mit dem Programm überein, seit der tägliche Abgleich das Abschalten übernimmt. (g) Das Einrichtungs-Ticket, in dem das Team die Einrichtung als Arbeitsnachweis festhält, bleibt nach der Löschung mit einer Kurzfassung der Einstellungen bestehen (Betriebsangaben, Namen der Leistungen, die ersten acht Namen der Personen oder Plätze, Wünsche des Kunden zum Einbau; keine Daten von Gästen). Leistungsbeschreibung Nr. 8 Abs. 3 und AVV Nr. 9 Abs. 6 sagen das; die Datenschutzerklärung (Abschnitt 6) nennt die Arbeitsnachweise und die Aufbewahrung bis zum Ablauf der Verjährungsfrist. Ist das neben der Löschpflicht des Art. 28 Abs. 3 lit. g DSGVO zulässig, auch wenn der Kunde die Löschung aller Daten anweist (AVV Nr. 9 Abs. 6)?
|
||||
|
||||
**Fundstelle im Text.** Abschnitt 3.4 (Nr. 8 Abs. 3 und 4), Abschnitt 3.5 (AVV Nr. 9, besonders Abs. 2, 6 und 8), Abschnitt 2.3; frühere Punkte H4, A3, C4 (Sicherungen), C7 (Satz in 6c); Mail K22 und Datenschutzerklärung 6c (Hausrunde); Programm: `LOESCHFRIST_TAGE`, `loescheAbgeschaltete`, `gleicheAboAb`; Einrichtungs-Ticket: `termineTicketDetails`.
|
||||
|
||||
**Unsere Annahme.** Ja. 30 Tage ab dem Abschalten sind angemessen, weil der Kunde in dieser Zeit Termine ansehen und exportieren kann und eine Rückkehr kostenlos ist. Die Rückkehr stellt den früheren Stand wieder her; die zugestimmte Fassung gilt weiter, eine neue Fassung braucht ihre Zustimmung nach AVV Nr. 12. Der Sicherungsüberhang von sieben Tagen folgt aus der täglichen Vollsicherung bei Hetzner (letzte sieben Tagessicherungen) und steht offen im Vertrag.
|
||||
|
||||
**Alternative.** Das Modell der Website-Analyse: Löschung am 61. Tag nach dem Ende, Kopie der Daten auf Verlangen bis zum 46. Tag (AVV Website-Analyse Nr. 9 Abs. 2); oder 30 Tage ab dem Tag des Abo-Endes statt ab dem Abschalten.
|
||||
|
||||
### Q10: AVV Terminplaner: Aufbau, Hetzner, Drittland, Daten, Weisungen
|
||||
|
||||
**Frage.** Bitte den AVV (Anlage A) prüfen; besonders: (a) der Abschluss durch Haken bei der Bestellung (Textform; Art. 28 Abs. 9 DSGVO) mit gespeicherter Fassung, Zeitpunkt, Konto und Person, und durch Zustimmung in Textform bei Freischaltung durch das Team (Q21); (b) die Wahl eines eigenen Vertrags (Geschwister des AVV der Website-Analyse, Vorrang vor Anlage 1 der Abo-Bedingungen) statt eines Rahmen-AVV oder eines Zusatzes, damit Kunden der Website-Analyse nicht neu zustimmen müssen; (c) Nr. 7: Hetzner Online GmbH, Rechenzentrum Falkenstein (Server und Sicherung) und Nürnberg (SMTP-Dienst des Webhostings) und der Satz „Eine Übermittlung in Drittländer findet nicht statt“, auch wenn ein Kunde den geheimen Kalender-Link in einem Kalenderdienst eines Anbieters wie Google oder Apple abonniert (das Abonnement richtet der Kunde ein, WEBklar übermittelt dorthin nichts) (A2); (d) Nr. 7 Abs. 4: Stripe ist kein Unterauftragsverarbeiter, weil keine Daten von Gästen oder Beschäftigten an Stripe gehen; (e) Aufruf des Widgets: IP-Adresse und Browser-Kennzeichen erhält der Server technisch bei jedem Abruf, Server-Protokolle 14 Tage (Art. 6 Abs. 1 lit. f DSGVO) (A1); im Knopfmodus lädt das Widget die Konfiguration schon beim Laden der Seite (A6, C8), vom selben Server wie das Skript; (f) Beschäftigtendaten: Namen, Zeiten und der geheime Kalender-Link; der Kunde informiert seine Beschäftigten (C3); (g) Nr. 3 Abs. 1: Absagegrund und Vermerke (Freitext, auch mit dem Namen aus einer Widerrufserklärung) sowie Preis, Zahlart, Zahlungsstatus und die Angabe, ob als Verbraucher gebucht wurde; Nr. 2 Abs. 3: Zählungen ohne Personenbezug, die WEBklar für den sicheren Betrieb braucht, sind keine eigenen Zwecke; (h) Nr. 4 Abs. 2 und Nr. 9 Abs. 6: Löschung auf Weisung in Textform „unverzüglich“, ohne feste Frist.
|
||||
|
||||
**Fundstelle im Text.** Abschnitt 3.5 (Vorbemerkung Abs. 1 und 2, Nr. 3 Abs. 1, Nr. 4 Abs. 2, Nr. 7, Nr. 9), Anlage A vollständig mit der Anlage der technischen und organisatorischen Maßnahmen; frühere Punkte A1, A2, A6, C3, C8; Programm: `baueNachweis`, `datenschutzAbsatz`.
|
||||
|
||||
**Unsere Annahme.** Der AVV ist vollständig und passt zum Programm (Datenarten, Löschfristen, Unterauftragsverarbeiter sind durch Tests an den Code gebunden). Eine Drittlandübermittlung durch WEBklar findet nicht statt; das Kalender-Abonnement liegt in der Verantwortung des Kunden. „Unverzüglich“ genügt für die Löschung auf Weisung.
|
||||
|
||||
**Alternative.** Ein Rahmen-AVV mit einer Anlage je Funktion (Abschnitt 6.2); im AVV einen Hinweis auf das Kalender-Abonnement bei Drittanbietern aufnehmen; für die Löschung auf Weisung eine feste Frist nennen (zum Beispiel 14 Tage).
|
||||
|
||||
### Q11: Gesundheits-Ausschluss
|
||||
|
||||
**Frage.** Der Terminplaner ist nicht für den Gesundheitsbereich bestimmt (Art. 9 DSGVO). Schutzmaßnahmen: die Branchenfrage im Fragebogen (der Gesundheitsbereich ist wählbar, die Bestellung dann aber gesperrt), Haken 2 mit Aufzählung (Arzt-, Zahnarzt-, Psychotherapie-, Heilpraktiker- oder Physiotherapiepraxis), eine weiche Stichwortprüfung über Betriebsname, Leistungen und Felder, die das Team warnt, das Recht in Leistungsbeschreibung Nr. 4 Abs. 3, die Einrichtung abzulehnen oder den Terminplaner dort abzuschalten, der Hinweis „Bitte keine Gesundheitsangaben“ über jedem Freitextfeld und im Datenschutz-Hinweis (A5), die Pflicht des Kunden in AVV Nr. 11 Abs. 4. Reicht das, damit WEBklar als Auftragsverarbeiter keine besonderen Kategorien verarbeitet? Und: Leistungsbeschreibung Nr. 4 Abs. 3 sagt nichts zur Erstattung, wenn WEBklar die Einrichtung wegen des Gesundheitsbereichs ablehnt oder abschaltet; muss der Text das regeln?
|
||||
|
||||
**Fundstelle im Text.** Abschnitt 3.1 (Haken 2), Abschnitt 3.4 (Nr. 4 Abs. 3), Abschnitt 3.7 (Hinweis), Anlage A Nr. 3 Abs. 3 und Nr. 11 Abs. 4; früherer Punkt A5; Programm: `BRANCHEN`, `gesundheitsWarnungen`, `bestellpruefung`.
|
||||
|
||||
**Unsere Annahme.** Die Kombination aus Frage, Haken, Warnung und Ablehnungsrecht genügt; die Aufzählung im Haken ist beispielhaft. Lehnt WEBklar die Einrichtung ab, erstattet WEBklar den Preis aus Kulanz (nach Widerruf oder Kulanz, kein Satz im Text).
|
||||
|
||||
**Alternative.** Eine ausdrückliche Erstattungsklausel für die Ablehnung; die Aufzählung im Haken durch „Heilkunde und Gesundheitsversorgung“ ersetzen; eine zweite Bestätigung im Fragebogen.
|
||||
|
||||
### Q12: Gäste-Belehrung: Abweichungen vom Muster und Gesetzlichkeitsfiktion
|
||||
|
||||
**Frage.** Die Belehrung für Gäste (Abschnitt 3.6) beruht auf dem Muster der Anlage 1 zu Art. 246a § 1 Abs. 2 Satz 2 EGBGB, angepasst für einen Dienstleistungsvertrag. Genügt sie für die Gesetzlichkeitsfiktion, obwohl sie abweicht? (a) Die Klammer zu den Lieferkosten im Absatz „Folgen des Widerrufs“ bleibt stehen, weil der Gestaltungshinweis keine Streichung bei Dienstleistungen vorsieht (B1); dasselbe gilt für die Belehrung der Funktionen (Abschnitt 3.2). (b) Die Widerrufsfunktion nennt statt einer Internetadresse einen „anderen geeigneten Hinweis“: die Schaltfläche „Vertrag widerrufen“ im Terminbereich und den Link „Termin verwalten“ in der Bestätigungs-E-Mail; die Adresse der Seite fehlt (B5). (c) „… dass die Dienstleistungen … beginnen soll“ steht im Singular des Gesetzestexts (B2). (d) Das Widget zeigt die Belehrung aufklappbar in der Zusammenfassung vor dem Knopf, die Bestätigungs-E-Mail enthält sie als Text (H1). (e) Die Fiktion gilt nur, wenn das Muster inhaltlich und gestalterisch dem Gesetz entspricht; Zeilenumbrüche, Überschriften und die Aufzählung im Formular sind unsere Darstellung (B6).
|
||||
|
||||
**Fundstelle im Text.** Abschnitt 3.6, Abschnitt 3.2 (Lieferkosten-Klammer und Widerrufsfunktion mit Adresse der Widerrufsseite bei den Funktionen), Abschnitt 3.9, Abschnitt 3.10 („Vertrag widerrufen“); frühere Punkte H1, B1, B2, B5, B6; Programm: `widerrufsbelehrung`, Muster in `docs/rechtliches/quellen/`.
|
||||
|
||||
**Unsere Annahme.** Ja: Die Abweichungen sind die Ausfüllung der Lücken, die das Muster vorsieht, oder der Gesetzestext selbst; die aufklappbare Darstellung ist zulässig, weil sie vor dem Absenden vollständig erreichbar ist.
|
||||
|
||||
**Alternative.** Die Klammer zu den Lieferkosten streichen; die Widerrufsfunktion zusätzlich mit der Adresse der Seite des Betriebs nennen; die Belehrung ohne Aufklappen offen anzeigen; „beginnen sollen“ im Plural.
|
||||
|
||||
### Q13: Gäste: Haken zum vorzeitigen Beginn und Wertersatz
|
||||
|
||||
**Frage.** Der Haken „Vorzeitiger Beginn“ (Abschnitt 3.7) wird nur Verbrauchern bei Leistungen mit Preis gezeigt, wenn der Termin vor Ablauf der Widerrufsfrist beginnt; bei Anfragen mit Preis immer, weil der Tag der Annahme nicht feststeht. Die Frist rechnen wir mit 14 Tagen ab dem Tag nach dem Vertragsschluss im Berliner Kalender (§§ 187, 188, 193 BGB), gerechnet ab Buchungszeitpunkt plus Haltezeit. (a) Genügt der Haken (Verlangen, Kenntnis, Wertersatz) in dieser Form? (b) Der Absatz zum Wertersatz steht immer in der Belehrung, auch wenn der Gast keinen vorzeitigen Beginn verlangt hat, weil das Muster ihn für Dienstleistungen vorsieht (B4). Ist das unschädlich? (c) Liegt der Termin nach Ablauf der Frist, zeigen wir keinen Haken: stimmt das?
|
||||
|
||||
**Fundstelle im Text.** Abschnitt 3.7 (Haken „Vorzeitiger Beginn“), Abschnitt 3.6 (Absatz „Haben Sie verlangt …“); früherer Punkt B4; Programm: `ZUSTIMMUNG_VORZEITIG`, `brauchtZustimmungVorzeitig`.
|
||||
|
||||
**Unsere Annahme.** Ja zu (a) bis (c): Der Haken wirkt nur, wo ein Widerrufsrecht noch besteht und die Leistung vor Fristende beginnt; der Muster-Absatz zum Wertersatz bleibt unverändert, um die Fiktion nicht zu gefährden.
|
||||
|
||||
**Alternative.** Den Haken bei jeder Buchung mit Preis zeigen; den Wertersatz-Absatz nur bei gesetztem Haken in die Belehrung aufnehmen (Abweichung vom Muster).
|
||||
|
||||
### Q14: Anfrage mit Festpreis, Bestätigungslink und Seitenknopf als Bestellung
|
||||
|
||||
**Frage.** Wann ist die Buchung eines Gastes eine Bestellung mit Zahlungspflicht im Sinne von § 312j Abs. 3 BGB? Unser Ablauf: Bei einer verbindlichen Buchung mit Preis trägt der Knopf im Widget die Aufschrift „Zahlungspflichtig buchen“; der Gast bestätigt dann per Link in einer E-Mail, und die Bestätigungsseite hat den Knopf „Zahlungspflichtig bestätigen“; erst damit kommt der Vertrag zustande. Bezahlt wird vor Ort. Bei einer Terminanfrage, auch mit Festpreis, heißen die Knöpfe „Termin anfragen“ und „Anfrage absenden“, der Vertrag kommt erst mit der Annahme durch den Betrieb zustande. Ohne Preis lauten die Knöpfe „Termin buchen“ und „Termin bestätigen“. Genügt das? Muss der Knopf einer Anfrage mit Festpreis ebenfalls „zahlungspflichtig“ heißen? Ist der Seitenknopf auf der Bestätigungsseite die maßgebliche Bestellung?
|
||||
|
||||
**Fundstelle im Text.** Abschnitt 3.10 (Knopftexte), Abschnitt 2.2 Punkt 2; Programm: `knopfText`.
|
||||
|
||||
**Unsere Annahme.** Ja: Die zahlungspflichtige Handlung ist die Bestätigung auf der Bestätigungsseite; bei einer Anfrage entsteht die Zahlungspflicht erst mit der Annahme, die der Betrieb erklärt und nicht der Gast.
|
||||
|
||||
**Alternative.** Auch den Knopf einer Anfrage mit Festpreis „Zahlungspflichtig anfragen“ nennen; bei einem Preis den Preis am Knopf zeigen.
|
||||
|
||||
### Q15: Einstufung als Freizeitleistung und Hinweis im Portal
|
||||
|
||||
**Frage.** Im Kundenportal stuft der Betrieb selbst ein, ob seine Termine Freizeitveranstaltungen zu einem festen Termin sind (§ 312g Abs. 2 Nr. 9 BGB); kreuzt er das an, zeigt der Terminplaner keine Widerrufsbelehrung, kein Formular und keine Widerrufsfunktion, und die Bestätigung nennt den Satz aus Abschnitt 3.9. Der Hinweis dazu im Portal lautet:
|
||||
|
||||
> Nur ankreuzen, wenn deine Termine Freizeitveranstaltungen zu einem festen Termin sind (§ 312g Abs. 2 Nr. 9 BGB), zum Beispiel ein einzelner Tanzabend, ein Kurs oder ein Escape Room. Nach unserer Einschätzung gilt bei Friseur, Kosmetik, Nagel- und Fußpflege, Tattoo, Fotoshootings, Coaching und Beratung in der Regel ein Widerrufsrecht; bei Massage und Wellness ist die Einordnung unklar, im Zweifel gilt ein Widerrufsrecht. Kreuzt du es an, zeigt der Terminplaner keine Widerrufsbelehrung. Kreuzt du es zu Unrecht an, können deine Gäste noch bis zu 12 Monate und 14 Tage widerrufen. Das ist keine Rechtsberatung: Im Zweifel nicht ankreuzen oder dich rechtlich beraten lassen.
|
||||
|
||||
Ist diese Einschätzung vertretbar, vor allem für Fotoshootings, Coaching, Beratung, Tattoo und Massage? Genügt „Das ist keine Rechtsberatung“, oder haftet WEBklar, wenn ein Betrieb zu Unrecht ankreuzt und Gäste noch bis zu 12 Monate und 14 Tage widerrufen können? Ein zweiter Hinweis im Portal (`WIDERRUF_HINWEIS`) erklärt dem Betrieb, dass Privatpersonen eine Buchung 14 Tage nach Vertragsschluss widerrufen können (bei Anfragen zählt die Annahme), dass das Widerrufsrecht seiner Stornoregel vorgeht, dass bei vorzeitigem Beginn Wertersatz geschuldet sein kann (§ 357a Abs. 2 BGB) und dass der Terminplaner Belehrung und Knopf „Vertrag widerrufen“ automatisch zeigt.
|
||||
|
||||
**Fundstelle im Text.** Abschnitt 3.9 (Satz „kein Widerrufsrecht“), Leistungsbeschreibung Nr. 4 Abs. 8 und Nr. 11 Abs. 1 (die Einstufung trägt der Kunde); Programm: `FREIZEIT_HINWEIS`, `WIDERRUF_HINWEIS`, `zeigtWiderruf`.
|
||||
|
||||
**Unsere Annahme.** Friseur, Kosmetik, Nagel- und Fußpflege, Tattoo, Fotoshootings, Coaching und Beratung fallen in der Regel nicht unter die Ausnahme; bei Massage und Wellness ist die Einordnung unklar, im Zweifel besteht ein Widerrufsrecht. Der Hinweis stellt die Einstufung klar dem Betrieb zu.
|
||||
|
||||
**Alternative.** Die Einstufung nicht dem Betrieb überlassen: Der Terminplaner zeigt die Belehrung immer, die Freizeit-Ausnahme gibt es nicht; oder die Beispiele im Hinweis streichen.
|
||||
|
||||
### Q16: Storno-Muster (50 Prozent, 24 Stunden) und Wegfall bei kostenlosen Leistungen
|
||||
|
||||
**Frage.** Die Vorlagen schlagen dem Betrieb Stornobedingungen vor, zum Beispiel für Salon, Kosmetik und Beratung:
|
||||
|
||||
> Sie können Ihren Termin bis 24 Stunden vorher kostenlos absagen oder verschieben (Link in der Bestätigungsmail). Danach oder bei Nichterscheinen berechnen wir für den freigehaltenen Termin 50 % des Preises. Dem Kunden bleibt der Nachweis vorbehalten, dass uns kein oder ein geringerer Aufwand entstanden ist. Ihr gesetzliches Widerrufsrecht bleibt unberührt.
|
||||
|
||||
(Bei Tattoo und Fotografie 48 Stunden.) Im Portal steht neben dem Textfeld der Hinweis:
|
||||
|
||||
> Das ist ein Muster, kein Rechtstext. Bitte prüfe es und passe es an deine Bedingungen an.
|
||||
|
||||
(a) Hält das Muster einer Kontrolle nach § 307 BGB (und § 309 Nr. 5 BGB gegenüber Verbrauchern) stand, soweit es als Vorschlag für den Betrieb dient? (b) Hat keine Leistung einer Vorlage einen Preis (Vorlagen „handwerk“ und „sonstiges“), gibt es keinen Vorschlag. Bietet ein Betrieb einzelne kostenlose Leistungen neben Leistungen mit Preis an, bleibt sein Stornotext stehen, aber der Pflicht-Haken „Ich habe die Stornobedingungen gelesen.“ entfällt bei den kostenlosen Leistungen. Genügt das? (c) Die Bestätigungs-E-Mail G02 zeigt den Stornotext immer, wenn der Betrieb einen hinterlegt hat; ein Betrieb kann auch dann einen Text eingeben, wenn er kostenlose Leistungen anbietet, und dann steht er auch in der Bestätigung kostenloser Buchungen. Der Satz „Absagen richten sich nach den Stornobedingungen“ im Block „kein_widerruf“ (Abschnitt 3.9) steht dagegen bei jeder Buchung, die der Betrieb als Freizeit-Dienstleistung einstuft, also auch bei kostenlosen Buchungen und auch ohne hinterlegten Stornotext. Die Einbauanleitung für Mitarbeiter nennt den Stornotext unabhängig vom Preis. Ist das bei kostenlosen Leistungen irreführend? Wir wollen für G02 einen neutralen Satz festlegen (Checkliste, Abschnitt 7.4).
|
||||
|
||||
**Fundstelle im Text.** Abschnitt 3.7 (Haken „Stornobedingungen“), Abschnitt 3.9 (G02); Programm: `stornoText`, `STORNO_PROZENT`, `pruefeEingabe`, `STORNO_MUSTER_HINWEIS`.
|
||||
|
||||
**Unsere Annahme.** Der Vorschlag ist ein Muster mit Nachweisvorbehalt und dem Hinweis auf das Widerrufsrecht; der Betrieb prüft und passt ihn an. Bei kostenlosen Leistungen gibt es nichts zu stornieren, der Haken ist dort nicht nötig. Der Satz in G02 wird für Buchungen ohne Preis neutral gefasst.
|
||||
|
||||
**Alternative.** Keinen Stornovorschlag mitliefern; oder 50 Prozent durch eine feste Gebühr oder einen Staffelsatz ersetzen.
|
||||
|
||||
### Q17: Datenschutz- und Impressum-Link; Widerrufssatz am Erinnerungshaken
|
||||
|
||||
**Frage.** (a) Der Datenschutz-Hinweis am Buchungsformular verweist auf die Datenschutzerklärung des Betriebs; das Widget ließe den Link sonst still weg. Deshalb verlangt das Bestell-Gate vor der Bestellung die Adressen von Impressum und Datenschutzerklärung (H3). Ist die Pflicht ausreichend, oder müssen Links zu Datenschutzerklärung und Impressum direkt am Haken stehen? (b) Der Erinnerungshaken endet mit „Diese Einwilligung kann ich jederzeit widerrufen.“ (Art. 7 Abs. 3 DSGVO), ohne Widerrufsweg und ohne Link am Haken; die Einzelheiten stehen im Datenschutz-Absatz (H2). Genügt das?
|
||||
|
||||
**Fundstelle im Text.** Abschnitt 3.7 (Haken „Erinnerung“), Abschnitt 3.8 (Datenschutz-Hinweis); frühere Punkte H2, H3; Programm: `ZUSTIMMUNG_ERINNERUNG`, `formularHinweis`, `bestellpruefung`.
|
||||
|
||||
**Unsere Annahme.** Ja: Der Satz am Haken informiert vor der Einwilligung über das Recht zum Widerruf, die Einzelheiten und der Weg stehen im Hinweis und im Datenschutz-Absatz.
|
||||
|
||||
**Alternative.** Den Widerrufsweg („über den Link in der E-Mail oder per Nachricht an {{E-Mail}}“) in den Haken schreiben; Links zu Datenschutzerklärung und Impressum neben den Haken setzen.
|
||||
|
||||
### Q18: Datenschutz-Absatz auf der Kundenseite: Einfügen durch WEBklar im Preis
|
||||
|
||||
**Frage.** Leistungsbeschreibung Nr. 4 Abs. 9: Liegt die Datenschutzerklärung in der von WEBklar betreuten Website, fügt WEBklar beim Einbau den Datenschutz-Absatz für die Online-Terminbuchung ein, das gehört zur Einrichtung und wird nicht auf das Stundenkontingent angerechnet; sonst erhält der Kunde den Absatz als Textbaustein. Der Kunde bleibt für seine Datenschutzerklärung verantwortlich; fällt WEBklar ein offensichtlicher Widerspruch auf, weist WEBklar darauf hin, eine Prüfpflicht entsteht nicht. (a) Ist das Einfügen eines Mustertextes durch WEBklar eine Rechtsdienstleistung (§ 2 RDG), oder bleibt es Technik und Mustertext (Nr. 11 Abs. 1)? (b) Reicht die Abgrenzung der Verantwortung? (c) Dieselbe Regel steht in der Leistungsbeschreibung der Website-Analyse (Nr. 4 Abs. 4); wir wollen sie für beide gleich halten.
|
||||
|
||||
**Fundstelle im Text.** Abschnitt 3.4 (Nr. 4 Abs. 9, Nr. 11 Abs. 1); `docs/rechtliches/leistungsbeschreibung-website-analyse.md` Nr. 4 Abs. 4; Programm: `datenschutzAbsatz`.
|
||||
|
||||
**Unsere Annahme.** Das Einfügen eines unveränderten Mustertextes mit Hinweis ist keine Rechtsdienstleistung im Einzelfall; die Verantwortung bleibt beim Kunden.
|
||||
|
||||
**Alternative.** WEBklar fügt nichts ein und liefert nur den Textbaustein; oder WEBklar fügt ein und der Kunde bestätigt den Text ausdrücklich.
|
||||
|
||||
### Q19: Preisangabe nach § 19 UStG; Preis am Knopf
|
||||
|
||||
**Frage.** (a) Die Einrichtung steht überall sinngemäß als „149 € einmalig, ohne Umsatzsteuer gemäß § 19 UStG“, im Wortlaut so: im Haken 4 als „Gesamtpreis 149 € einmalig (ohne Umsatzsteuer gemäß § 19 UStG)“ (Abschnitt 3.1), in Leistungsbeschreibung Nr. 5 Abs. 2:
|
||||
|
||||
> (2) Die Einrichtung kostet 149 € einmalig je Website-Projekt, ohne Umsatzsteuer gemäß § 19 UStG.
|
||||
|
||||
und in der Bestellbestätigung K15 als Zusatz zur Preiszeile:
|
||||
|
||||
> einmalig, ohne Umsatzsteuer gemäß § 19 UStG; der Terminplaner läuft, solange Dein Wartungs-Abo läuft
|
||||
|
||||
Früher stand bei den News „inklusive Umsatzsteuer“ (Q4); das haben wir korrigiert. Ist die Angabe als Endpreis richtig, solange WEBklar Kleinunternehmer bleibt, und was ist bei Überschreiten der Grenze zu tun? (b) Der Knopf trägt den Preis: „Zahlungspflichtig bestellen · 149 €“ (Abschnitt 3.1). Ist der Zusatz mit § 312j Abs. 3 BGB vereinbar und nötig?
|
||||
|
||||
**Fundstelle im Text.** Abschnitt 3.1 (Haken 4 und Knopf), Abschnitt 3.4 (Nr. 5); Programm: `ustHinweis`, `TERMINE_PREIS`, `termineConsent`.
|
||||
|
||||
**Unsere Annahme.** WEBklar bleibt Kleinunternehmer (der Inhaber bestätigt das, Abschnitt 7.1); der Preis am Knopf schadet nicht, weil die gesetzlichen Worte „zahlungspflichtig bestellen“ unverändert davor stehen.
|
||||
|
||||
**Alternative.** Knopf ohne Preis und den Preis direkt darüber zeigen; bei Überschreiten der Kleinunternehmergrenze Preise und Texte neu fassen (neues Fassungsdatum).
|
||||
|
||||
### Q20: Pflichtinformationen (Art. 246a § 1 EGBGB) als Haken und in K15
|
||||
|
||||
**Frage.** Haken 4 bestätigt, dass der Besteller die Pflichtinformationen gelesen hat: Leistung (Einrichtung des Terminplaners für diese Website: ein Widget an einer Stelle, gestaltet nach dem Fragebogen, mit Einbau und Datenschutz-Absatz), Gesamtpreis, voraussichtliche Dauer, Ende mit dem Wartungs-Abo, und dass er die Widerrufsbelehrung gelesen hat. Genügen Haken 4 zusammen mit der Belehrung, den versionierten Links auf Leistungsbeschreibung und AVV und der Bestellbestätigung K15 (Zustimmungstexte und PDF-Anhänge) den Informationspflichten nach Art. 246a § 1 Abs. 1 EGBGB? Wird zwischen Klick und Zahlung eine neue Fassung veröffentlicht, schickt K15 die Unterlagen der Fassung, der der Kunde zugestimmt hat (der vereinbarten Fassung), nicht die aktuelle; fehlen sie, geht K15 ohne Anhänge und das Team sendet sie von Hand. Ist das ausreichend? Die gerenderte Mail K15 folgt als Anlage C nach der Mail-Überarbeitung des Inhabers.
|
||||
|
||||
**Fundstelle im Text.** Abschnitt 3.1 (Haken 4), Abschnitt 3.4 (Nr. 5), Abschnitt 2.1 Schritt 6; Programm: `k15Rechtsunterlagen`, `onFeaturePaid`, `legalPdfAttachments`.
|
||||
|
||||
**Unsere Annahme.** Ja: Die Informationen stehen vor der Bestellung im Text der Seite und in den Unterlagen; der Haken macht die Kenntnisnahme nachweisbar; K15 gibt sie auf einem dauerhaften Datenträger wieder.
|
||||
|
||||
**Alternative.** Die Pflichtinformationen als eigenen Textblock auf der Bestellseite und in K15 im Wortlaut wiederholen, statt sie im Haken zu bündeln.
|
||||
|
||||
### Q21: Freischaltung durch das Team mit Zustimmung in Textform; K16 mit PDFs
|
||||
|
||||
**Frage.** Ohne Bestellung (Kulanz, Pilot) schaltet das Team einen Terminplaner in der Betriebsart `on` (alle Kunden mit Wartungs-Abo, Abschnitt 1) nur frei, wenn der Kunde vorher in Textform (Chat im Kundenportal oder E-Mail) zugestimmt hat; Pilotadressen sind davon ausgenommen. Das Portal speichert als Nachweis (ohne Stripe-Sitzung, IP und Browser; Konto ist der Mitarbeiter) diesen Satz, mit Fassung, Datum und Weg des Falls:
|
||||
|
||||
> Der Kunde hat der Leistungsbeschreibung Terminplaner und dem Auftragsverarbeitungsvertrag Terminplaner (Fassung \<Fassung>) am \<TT>.\<MM>.\<JJJJ> per Chat im Kundenportal in Textform zugestimmt und bestätigt, dass sein Angebot nicht im Gesundheitsbereich liegt.
|
||||
|
||||
Bei der Zustimmung per E-Mail steht „per E-Mail“ statt „per Chat im Kundenportal“. Für den Chat liefert das Ticketsystem dem Mitarbeiter diese Textvorlage zum Kopieren (die Adressen sind die versionierten Seiten der beiden Unterlagen):
|
||||
|
||||
> Damit wir den Terminplaner für Dich einschalten können, brauchen wir Deine Zustimmung in Textform. Es gelten die Leistungsbeschreibung Terminplaner und der Auftragsverarbeitungsvertrag Terminplaner in der Fassung \<Fassung>: \<Adresse der Leistungsbeschreibung> · \<Adresse des AVV>. Bitte antworte hier mit „Ich stimme zu“ und bestätige, dass Dein Angebot nicht im Gesundheitsbereich liegt.
|
||||
|
||||
Danach geht K16 mit Leistungsbeschreibung und AVV als PDF an den Kunden. Genügt das? (a) Zustimmung durch eine Chat-Antwort für den AVV (Art. 28 Abs. 9 DSGVO) und die Leistungsbeschreibung; (b) der Nachweis ist der Eintrag des Mitarbeiters, die Chat-Nachricht bleibt im Portal; (c) K16 nennt die Anhänge nicht im Text und ist die Bestätigung auf einem dauerhaften Datenträger (die gerenderte Mail folgt als Anlage C nach der Mail-Überarbeitung des Inhabers); (d) für diesen Weg gibt es keine Widerrufsbelehrung (kein Kauf, kein Preis); (e) auch das Wiedereinschalten eines abgeschalteten Terminplaners, den das Team ohne Bestellung freigeschaltet hatte, verlangt dieselbe Textform (bei einem gekauften oder schon aktiven Terminplaner entfällt sie).
|
||||
|
||||
**Fundstelle im Text.** Abschnitt 3.5 (Vorbemerkung Abs. 2), Abschnitt 2.1 (Freischaltung ohne Bestellung); Programm: `termineTextformNoetig`, `textformFuerAktivierung`, `adminActivate`, `textformNachweisText`, `meldeFreigeschaltet`; Zusammenhang mit Q7.
|
||||
|
||||
**Unsere Annahme.** Ja: Textform genügt für die Zustimmung zu Leistungsbeschreibung und AVV, der Nachweis des Mitarbeiters mit dem Chatverlauf im Portal ist hinreichend, und K16 liefert den dauerhaften Datenträger.
|
||||
|
||||
**Alternative.** Die Zustimmung durch den Kunden selbst im Portal (Haken) einholen, auch für Kulanzfälle; K16 im Text auf die Anhänge hinweisen lassen.
|
||||
|
||||
### Q22: BFSG-Satz ohne Konformitätszusage
|
||||
|
||||
**Frage.** Leistungsbeschreibung Nr. 11 Abs. 2 sagt: Das Widget ist für die Bedienung mit Tastatur und Hilfstechniken gestaltet (Beschriftungen, Fokusführung, Fehlermeldungen als Live-Region); eine Konformität nach WCAG 2.1 AA oder EN 301 549 sagt WEBklar nicht zu; ob der Kunde als Dienstleistungserbringer unter das BFSG fällt (Ausnahme für Kleinstunternehmen, § 3 Abs. 3 BFSG), entscheidet und prüft der Kunde. Ist die Formulierung ohne Konformitätszusage zulässig und für den Kunden klar genug? Nach unserem Stand gab es für das Widget bisher keinen Lauf mit einem automatischen Prüfwerkzeug oder einem Screenreader.
|
||||
|
||||
**Fundstelle im Text.** Abschnitt 3.4 (Nr. 11 Abs. 2).
|
||||
|
||||
**Unsere Annahme.** Ja. Der Satz sagt nur, was gestaltet wurde, und weist die Prüfung des BFSG dem Kunden zu.
|
||||
|
||||
**Alternative.** Den Satz streichen; oder eine Prüfung des Widgets beauftragen und die Aussage danach anpassen.
|
||||
|
||||
### Q23: Nachweis der Bestellung mit IP-Adresse und Browser; Aufbewahrung drei Jahre
|
||||
|
||||
**Frage.** Beim Klick auf „Zahlungspflichtig bestellen“ speichert das Portal als Nachweis die gezeigten Texte und Fassungen, den Zeitpunkt, die Stripe-Sitzung, IP-Adresse und Browserkennung, das Konto und die angemeldete Person. Die Datenschutzerklärung sagt dazu:
|
||||
|
||||
> Bestellen Sie eine Funktion (News-Pinnwand, Terminplaner), speichern wir als Nachweis die Texte und Fassungen, denen Sie zugestimmt haben, den Zeitpunkt Ihres Klicks auf „Zahlungspflichtig bestellen“, die Stripe-Sitzung, IP-Adresse und Browserkennung sowie Ihr Konto und die angemeldete Person. Rechtsgrundlagen sind Art. 6 Abs. 1 lit. b, c und f DSGVO; unser berechtigtes Interesse ist der Nachweis des Vertragsschlusses und der Zustimmungen, insbesondere für den Widerruf. Diese Angaben löschen wir drei Jahre nach Ablauf des Jahres, in dem Ihr letztes Abo endet. Bestellen Sie ohne Abo (zum Beispiel nur die News-Pinnwand), löschen wir diese Angaben spätestens drei Jahre nach Ablauf des Jahres der Bestellung. Den Entwurf Ihres Fragebogens speichern wir bis zu 90 Tage nach Ihrer letzten Änderung und löschen ihn danach, wenn Sie nichts bestellt haben.
|
||||
|
||||
Ist die Speicherung der IP-Adresse und der Browserkennung für den Nachweis des Vertragsschlusses und der Zustimmungen erforderlich (Art. 6 Abs. 1 lit. f DSGVO) und die Aufbewahrung bis drei Jahre nach Ablauf des Jahres, in dem das letzte Abo endet, angemessen? Genügt die Information in der Datenschutzerklärung? Für Bestellungen ohne Abo (nur News) nennt die Erklärung eine eigene Frist: drei Jahre nach Ablauf des Jahres der Bestellung. Das Programm löscht diese Nachweise heute noch nicht; die Löschung wird vor Ablauf der ersten Frist (31.12.2029) nachgerüstet. Ist diese Frist angemessen?
|
||||
|
||||
**Fundstelle im Text.** Abschnitt 2.1 Schritt 4, Datenschutzerklärung § 7 (Hausrunde); Programm: `speichereZustimmung`, `zustimmungFuerSitzung`, `findDueDeletions` (Löschung der Nachweise gemeinsam mit den Abo-Daten des Kunden; für Kunden ohne Abo noch offen, Abschnitt 7.7).
|
||||
|
||||
**Unsere Annahme.** Ja: Der Nachweis dient dem Widerrufsrecht und der Beweisführung; die Frist entspricht der regelmäßigen Verjährung samt Ablauf des Jahres.
|
||||
|
||||
**Alternative.** IP-Adresse und Browserkennung weglassen (nur Zeit, Texte, Konto); oder eine kürzere Frist, etwa bis zum Ablauf der Widerrufsfrist und der Gewährleistung.
|
||||
|
||||
### Q24: Rollen in Abschnitt 6c; Entwurfsaufbewahrung 90 Tage
|
||||
|
||||
**Frage.** Die Datenschutzerklärung von WEBklar (Abschnitt 6c) beschreibt WEBklar als Auftragsverarbeiter für die Daten der Gäste und als Verantwortlichen für die Betriebsdaten im Kundenportal, den Entwurf des Fragebogens und die Bestellung (C1). (a) Stimmt die Rollenverteilung? (b) Der Satz „mit der Buchung speichern wir die IP-Adresse nicht“ ist durch einen Test belegt (Buchung ohne IP und Browser-Kennzeichen); für den Missbrauchsschutz merkt sich der Server Zeitpunkte je IP-Adresse nur im Arbeitsspeicher (C2). Genügt die Darstellung? (c) Anfragen von Gästen leitet WEBklar an den Kunden weiter (A4). (d) Entwürfe aus dem Fragebogen ohne Bestellung löscht WEBklar 90 Tage nach der letzten Änderung: Ist diese Frist zweckgebunden und angemessen?
|
||||
|
||||
**Fundstelle im Text.** Abschnitt 2.1 Schritt 2, Anlage A Nr. 8 und Nr. 9 Abs. 7; Datenschutzerklärung 6c (`docs/rechtliches/termine-datenschutz-6c.md`, Hausrunde); frühere Punkte C1, C2, A4; Programm: `ENTWURF_TAGE`.
|
||||
|
||||
**Unsere Annahme.** Die Rollen sind richtig verteilt (wie beim Abschnitt 6b der Website-Analyse); 90 Tage Entwurf sind angemessen, weil Kunden Fragebögen über Wochen ausfüllen.
|
||||
|
||||
**Alternative.** Entwürfe kürzer aufbewahren (30 Tage) oder nur auf ausdrückliches Speichern des Kunden.
|
||||
|
||||
### Q25: Mindestlaufzeit des Wartungs-Abos und Terminplaner
|
||||
|
||||
**Frage.** Das Wartungs-Abo hat eine Mindestlaufzeit von zwölf Monaten (§ 8 der Abo-Bedingungen) und ist danach zum Monatsende kündbar. Der Terminplaner hat keine eigene Laufzeit und kein Sonderkündigungsrecht; er endet mit dem Abo. Bestellbar ist er nur bei aktivem Abo ohne vorgemerkte Kündigung (Abschnitt 2.1 Schritt 1). (a) Ist es unbedenklich, dass der Kunde 149 € einmalig für etwas zahlt, das mit dem Abo enden kann, zum Beispiel wenn die Mindestlaufzeit bald endet? (b) Reicht Haken 4 („der Terminplaner endet mit dem Wartungs-Abo“) für die Transparenz, oder braucht die Bestellseite einen Hinweis auf das Ende der laufenden Mindestlaufzeit?
|
||||
|
||||
**Fundstelle im Text.** Abschnitt 3.1 (Haken 4), Abschnitt 3.4 (Nr. 8 Abs. 1 und 5), Abo-Bedingungen § 8 (`public/abo-bedingungen.html`); Programm: `bestellbar`, `laeuft`.
|
||||
|
||||
**Unsere Annahme.** Ja: Ohne vorgemerkte Kündigung läuft das Abo weiter; wird es später gekündigt, greift die Erstattung nur vor der Freischaltung, danach hat der Kunde den Terminplaner genutzt.
|
||||
|
||||
**Alternative.** Auf der Bestellseite das Ende der Mindestlaufzeit anzeigen; bei Beendigung in den ersten Monaten nach der Freischaltung anteilig erstatten.
|
||||
|
||||
### Q26: Nachweis der Gäste: Hash und Archiv der Textfassung
|
||||
|
||||
**Frage.** Die Buchung eines Gastes speichert als Nachweis die Fassung der Gästetexte, den Zeitpunkt, den Text des Buchungsknopfs, die Anrede und Hashes der gezeigten Texte, nicht diese Texte selbst. WEBklar archiviert jede Fassung der Gästetexte (Hinweis am Formular, Texte der Haken, Datenschutz-Absatz, Belehrung für Gäste) mit Platzhaltern für die Betriebsangaben bis drei Jahre nach dem Ende des Terminplaners. Die Betriebsangaben (Name, Anschrift, Stornotext, Stunden der Erinnerung, Aufbewahrung) ändern sich später; der Stornotext hat eine eigene Version. Genügt der Hash zusammen mit dem Archiv der Textfassung, um die Zustimmung eines Gastes nachzuweisen? Der Nachweis beruht auf dem Textstand des Servers zum Zeitpunkt der Buchung, nicht auf dem, was das Widget angezeigt hat; wird die Fassung während der Haltezeit von 30 Minuten gewechselt, können beide abweichen (Änderungen sollen außerhalb der Stoßzeiten eingespielt werden).
|
||||
|
||||
**Fundstelle im Text.** Abschnitt 2.2 Punkt 5, Abschnitt 3.5 (AVV Nr. 8 Abs. 5); Archiv `docs/rechtliches/archiv/termine-gaestetexte-2026-10-05.md`; Programm: `baueNachweis`, `baueArchiv`.
|
||||
|
||||
**Unsere Annahme.** Ja: Mit der Fassung, dem Archiv und den Betriebsangaben des Zeitpunkts lässt sich der gezeigte Wortlaut rekonstruieren; das genügt für den Nachweis, ohne Volltexte je Buchung zu speichern.
|
||||
|
||||
**Alternative.** Den vollständigen Text je Buchung speichern (mehr Daten, mehr Speicher); oder den Text je Betrieb und Fassung einmal als Schnappschuss ablegen.
|
||||
|
||||
## 5. Fundstellen im Code
|
||||
|
||||
Pfade gelten im Repository des Kundenportals (`Webklar-Kundenbereich`); Bezeichner sind Funktionen oder Konstanten und stehen in der genannten Datei. Zeilennummern nennen wir nicht, weil sie sich ändern.
|
||||
|
||||
| Thema | Datei | Funktion/Konstante |
|
||||
|---|---|---|
|
||||
| Fassung der Vertragsunterlagen und Bestelltexte | `server/config.js` | `avvVersion` |
|
||||
| Bestell-Haken und Knopf des Terminplaners (Register) | `server/config.js` | `termineConsent`, `TERMINE_PREIS` |
|
||||
| Preisangabe und Umsatzsteuerhinweis | `server/config.js` | `ustHinweis` |
|
||||
| Preis im Produktkatalog (14 900 Cent) | `server/config.js` | `termineSetup`, `amountCents` |
|
||||
| Widerrufsbelehrung der Funktionen (WEBklar als Unternehmer) | `server/services/termine/rechtstexte.js` | `widerrufsbelehrungFunktion`, `WEBKLAR_KONTAKT` |
|
||||
| Gästetexte: Belehrung, Haken, Hinweis, Datenschutz-Absatz | `server/services/termine/rechtstexte.js` | `widerrufsbelehrung`, `ZUSTIMMUNG_VORZEITIG`, `ZUSTIMMUNG_STORNO`, `ZUSTIMMUNG_ERINNERUNG`, `GESUNDHEIT_HINWEIS`, `formularHinweis`, `datenschutzAbsatz` |
|
||||
| Fassung der Gästetexte | `server/services/termine/rechtstexte.js` | `RECHTSTEXTE_VERSION` |
|
||||
| Knopftexte der Gäste, Widerrufsfunktion | `server/services/termine/einstellungen.js` | `knopfText`, `zeigtWiderruf` |
|
||||
| Wann der Haken zum vorzeitigen Beginn gilt | `server/services/termine/einstellungen.js` | `brauchtZustimmungVorzeitig` |
|
||||
| Eingabeprüfung der Buchung, Storno-Haken nur bei Preis, Nachweis mit Hashes | `server/services/termine/ablauf.js` | `pruefeEingabe`, `baueNachweis` |
|
||||
| Storno-Vorschlag der Vorlagen (50 %, 24 Stunden) | `server/services/termine/vorlagen.js` | `stornoText`, `STORNO_PROZENT` |
|
||||
| Zustimmungsnachweis der Bestellung | `server/services/featureConsents.js` | `speichereZustimmung`, `zustimmungFuerSitzung` |
|
||||
| Bestellung: Reihenfolge Zahlungssitzung, Nachweis, Weiterleitung | `server/routes/funktionen.js` | `/:key/checkout`, `speichereNachweis`, `createFeatureCheckout` |
|
||||
| Bestellbestätigung K15 mit PDF-Anhängen der vereinbarten Fassung | `server/services/siteFeatures.js` | `onFeaturePaid`, `k15Rechtsunterlagen` |
|
||||
| Adressen und Dateien der Rechtsunterlagen | `server/services/legalDocs.js` | `legalPaths`, `legalPdfAttachments`, `featureLegalAttachments` |
|
||||
| Abo-Prüfung (laufend, bestellbar) | `server/services/aboStatus.js` | `laeuft`, `bestellbar` |
|
||||
| Täglicher Abo-Abgleich, Mail K22/K23, Erstattung T19 | `server/services/termine/aboAbgleich.js` | `gleicheAboAb`, `meldeAboEnde` |
|
||||
| Abschalten, Wiedereinschalten, Abschaltgründe | `server/services/siteFeatures.js` | `disable`, `activate`, `DISABLED_REASONS` |
|
||||
| Löschfrist nach dem Abschalten (30 Tage) | `server/services/termine/frist.js` | `LOESCHFRIST_TAGE` |
|
||||
| Löschlauf, Fristen der Buchungen | `server/services/termine/jobs.js` | `loescheAbgeschaltete`, `VERFALLEN_BEHALTEN_MS`, `ABGESAGT_BEHALTEN_MS`, `AUFBEWAHRUNG_VORGABE` |
|
||||
| Aufbewahrung der Entwürfe (90 Tage) | `server/services/termine/entwuerfe.js` | `ENTWURF_TAGE` |
|
||||
| Versandwarteschlange für E-Mails | `server/services/termine/outbox.js` | `GESENDET_BEHALTEN_MS`, `FEHLER_BEHALTEN_MS` |
|
||||
| Aufbewahrung der Zustimmungsnachweise der Bestellung | `server/services/retention.js` | `findDueDeletions`, `applyDeletions` |
|
||||
| Gesundheits-Ausschluss: Branchen, Stichwortwarnung, Bestell-Gate | `server/services/termine/fragebogen.js` | `BRANCHEN`, `gesundheitsWarnungen`, `bestellpruefung` |
|
||||
| Freischaltung ohne Bestellung: Textform nötig? | `server/services/termine/schalter.js` | `termineTextformNoetig` |
|
||||
| Freischaltung ohne Bestellung: Prüfung von Datum und Weg, PDFs | `server/routes/admin/funktionenAdmin.js` | `textformFuerAktivierung`, `unterlagenAlsAnhang` |
|
||||
| Freischaltung ohne Bestellung: Nachweis und Aktivierung | `server/services/siteFeatures.js` | `adminActivate`, `textformNachweisText` |
|
||||
| Widerruf der Bestellung: Fristende je Bestellung, spätestes Fristende entscheidet über „rechtzeitig“ | `server/routes/abo.js` | `withdrawFeature` |
|
||||
| Widerrufsfrist (14 Tage), Fristende | `server/services/deadlines.js` | `WITHDRAWAL_DAYS`, `withdrawalDeadline` |
|
||||
| Widerruf durch Gäste (Widerrufsfunktion) | `server/routes/publicTermine.js` | `/:siteKey/widerruf`, `/termin/widerruf`, `widerrufeGast`, `widerrufeOhneLink` |
|
||||
| Bestätigungs-E-Mail an Gäste, Hinweis kein Widerrufsrecht | `server/mail-templates/G02 Termin bestaetigt.html` | `kein_widerruf`, `widerrufsbelehrung` |
|
||||
| Versand der Bestätigungs-E-Mail | `server/services/termine/mails.js` | `gastG02` |
|
||||
| Eingangsbestätigung des Widerrufs | `server/mail-templates/G08 Eingang Widerruf.html` | `{{eingang}}`, `{{nummer}}`, `nach_frist` |
|
||||
| Mail „Terminplaner beendet“ (30 Tage, Rückkehr) | `server/mail-templates/K22 Terminplaner beendet.html` | `loeschdatum`, `Rückkehr` |
|
||||
| Mail „Terminplaner läuft wieder“ | `server/mail-templates/K23 Terminplaner laeuft wieder.html` | `{{kunde_name}}`, `innerhalb der Löschfrist` |
|
||||
| Hinweise im Kundenportal (Widerruf, Freizeit, Storno-Muster) | `public/termine-tab-einstellungen.js` | `WIDERRUF_HINWEIS`, `FREIZEIT_HINWEIS`, `STORNO_MUSTER_HINWEIS` |
|
||||
| Archiv der Gästetexte je Fassung | `scripts/termine-texte-archiv.mjs` | `baueArchiv` |
|
||||
| HTML-Seiten der Vertragsunterlagen | `scripts/build-legal-html.mjs` | `termineDokumente` |
|
||||
| PDFs der Vertragsunterlagen | `scripts/build-legal-pdfs.sh` | `termine_version`, `TERMINE_VERSION` |
|
||||
| Muster der Widerrufsbelehrung (Anlage 1) | `docs/rechtliches/quellen/anlage-1.txt` | `Folgen des Widerrufs` |
|
||||
| Muster-Widerrufsformular (Anlage 2) | `docs/rechtliches/quellen/anlage-2.txt` | `Muster-Widerrufsformular` |
|
||||
| Datenschutzerklärung von WEBklar: Abschnitt 6c, § 6, § 7 | `public/datenschutz.html` | `id="termine"` |
|
||||
|
||||
## 6. Nicht Teil der Kanzleiprüfung
|
||||
|
||||
Dieser Abschnitt nennt, was wir der Kanzlei nicht zur Prüfung vorlegen, und warum.
|
||||
|
||||
### 6.1 Hausrunde
|
||||
|
||||
Diese Texte prüft WEBklar selbst (wie bei der Website-Analyse), weil sie keine der Kernfragen aus Abschnitt 1 betreffen:
|
||||
|
||||
- die Datenschutzerklärung von WEBklar mit Abschnitt 6c „Online-Terminbuchung auf Websites unserer Kunden“ und den Ergänzungen in § 6 (Kundenansicht) und § 7 (Nachweise der Funktionsbestellung); Quelle `docs/rechtliches/termine-datenschutz-6c.md` und `public/datenschutz.html`. Nur die Rollenverteilung fragen wir (Q24),
|
||||
- die Hinweise im Kundenportal (Widerrufs-, Freizeit- und Storno-Hinweise); zu zwei Punkten fragen wir (Q15, Q16),
|
||||
- die Mailtexte (K15, K16, K22, K23, K04, T09, G02, G08); sie gehen durch die Mail-Überarbeitung des Inhabers. Die offene Formulierung in G02 steht in Q16 und in Abschnitt 7,
|
||||
- die Einbauanleitung und das Einrichtungs-Ticket für Mitarbeiter.
|
||||
|
||||
### 6.2 Später
|
||||
|
||||
Diese Themen gehören nicht zu diesem Verkaufsweg und stehen für spätere Pakete bereit:
|
||||
|
||||
- Abo-Bedingungen in der Fassung 6 bzw. ein Rahmen-AVV mit einer Anlage je Funktion,
|
||||
- Themen der Online-Zahlung der Gäste (ein Satz zu Stripe in Abschnitt 6c, Zahlungsdienste, Vorauszahlung in den AGB, Preisangabenverordnung),
|
||||
- Pflichtangaben auf Geschäftsbriefen (§ 37a HGB),
|
||||
- Rechtsmissbrauch bei spätem Widerruf,
|
||||
- Minderjährige,
|
||||
- Mitgliedschaften,
|
||||
- wiederholtes Versenden der Mails G08 und K21 nach Fristende,
|
||||
- Widerruf nur vor Terminbeginn im Verwaltungslink.
|
||||
|
||||
### 6.3 Frühere Listen „Für die Rechtsprüfung offen“
|
||||
|
||||
Beim Fertigstellen der Gästetexte und der Datenschutzerklärung haben wir Listen offener Rechtsfragen aus den Dokumenten entfernt. Jeder Punkt dieser Listen hat eine Kennung: H1 bis H4 (Hinweise-Dokument), A1 bis A6 (Datenschutz-Absatz für die Website der Betriebe), B1 bis B6 (Muster der Widerrufsbelehrung für Gäste), C1 bis C8 (Entwurf von Abschnitt 6c der Datenschutzerklärung). Jeder Punkt wird entweder bei einer Frage in Abschnitt 4 behandelt (die Kennung steht dort in der Fundstelle) oder ist hier mit Grund aufgeführt:
|
||||
|
||||
| Kennung | Punkt | Wo behandelt |
|
||||
|---|---|---|
|
||||
| H1 | Belehrung vor dem Vertragsschluss: aufklappbare Darstellung im Widget | Q12 |
|
||||
| H2 | Widerruf der Einwilligung am Erinnerungshaken | Q17 |
|
||||
| H3 | Link zur Datenschutzerklärung neben dem Hinweis | Q17 |
|
||||
| H4 | Bestätigungsfrist: 48 Stunden sind der früheste Zeitpunkt der Löschung | Q9 |
|
||||
| A1 | Server-Protokolle: 14 Tage und Rechtsgrundlage | Q10 |
|
||||
| A2 | Drittländer: gilt nur, solange Server und Mailversand bei Hetzner in Deutschland bleiben | Q10 |
|
||||
| A3 | Löschlauf: Fristen sind die frühesten Zeitpunkte | Q9 |
|
||||
| A4 | Kontakt der Gäste: Anfragen gehen an den Betrieb, WEBklar leitet weiter | Q24 |
|
||||
| A5 | Gesundheitsbereich: Hinweis „Bitte keine Gesundheitsangaben“ | Q11 |
|
||||
| A6 | Knopfmodus: Konfiguration schon beim Laden der Seite | Q10 |
|
||||
| B1 | Lieferkosten-Klammer in der Belehrung | Q12 |
|
||||
| B2 | „beginnen soll“ im Singular | Q12 |
|
||||
| B3 | Anschrift und Telefon des Betriebs sind Pflicht | hier: technisch erledigt. `widerrufsbelehrung()` wirft einen Fehler, wenn eine der vier Angaben fehlt, und die Einstellungsprüfung verlangt beide Angaben. Keine Rechtsfrage. |
|
||||
| B4 | Wertersatz-Absatz steht immer in der Belehrung | Q13 |
|
||||
| B5 | Hinweis statt Internetadresse bei der Widerrufsfunktion | Q12 |
|
||||
| B6 | Gesetzlichkeitsfiktion des Musters | Q12 |
|
||||
| C1 | Rolle: Auftragsverarbeiter für die Gäste, Verantwortlicher für die Betriebsdaten | Q24 |
|
||||
| C2 | IP-Adresse im Arbeitsspeicher: Satz „mit der Buchung speichern wir die IP-Adresse nicht“ | Q24 |
|
||||
| C3 | Kalender-Abo-Link: Information der Beschäftigten | Q10 |
|
||||
| C4 | Sicherungen: gelöschte Daten bis zu sieben Tage | Q9 |
|
||||
| C5 | Zahlung der Gäste (spätere Online-Zahlung): Satz zu Stripe in Abschnitt 6c | hier: gehört zur späteren Online-Zahlung der Gäste (Abschnitt 6.2); der Terminplaner kennt heute nur die Zahlung vor Ort. |
|
||||
| C6 | Geschäftsbriefe: Bestätigungen mit Preis sind keine Rechnungen | hier: Abschnitt 6.2 (Pflichtangaben auf Geschäftsbriefen); die Aufbewahrung nach Abschnitt 9 der Datenschutzerklärung betrifft den Terminplaner nicht. |
|
||||
| C7 | Ende des Wartungs-Abos: Satz in Abschnitt 6c | Q9 |
|
||||
| C8 | Knopfmodus: Formulierung zum Laden der Konfiguration in Abschnitt 6c | Q10 |
|
||||
|
||||
## 7. Checkliste für den Inhaber
|
||||
|
||||
Diese Punkte erledigt der Inhaber; kein Punkt ist Aufgabe der Kanzlei. Die Reihenfolge entspricht dem Ablauf bis zum ersten fremden Betrieb.
|
||||
|
||||
### 7.1 Vor der Beauftragung der Kanzlei
|
||||
|
||||
- [ ] Die PDFs der Anlagen A und B und der Widerrufsbelehrung einmal am Bildschirm ansehen (Kopfzeile „WEBklar · …“, „Seite x von y“, Tabelle der Anlage des AVV); wir konnten sie nur über ihre HTML-Seiten prüfen.
|
||||
- [ ] Die Zahl der News-Bestellungen seit 2026-09-30 ermitteln, die mit dem alten Zustimmungstext und ohne Widerrufsbelehrung abgeschlossen wurden, und der Kanzlei nennen (Q4).
|
||||
- [ ] Bestätigen, dass WEBklar Kleinunternehmer nach § 19 UStG bleibt (Q19).
|
||||
|
||||
### 7.2 Hetzner (nur im Konto prüfbar)
|
||||
|
||||
- [ ] Auftragsverarbeitungsvertrag mit Hetzner für die Cloud **und** für das Webhosting abgeschlossen (die Buchungs-E-Mails laufen über den SMTP-Dienst des Webhostings).
|
||||
- [ ] Anlage 1 des Vertrags nennt „Terminbucher“, „Beschäftigte“ und „Buchungsdaten“ als Betroffene und Datenarten.
|
||||
- [ ] Die Kontoadresse bei Hetzner stimmt mit dem Impressum überein.
|
||||
- [ ] Die Datensicherung (Backups) ist eingeschaltet; AVV Nr. 9 Abs. 8 und die Abo-Bedingungen versprechen sieben Tagessicherungen.
|
||||
|
||||
### 7.3 Uploads in WOMS „Rechtliches“
|
||||
|
||||
- [ ] Das Setup-Skript von WOMS mit API-Key ausführen: Es legt die drei Karten „Leistungsbeschreibung Terminplaner“, „AVV Terminplaner (Auftragsverarbeitung)“ und „Widerrufsbelehrung und Muster-Widerrufsformular (Funktionen)“ an.
|
||||
- [ ] Für diese drei Karten die Version der Fassung hochladen (PDF oder HTML aus `public/rechtliches/`; Markdown ist im Bucket nicht erlaubt).
|
||||
- [ ] Die neue Datenschutzerklärung (Stand 05.10.2026, `docs/WEBklar-Datenschutzerklaerung.pdf`) als neue Version der Karte „Datenschutzerklärung“ hochladen.
|
||||
- [ ] „Gültig ab“ in `docs/rechtliches/archiv/README.md` eintragen (Fassung der Gästetexte vom 2026-10-05 steht auf „offen bis Livegang“; bei der Fassung vom 2026-10-02 prüfen, ob „gültig ab 2026-10-03 (Pilot)“ stimmt).
|
||||
|
||||
### 7.4 Mailtexte
|
||||
|
||||
- [ ] Die Platzhalter „ENTWURF (Mailrunde): …“ in `mails.danach` und `mails.bereit` des Terminplaners (Register in `server/config.js`) durch den endgültigen Wortlaut ersetzen. Sie stehen heute in K15 und K16 an Kunden.
|
||||
- [ ] Die Kopfkommentare von K22 und K23 („Texte sind Entwurf für die Mail-Überarbeitung“) in der Mail-Überarbeitung abarbeiten.
|
||||
- [ ] K23 (Terminplaner läuft wieder) sagt „Deine Termine und Einstellungen sind unverändert da“. Für die Termine stimmt das nicht immer, weil der Löschlauf auch bei abgeschaltetem Terminplaner abgesagte und erledigte Termine nach ihren Fristen löscht. Den Satz in der Mail-Überarbeitung abschwächen.
|
||||
- [ ] Nach der Mail-Überarbeitung die gerenderten Mails K15 (mit den Zustimmungen) und K16 als **Anlage C** beilegen und der Kanzlei nachreichen; ihre Angemessenheit als Bestätigung auf einem dauerhaften Datenträger fragen Q3, Q20 und Q21.
|
||||
- [ ] G02 zeigt die Stornobedingungen und den Satz „Absagen richten sich nach den Stornobedingungen“ (Block `kein_widerruf`) auch bei kostenlosen Buchungen. Einen neutralen Satz festlegen, der für Buchungen ohne Preis passt (Q16).
|
||||
|
||||
### 7.5 Beauftragung und Freigabe
|
||||
|
||||
- [ ] Die Kanzlei mit der Prüfung des Kernpakets und den Fragen Q1 bis Q26 beauftragen (Anlagen A und B beilegen; Umfang und Frist vereinbaren).
|
||||
- [ ] Den Befund einarbeiten: Texte ändern, neues Fassungsdatum setzen, HTML und PDFs neu erzeugen, Archiv der Gästetexte und Prüfsummen nachziehen. Dieses Paket (`docs/rechtliches/termine-kanzlei-paket.md`) wird dabei nachgezogen; sein Test meldet jedes Zitat, das nicht mehr wörtlich im Programm oder in den Dokumenten steht.
|
||||
- [ ] Über die News-Altfälle entscheiden (Q4): Belehrung per Mail nachholen oder nur künftig die neuen Texte.
|
||||
- [ ] Die Fassung vor dem ersten fremden Betrieb ausdrücklich freigeben; erst danach die Adresse des ersten fremden Betriebs in die Pilotliste aufnehmen.
|
||||
- [ ] Beim Livegang die Reihenfolge in Abschnitt 7.6 einhalten; den Datenschutz-Abschnitt der Test-Website des Terminplaners auf den Endtext bringen.
|
||||
|
||||
### 7.6 Livegang: Reihenfolge
|
||||
|
||||
Diese Schritte gelten für den Tag, an dem die Betriebsart `on` gesetzt wird. Die Reihenfolge ist wichtig, vor allem: beide Schemata vor dem Code-Deploy und `on` als letzter Schritt.
|
||||
|
||||
- [ ] Vor dem Code-Deploy das Schema des Terminplaners anlegen, erst mit `--check`: `node scripts/termine-setup.mjs` ergänzt in `termineConfig` die Spalte `fragebogen`, in `termineBookings` die Indizes `idx_status_ends` und `idx_status_updated` und in `termineOutbox` den Index `idx_booking`. Ohne sie scheitern Schreibzugriffe und Abfragen des neuen Programms (der Fragebogen, der tägliche Löschlauf und das Löschen von Gastdaten).
|
||||
- [ ] Vor dem Code-Deploy das Schema der Funktionen anlegen, erst mit `--check`: `node scripts/feature-setup.mjs` (neue Felder von `portalFeatures` und die Collection `featureConsents` mit ihren Indizes). Der Zeitpunkt ist der Code-Deploy, nicht erst der erste Löschlauf der Aufbewahrungsfristen: Schon die erste Bestellung des neuen Programms schreibt die neuen Felder, und ohne Schema antwortet Appwrite mit „Unknown attribute“.
|
||||
- [ ] Hat sich `public/datenschutz.html` geändert, das PDF der Datenschutzerklärung mit `scripts/build-legal-pdfs.sh datenschutz` neu drucken, nicht mit `all` (das druckt auch die PDFs der Abo-Bedingungen und des Leitfadens neu); die Prüfsumme `docs/WEBklar-Datenschutzerklaerung.pdf.sha256` gehört dazu.
|
||||
- [ ] Den Code zu einer ruhigen Zeit deployen. Der Nachweis einer Buchung speichert die Fassung der Gästetexte, die der Server beim Speichern hat; bucht ein Gast genau während des Deploys, kann sie von der abweichen, die er auf dem Bildschirm sah.
|
||||
- [ ] Nach dem Deploy die Uploads in WOMS „Rechtliches“ erledigen (Abschnitt 7.3).
|
||||
- [ ] Nach dem Deploy und nur mit Freigabe des Inhabers: `scripts/feature-consent-felder-leeren.mjs` zuerst als Probelauf ausführen (ohne Argument oder mit `--dry-run`; schreibt nichts), die Liste prüfen und erst dann mit `--apply` ausführen. Das Skript leert IP-Adresse und Browserkennung der Bestellung an den Funktionsdokumenten (`portalFeatures`), soweit der Nachweis (`featureConsents`) sie trägt; neue Bestellungen speichern sie dort nicht mehr.
|
||||
- [ ] Als allerletzten Schritt `TERMINE_ENABLED=on` setzen, nachdem alle Punkte oben und die Freigabe der Fassung (Abschnitt 7.5) erledigt sind.
|
||||
|
||||
### 7.7 Nach dem Livegang
|
||||
|
||||
- [ ] Die Löschfrist für die Nachweise von Kunden ohne Abo (nur News) umsetzen, bevor die erste Frist abläuft (31.12.2029). Die Datenschutzerklärung (Abschnitt 7) sagt für Bestellungen ohne Abo „spätestens drei Jahre nach Ablauf des Jahres der Bestellung“; der Löschlauf (`findDueDeletions`) erfasst heute nur Kunden mit einem Abo, ein Kunde ohne Abo ist nie fällig. Umsetzung: Kunden ohne Abo-Dokument, aber mit Nachweisen in `featureConsents`, mit dem Ankerjahr der letzten Bestellung in den Plan aufnehmen und nur die Nachweise und die Bestellangaben am Funktionsdokument löschen, nicht die Beiträge.
|
||||
@@ -0,0 +1,3 @@
|
||||
# Fragen an die Kanzlei: Webshop
|
||||
|
||||
Die Arbeitsliste der Fragen F1 bis F18 ist in das Kanzlei-Paket übernommen und dort um F19 bis F29 ergänzt worden. Maßgeblich ist jetzt: **`docs/rechtliches/webshop-kanzlei-paket.md`**, Abschnitt 4. Diese Datei bleibt nur als Verweis bestehen.
|
||||
@@ -0,0 +1,394 @@
|
||||
# Kanzlei-Paket Webshop (WEBklar)
|
||||
|
||||
Dieses Paket gehört zum Verkaufsweg des Webshops der WEBklar GbR (Am Schwimmbad 10, 67722 Winnweiler, vertreten durch die Gesellschafter Kenso Grimm und Justin Klein). Es zeigt einer Kanzlei, was verkauft wird, wie der Ablauf ist, welche Texte Händler und Käufer in Endfassung sehen, wo diese Texte im Programm stehen und was wir wissen möchten. Es ist wie das Paket des Terminplaners (`docs/rechtliches/termine-kanzlei-paket.md`) aufgebaut; Fragen, die dort schon gestellt sind, nennen wir mit Verweis.
|
||||
|
||||
Fassungen: Leistungsbeschreibung Webshop, Auftragsverarbeitungsvertrag Webshop (AVV) und die Bestelltexte tragen die Fassung **2026-10-05** (Konstante `config.shop.fassung`). Die Widerrufsbelehrung mit Muster-Widerrufsformular ist die gemeinsame der Funktionen (`docs/rechtliches/widerrufsbelehrung-funktionen.md`, Fassung des Terminplaners). Alle Texte sind noch nie ausgeliefert worden; der Webshop bleibt bis zu Ihrem Befund hinter dem Schalter `SHOP_ENABLED` (nur Pilot).
|
||||
|
||||
## 1. Zweck und Umfang
|
||||
|
||||
**Zweck.** Der Inhaber lässt die Texte des Webshops durch eine Kanzlei prüfen, bevor der erste fremde Betrieb ihn nutzt (Schalter `SHOP_ENABLED=on`). Das Paket soll ohne Rückfragen zeigen, was die Kanzlei prüfen soll und von welchen Annahmen wir ausgehen.
|
||||
|
||||
**Was verkauft wird.** WEBklar verkauft nicht Waren, sondern ein Hilfsmittel: ein Shop-Widget für die Website eines Händlers samt Verwaltung im Kundenportal, Einrichtung einmalig 299 €, ohne Umsatzsteuer gemäß § 19 UStG, keine laufende Gebühr, keine Umsatzbeteiligung. Die Käufer bezahlen über Stripe Connect direkt an den Händler (eigenes Stripe-Konto, Direct Charges); WEBklar erhält und verwahrt kein Geld. Vertragspartner der Käufer ist der Händler. Der Webshop läuft, solange WEBklar die Website des Händlers betreibt; er ist nicht an Abo-Daten im Programm gekoppelt (Entscheidung W5, Abschnitt 2.5).
|
||||
|
||||
**Kernpaket.** Wir bitten die Kanzlei, zu prüfen:
|
||||
|
||||
- Leistungsbeschreibung Webshop (Anlage A) und AVV Webshop (Anlage B), vor allem Haftung, Ende mit dem Vertrag über den Betrieb der Website, ausgeschlossene Waren und Rolle bei Zahlungen (F1 bis F18),
|
||||
- die Bestelltexte der Einrichtung: fünf Haken und Knopf (Abschnitt 3.1, F26),
|
||||
- die Muster-Rechtstexte für Händler (Anlage C: AGB, Widerrufsbelehrung inklusive § 356a BGB, Muster-Widerrufsformular, Versand und Zahlung; F25),
|
||||
- die Kasse im Widget: Knopf, Pflichtinformationen, Widerrufsfunktion und Gewährleistungshinweis (Abschnitte 3.3 und 3.4, F19, F20, F24),
|
||||
- die Pflichtangaben am Artikel: Grundpreis, Lebensmittel, Produktsicherheit (F21 bis F23).
|
||||
|
||||
**Hausrunde.** Die Datenschutzerklärung von WEBklar (Abschnitt 6d), den Muster-Absatz für die Datenschutzerklärung des Händlers (Anlage D), die Mailtexte und die Hinweise im Kundenportal prüft WEBklar selbst. Zu einzelnen Punkten daraus stellen wir trotzdem Fragen (F27, F28).
|
||||
|
||||
**Begriffe.** *Händler* ist der Kunde von WEBklar, der den Webshop betreibt; *Käufer* sind die Besucher seiner Website, die bestellen. *Betriebsart* des Webshops ist `off` (abgeschaltet), `pilot` (nur Adressen einer Pilotliste) oder `on` (alle Kunden). Derzeit ist der Webshop nur für Konten des Inhabers freigeschaltet. `on` setzt der Inhaber erst nach Ihrem Befund und seiner Freigabe.
|
||||
|
||||
**Keine Rechtsberatung durch WEBklar.** Die Texte für Händler sind Muster ohne Gewähr (Leistungsbeschreibung Nr. 6). Die rechtlichen Aussagen in diesem Paket (vor allem „unsere Annahme“ bei den Fragen) sind ein Arbeitsstand von WEBklar und kein Rechtsurteil.
|
||||
|
||||
**Fassungen und Änderungen.** Ändern wir nach dem Befund einen Text, bekommt er ein neues Fassungsdatum und neue Dateien (HTML, PDF und Prüfsumme); die Zitate in diesem Paket werden dann angepasst.
|
||||
|
||||
**Aufbau.** Abschnitt 2 beschreibt den Produktablauf, Abschnitt 3 enthält die Texte, Abschnitt 4 unsere Fragen F1 bis F29, Abschnitt 5 die Fundstellen im Programm, Abschnitt 6 was nicht Teil der Prüfung ist, Abschnitt 7 die Checkliste für den Inhaber vor `SHOP_ENABLED=on`. Anlagen: **Anlage A** Leistungsbeschreibung Webshop, **Anlage B** AVV Webshop, **Anlage C** Muster-Rechtstexte der Händler, **Anlage D** Muster-Datenschutzabsatz für die Händler-Website (Pfade in Abschnitt 3.5).
|
||||
|
||||
## 2. Produktablauf in Kurzform
|
||||
|
||||
### 2.1 Anfrage, Freigabe und Bestellung der Einrichtung
|
||||
|
||||
| Schritt | Was geschieht |
|
||||
|---|---|
|
||||
| 1. Anfrage | Der Händler fragt den Webshop im Kundenportal für eine seiner Websites an: was er verkauft (Freitext und Warenart), ungefähre Artikelzahl, Versand und/oder Abholung, zwei Pflicht-Haken (er ist Unternehmer; er verkauft keine der ausgeschlossenen Waren), Nachricht an das Team. |
|
||||
| 2. Prüfung | Das Team prüft die Anfrage (erlaubte Waren, Eignung) und nimmt sie an oder lehnt sie mit Begründung ab. Der Händler erhält eine Mail mit der Entscheidung. Ein Anspruch auf Freigabe besteht nicht (LB Nr. 2 Abs. 1). |
|
||||
| 3. Bestellung | Nach der Freigabe bestellt der Händler die Einrichtung für 299 €: Zusammenfassung, volle Widerrufsbelehrung mit Muster-Widerrufsformular, fünf Haken, Knopf „Zahlungspflichtig bestellen · 299 €“ (Abschnitte 3.1 und 3.2). Eine Bindung an ein Abo besteht nicht. |
|
||||
| 4. Nachweis | Der Server legt zuerst die Zahlungssitzung bei Stripe an und speichert den Nachweis: gezeigte Texte, Fassungen, Hash, Zeitpunkt des Klicks, IP-Adresse und Browser-Kennzeichen, Kundenkonto und angemeldete Person. Der Zeitpunkt des Klicks ist der Anker der Widerrufsfrist. |
|
||||
| 5. Zahlung und Ticket | Stripe Checkout; danach legt das Portal ein Einrichtungs-Ticket mit der Einbauanleitung an, der Händler erhält die Bestellbestätigung mit den Unterlagen (Leistungsbeschreibung, AVV, Widerrufsbelehrung als PDF) in der Fassung, der er zugestimmt hat. |
|
||||
| 6. Einrichtung | Der Händler verbindet sein Stripe-Konto (Onboarding bei Stripe), legt Artikel, Versand, Zahlungsarten und Rechtstexte an und bestätigt die Rechtstexte (Abschnitt 3.2). WEBklar baut das Widget und den Link „Vertrag widerrufen“ in die Website ein (Anleitung `docs/funktionen/shop-einbau.md`), fügt den Datenschutz-Absatz ein (Anlage D), macht mit dem Händler einen Testkauf mit Erstattung und schließt das Ticket; das schaltet die Funktion frei. Schaltet ein Mitarbeiter die Funktion stattdessen ohne Zahlung ein (Betriebsart `on`, Kunde nicht in der Pilotliste), verlangt das Portal vorher die Zustimmung des Kunden in Textform (Datum, Weg) und speichert sie als Zustimmungsnachweis (`featureConsents`, mit Mitarbeiter, ohne IP-Adresse und Browser-Kennzeichen); der Kunde erhält danach Leistungsbeschreibung und AVV als PDF (AVV Vorbemerkung Abs. 2). |
|
||||
| 7. Öffnen | Der Händler öffnet den Shop (Schalter „Shop geöffnet“), sobald Einbau fertig, Stripe-Konto zahlungsfähig, Rechtstexte bestätigt, ein Artikel aktiv und Versand oder Abholung eingerichtet sind. Der Server prüft das bei jeder Bestellung erneut. |
|
||||
|
||||
### 2.2 Käufer: Bestellung
|
||||
|
||||
1. Der Käufer wählt Artikel (mit Grundpreis, Lebensmittel- und Produktsicherheitsangaben, Widerrufsausschluss), legt sie in den Warenkorb (im Sitzungsspeicher des Tabs, kein Cookie) und wählt Versand (nur Deutschland und Österreich) oder Abholung. Ganz oben im Widget stehen Lieferländer und Zahlungsarten.
|
||||
2. In der Kasse stehen unmittelbar über dem Knopf die Pflichtinformationen, der Gewährleistungshinweis (Abschnitt 3.4) und der Knopf „Zahlungspflichtig bestellen“ (Abschnitt 3.3). Der Server rechnet alle Preise neu, prüft und reserviert den Bestand (30 Minuten).
|
||||
3. Der Käufer wird zu Stripe Checkout geleitet und gibt dort Name, Adresse und Zahlung ein (Karte, Apple Pay, Google Pay, Klarna, SEPA-Lastschrift je nach Einstellung des Händlers; kein PayPal). Die Stripe-Kasse verlangt keine Telefonnummer (WEBklar erhebt und speichert auch keine) und fragt die Rechnungsanschrift nur ab, soweit die Zahlungsart sie braucht. SEPA gilt erst bei Eingang als bezahlt; bis dahin erhält der Käufer einmal eine Eingangsmail (G15) ohne Vertragsschluss.
|
||||
4. Nach der Zahlung erhält der Käufer die Bestellbestätigung (G09, Abschnitt 3.3), der Händler die Mail „Neue Bestellung“ (K25).
|
||||
5. Der Händler pflegt Status (versendet, abholbereit, abgeschlossen), erstattet über das Portal (Stripe-Rückzahlung auf dem Konto des Händlers) und exportiert Bestellungen als CSV.
|
||||
|
||||
### 2.3 Käufer: Widerruf (§ 356a BGB)
|
||||
|
||||
Der Käufer widerruft über „Vertrag widerrufen“ im Widget, auf der Standalone-Seite oder über den Link in der Fußzeile jeder Seite der Website: Schaltfläche, Formular (Name, E-Mail-Adresse, Bestellnummer „falls zur Hand“; ohne Bestellnummer ist die Erklärung Pflicht und soll Bestelldatum und Artikel nennen), Schaltfläche „Widerruf bestätigen“. Der Eingang wird gespeichert, der Käufer erhält sofort die Eingangsbestätigung mit Datum und Uhrzeit (G12), der Händler die Mail K26. Der Händler bearbeitet den Widerruf und die Erstattung selbst (LB Nr. 5 Abs. 6). Ohne Anmeldung, auch ohne passende Bestellung, mit Mengenbegrenzung gegen Missbrauch. ⚖ F20
|
||||
|
||||
### 2.4 Daten und Fristen
|
||||
|
||||
Käuferdaten liegen auf dem Server bei Hetzner (Falkenstein), E-Mails laufen über den E-Mail-Dienst von Hetzner (Nürnberg). Nicht bezahlte oder stornierte Bestellungen werden nach 30 Tagen gelöscht; bei bezahlten Bestellungen und Widerrufen werden Name, E-Mail-Adresse und Anschriften drei Jahre nach dem Ende des Bestelljahres anonymisiert (Positionen, Beträge, Bestellnummer und Status bleiben); der IP-Hash (Hashwert der IP-Adresse für den Missbrauchsschutz, nur in der offenen Bestellung) wird gelöscht, sobald die Bestellung bezahlt oder storniert wird, spätestens mit der Löschung nicht bezahlter Bestellungen nach 30 Tagen; eine Telefonnummer des Käufers wird nicht gespeichert. Nach dem Abschalten sind Bestellungen 90 Tage lesbar und als CSV exportierbar, danach löscht der tägliche Job alle Daten des Webshops (Artikel, Bilder, Bestellungen, Widerrufe, Einstellungen, Archiv der bestätigten Rechtstexte); das Einrichtungs-Ticket bleibt ohne Käuferdaten. Gelöschte Daten bleiben bis zu sieben Tage in den Sicherungen. ⚖ F14
|
||||
|
||||
### 2.5 Ende mit dem Vertrag über den Betrieb der Website
|
||||
|
||||
Der Webshop läuft, solange WEBklar die Website des Händlers betreibt (Hosting-Abo, Wartungs-Abo oder ein anderer Vertrag über den Betrieb der Website; LB Nr. 10 Abs. 1). Das Programm prüft das **nicht**: Es liest keine Abo-Daten für den Webshop und schaltet nicht von selbst ab. Das Team schaltet zum Vertragsende von Hand ab (Ticketsystem, Kunde, Funktionen, Webshop, „Abschalten“) und schreibt dem Händler vorher eine E-Mail. Beginnt innerhalb der 90 Tage wieder ein solcher Vertrag, schaltet das Team auf Wunsch wieder ein; endet der Vertrag, bevor der Webshop freigeschaltet ist, erstattet WEBklar die 299 € vollständig (von Hand in Stripe). ⚖ F9, F29
|
||||
|
||||
### 2.6 Widerruf der Bestellung durch den Händler
|
||||
|
||||
Der Händler (auch ein Existenzgründer oder Verbraucher) widerruft auf der Seite „Vertrag widerrufen“ von WEBklar mit der Auswahl „Webshop (Einrichtung)“. Die Frist von 14 Tagen beginnt mit dem Klick auf „Zahlungspflichtig bestellen“. Das Team erhält die Mitteilung T09 mit Fristende, Preis und Projekt; es wird nichts automatisch geändert. Das Team prüft, erstattet in Stripe und schaltet den Webshop ab; mit dem Widerruf endet der Webshop (LB Nr. 9 Abs. 6). ⚖ F8
|
||||
|
||||
### 2.7 Zahlen
|
||||
|
||||
| Wert | Bedeutung | Quelle im Code |
|
||||
|---|---|---|
|
||||
| 299 € | Einrichtung je Website-Projekt, einmalig, ohne Umsatzsteuer gemäß § 19 UStG | `SHOP_PREIS` in `server/config.js` |
|
||||
| 14 Tage | Widerrufsfrist (Händler wie Käufer) | `WITHDRAWAL_DAYS` |
|
||||
| 30 Minuten | Haltezeit einer offenen Bestellung (Checkout-Sitzung) | `bestellAblaufMinuten` in `server/config.js`, `legeBestellungAn` |
|
||||
| 30 Tage | Löschung nicht bezahlter oder stornierter Bestellungen | `taeglicherJob` |
|
||||
| 3 Jahre nach Ende des Bestelljahres | Anonymisierung der Käuferdaten | `anonymisierenJahre` in `server/config.js` |
|
||||
| 90 Tage | Lesen und Export nach dem Abschalten, danach Löschung | `taeglicherJob` |
|
||||
| 7 Tage | Eintrag in der Versandwarteschlange nach dem Versand; Sicherungen | AVV Nr. 9 |
|
||||
|
||||
## 3. Texte in Endfassung
|
||||
|
||||
Alle Zitate stehen wörtlich so im Programm oder in den Dokumenten (Abschnitt 5 nennt die Fundstellen).
|
||||
|
||||
### 3.1 Bestellung der Einrichtung: fünf Haken und Knopf
|
||||
|
||||
Auf der Bestellseite stehen vor dem Knopf die volle Widerrufsbelehrung mit Muster-Widerrufsformular und diese fünf Haken, keiner ist vorangekreuzt. Die Links auf Leistungsbeschreibung, AVV und Widerrufsbelehrung in der Fassung stehen daneben, nicht im Text der Haken. Gespeichert wird genau der gezeigte Text mit Fassung und Zeitpunkt. ⚖ F26
|
||||
|
||||
**Haken 1 (`unterlagen`)**
|
||||
|
||||
> Ich stimme der Leistungsbeschreibung Webshop und dem Auftragsverarbeitungsvertrag Webshop in der Fassung 2026-10-05 zu.
|
||||
|
||||
**Haken 2 (`verantwortung`)**
|
||||
|
||||
> Ich verkaufe als Unternehmer in eigenem Namen. Für meine Produkte, Preise, Produktangaben und Rechtstexte (AGB, Widerrufsbelehrung, Impressum, Datenschutzerklärung) bin ich selbst verantwortlich; WEBklar stellt Muster bereit und berät nicht rechtlich. Ich verkaufe keine der in der Leistungsbeschreibung ausgeschlossenen Waren.
|
||||
|
||||
**Haken 3 (`stripe`)**
|
||||
|
||||
> Die Zahlungen meiner Käufer laufen über mein eigenes Stripe-Konto zu den Bedingungen von Stripe. WEBklar erhält und verwahrt kein Geld meiner Käufer und erhebt keine Umsatzbeteiligung.
|
||||
|
||||
**Haken 4 (`beginn`)**
|
||||
|
||||
> Ich verlange ausdrücklich, dass WEBklar vor Ablauf der Widerrufsfrist mit der Einrichtung des Webshops beginnt. Mir ist bekannt, dass mein Widerrufsrecht mit der vollständigen Erbringung der Einrichtung erlischt (§ 356 Abs. 5 Nr. 2 BGB). Widerrufe ich vor der vollständigen Erbringung, schulde ich einen angemessenen Betrag für die bis dahin erbrachte Leistung (§ 357a Abs. 2 BGB).
|
||||
|
||||
**Haken 5 (`information`)**
|
||||
|
||||
> Ich habe die Pflichtinformationen zu dieser Bestellung gelesen: Leistung (Einrichtung des Webshops für diese Website: Einbau des Shop-Widgets, Verwaltung im Portal, Muster-Rechtstexte), Gesamtpreis 299 € einmalig (ohne Umsatzsteuer gemäß § 19 UStG), voraussichtliche Dauer meist innerhalb von 2–3 Wochen, keine laufende Gebühr von WEBklar; der Webshop läuft, solange WEBklar die Website betreibt. Die Widerrufsbelehrung mit Muster-Widerrufsformular habe ich gelesen.
|
||||
|
||||
**Knopf**
|
||||
|
||||
> Zahlungspflichtig bestellen · 299 €
|
||||
|
||||
### 3.2 Bestätigung der Rechtstexte durch den Händler
|
||||
|
||||
Bevor der Händler den Shop öffnet, bestätigt er im Portal (Reiter Rechtliches) die Muster oder seine geänderten Texte mit diesem Haken. Zeit, Person und Hash der Texte werden gespeichert und die bestätigte Fassung wird zusätzlich im Archiv der Rechtstexte abgelegt (⚖ F16); jede Änderung der Texte hebt die Bestätigung auf. Neue Bestellungen werden mit der zuletzt bestätigten Fassung geschlossen. ⚖ F25
|
||||
|
||||
> 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.
|
||||
|
||||
### 3.3 Kasse im Widget und Bestellbestätigung
|
||||
|
||||
Über dem Knopf zeigt das Widget (Quelle `public/embed/shop.js`, Zusammenfassung vom Server) unter den Überschriften „Ihre Bestellung“ (Positionen mit Einzel- und Summenpreis, Zwischensumme, Versandkosten oder Lieferart, Gesamtsumme, Steuerhinweis), „Lieferung und Zahlung“ (Lieferart, Lieferzeit, Zahlungsarten, Anbieter) und „Widerrufsrecht“, die Links „AGB lesen“, „Widerrufsbelehrung lesen“, „Versand & Zahlung lesen“, den Gewährleistungshinweis und den Satz „Name, Lieferadresse und Zahlung geben Sie im nächsten Schritt bei unserem Zahlungsdienstleister Stripe ein.“ Der Knopf heißt „Zahlungspflichtig bestellen“. Das Widerrufsrecht steht dort so:
|
||||
|
||||
> Sie haben ein gesetzliches Widerrufsrecht, soweit unten für einzelne Artikel nichts anderes steht. Einzelheiten finden Sie in der Widerrufsbelehrung.
|
||||
|
||||
Die Widerrufsfunktion führt mit diesen Texten:
|
||||
|
||||
> Hier können Sie Ihren Vertrag widerrufen. Bitte geben Sie Ihren Namen, die E-Mail-Adresse Ihrer Bestellung und möglichst die Bestellnummer an. Sie steht in Ihrer Bestätigungs-E-Mail.
|
||||
|
||||
> Bitte prüfen Sie Ihre Angaben. Mit „Widerruf bestätigen“ erklären Sie den Widerruf Ihres Vertrags.
|
||||
|
||||
Die Bestellbestätigung G09 (`server/mail-templates/G09 Bestellbestaetigung.html`) und die Eingangsbestätigung G12 (`G12 Eingang Widerruf.html`) sind (zusammen mit der Eingangsmail G15 bei ausstehender Zahlung) Anlage E in der gerenderten Fassung nach der Mail-Überarbeitung (Abschnitt 7.4). ⚖ F19, F20, F27
|
||||
|
||||
### 3.4 Hinweis auf die gesetzliche Gewährleistung
|
||||
|
||||
Der Hinweis steht hervorgehoben über dem Knopf (als Grafik `public/img/gewaehrleistung-eu.webp`, Wortlaut als Alternativtext) und in der Bestellbestätigung. Wortlaut (`gewaehrleistungsHinweis()`):
|
||||
|
||||
> GESETZLICHE GEWÄHRLEISTUNG
|
||||
>
|
||||
> Mindestens zwei Jahre gesetzliche Gewährleistung der Vertragsmäßigkeit für Waren, die in der Europäischen Union verkauft werden.
|
||||
>
|
||||
> Verbraucherinnen und Verbraucher können ihre Rechte im Rahmen des gesetzlichen Gewährleistungsrechts geltend machen, z. B. wenn die Waren nicht der Beschreibung entsprechen, nicht bestimmungsgemäß funktionieren.
|
||||
>
|
||||
> Verkäufer haften für jede Vertragswidrigkeit, die zum Zeitpunkt der Lieferung der Waren bestand und innerhalb des Zeitraums der gesetzlichen Gewährleistung erkennbar wird. Verkäufer müssen in solchen Fällen Folgendes anbieten: kostenlose Nachbesserung oder kostenlose Ersatzlieferung, in bestimmten Fällen eine Preisminderung oder eine vollständige Erstattung des Kaufpreises.
|
||||
>
|
||||
> In einigen Ländern gilt ein längerer Zeitraum für die gesetzliche Gewährleistung. Für gebrauchte Waren kann ein kürzerer Zeitraum gelten, jedoch nicht weniger als ein Jahr.
|
||||
>
|
||||
> Für weitere Informationen zu Ihren Rechten in einem bestimmten Land scannen Sie den nachstehenden QR-Code oder fragen Sie den Verkäufer. europa.eu/youreurope/garantien
|
||||
>
|
||||
> Was ist zu tun, wenn Sie vertragswidrige Waren erhalten? 1. Melden Sie dem Verkäufer das Problem so bald wie möglich. 2. Legen Sie einen Kaufnachweis vor, z. B. die Quittung, Rechnung oder einen Kontoauszug.
|
||||
>
|
||||
> Verkäufer und Hersteller können auch gewerbliche Garantien gewähren, die unabhängig von der gesetzlichen Gewährleistung gelten. Diese GARAN-Kennzeichnung zeigt beispielsweise, dass der Hersteller eine gewerbliche Haltbarkeitsgarantie ohne zusätzliche Kosten gewährt, die die gesamte Ware abdeckt.
|
||||
|
||||
⚖ F24
|
||||
|
||||
### 3.5 Dokumente und Fundstellen der Texte
|
||||
|
||||
| Anlage | Dokument | Pfad |
|
||||
|---|---|---|
|
||||
| A | Leistungsbeschreibung Webshop (Markdown, HTML, PDF) | `docs/rechtliches/leistungsbeschreibung-webshop.md`, `public/rechtliches/leistungsbeschreibung-webshop-2026-10-05.html` und `.pdf` |
|
||||
| B | Auftragsverarbeitungsvertrag Webshop (Markdown, HTML, PDF) | `docs/rechtliches/avv-webshop.md`, `public/rechtliches/avv-webshop-2026-10-05.html` und `.pdf` |
|
||||
| C | Muster-Rechtstexte der Händler (Belehrung, Formular, AGB, Versand und Zahlung, Gewährleistungshinweis) | `docs/funktionen/shop-muster-rechtstexte.md` (Quelltext der Muster: `server/services/shop/rechtstexte.js`) |
|
||||
| D | Muster-Datenschutzabsatz für die Website des Händlers | `docs/funktionen/shop-datenschutz-absatz.md` |
|
||||
| E | Gerenderte Mails G09 bis G15 und K24 bis K26 (nach der Mail-Überarbeitung nachgereicht) | `server/mail-templates/` |
|
||||
| – | Gemeinsame Widerrufsbelehrung der Funktionen mit Muster-Widerrufsformular | `docs/rechtliches/widerrufsbelehrung-funktionen.md` |
|
||||
| – | Bestelltexte (fünf Haken, Knopf) | `server/config.js`, `shopConsent` |
|
||||
| – | Datenschutzerklärung von WEBklar, Abschnitt 6d (Hausrunde) | `public/datenschutz.html`, `id="webshop"` |
|
||||
| – | Einbauanleitung für das Team, inklusive Abschalten von Hand | `docs/funktionen/shop-einbau.md` |
|
||||
| – | Hinweis auf die gesetzliche Gewährleistung (Grafik und Text) | `public/img/gewaehrleistung-eu.webp`, `server/services/shop/rechtstexte.js`, `gewaehrleistungsHinweis` |
|
||||
| – | Widerruf der Bestellung durch den Händler | `public/widerrufen.html`, `server/routes/abo.js` |
|
||||
|
||||
## 4. Fragen an die Kanzlei
|
||||
|
||||
Die Markierung ⚖ steht nur hier und in den Abschnittsverweisen, nie in den veröffentlichten Texten. „Unsere Annahme“ ist ein Arbeitsstand von WEBklar und kein Rechtsurteil. Kürzel: **LB** = Leistungsbeschreibung Webshop, **AVV** = Auftragsverarbeitungsvertrag Webshop, **AB** = Abo-Bedingungen. Die Fragen F1 bis F18 betreffen Anlagen A und B, F19 bis F29 den Ablauf im Widget und die übrigen Texte.
|
||||
|
||||
## Leistungsbeschreibung (Anlage A)
|
||||
|
||||
### F1: LB Nr. 1 Abs. 4: Webshop im Hosting-Abo
|
||||
**Text.** Leistungsbeschreibung A Nr. 2 Abs. 3 (keine Formulare mit Server) und Nr. 4 (Rechtstexte nicht enthalten) sowie Leistungsbeschreibung B Nr. 3 stehen dem Webshop „nicht entgegen“, auch im Hosting-Abo.
|
||||
**Frage.** Reicht diese Klarstellung, um die statische Hosting-Website mit einem eingebundenen Widget zu vereinbaren, oder muss die Leistungsbeschreibung A selbst geändert werden (Änderungsverfahren § 15 AB)? Ist der Vorrang der LB vor den AB (Abs. 3) wirksam?
|
||||
**Unsere Annahme.** Ja, weil das Widget auf dem Server von WEBklar läuft und die Website statisch bleibt.
|
||||
|
||||
### F2: LB Nr. 3: Ausgeschlossene Waren und Abschaltung
|
||||
**Text.** Katalog ausgeschlossener Waren (Alkohol, Tabak, Arzneimittel, Waffen und Feuerwerk, Erotik, lebende Tiere, digitale Inhalte, Gutscheine, Abonnements, Stripe-Liste); Abschalten nach Hinweis, bei Gefährdung auch ohne Hinweis; dann keine Erstattung des Einrichtungspreises, wenn der Händler den Verstoß zu vertreten hat.
|
||||
**Frage.** Ist der Katalog gegenüber Unternehmern als AGB-Klausel haltbar (§ 307 BGB, auch bei Verweis auf eine fremde Liste wie die von Stripe, die sich ändert)? Ist die Abschaltung ohne vorherigen Hinweis und der Ausschluss der Erstattung angemessen? Sollte der Verweis auf die Stripe-Liste dynamisch bleiben?
|
||||
|
||||
### F3: LB Nr. 4: Zahlungen, Rolle von WEBklar, ZAG
|
||||
**Text.** Stripe Connect mit eigenem Konto des Händlers (Direct Charges), WEBklar erhält und verwahrt kein Geld, keine Umsatzbeteiligung, „WEBklar ist kein Zahlungsdienstleister“; PayPal nicht verfügbar.
|
||||
**Frage.** Trägt diese Konstruktion ohne Erlaubnis nach dem ZAG (Bereichsausnahme für technische Dienstleister, § 2 Abs. 1 Nr. 9 ZAG)? Ist der Satz „WEBklar ist kein Zahlungsdienstleister“ als Aussage in der LB unbedenklich oder sollte er entfallen? Bleibt das so, wenn der Webshop später eine Plattformgebühr oder PayPal bekommt?
|
||||
**Unsere Annahme.** Keine Erlaubnis nötig, solange kein Geld über WEBklar läuft und keine Gebühr einbehalten wird.
|
||||
|
||||
### F4: LB Nr. 5: Pflichten des Händlers und Rolle als Hosting-Anbieter
|
||||
**Text.** Der Händler verantwortet Artikel, Preise, Produktangaben (Lebensmittel, Produktsicherheit, Grundpreis), Rechtstexte, Lieferung, Gewährleistung, Rechnungen und Aufbewahrung.
|
||||
**Frage.** Ist die Verteilung vollständig und vertretbar? Trifft WEBklar als Anbieter des Widgets eigene Pflichten (Verordnung über digitale Dienste für Hosting-Dienste; Produktsicherheitsverordnung für Online-Marktplätze), obwohl WEBklar nicht Verkäufer ist und keinen Marktplatz betreibt? Brauchen wir eine Meldestelle für rechtswidrige Angebote?
|
||||
|
||||
### F5: LB Nr. 6: Muster-Rechtstexte ohne Gewähr
|
||||
**Text.** WEBklar stellt Muster-AGB, Muster-Widerrufsbelehrung und Muster-Widerrufsformular, Versand und Zahlung bereit („Muster ohne Gewähr“, keine Rechtsberatung); das Widget zeigt den Hinweis auf die gesetzliche Gewährleistung (Verordnung (EU) 2025/1960).
|
||||
**Frage.** Ist die Bereitstellung von Mustern als Nebenleistung eines technischen Produkts nach dem Rechtsdienstleistungsgesetz zulässig (§ 2 Abs. 3, § 5 RDG)? Genügt der Hinweis „Muster ohne Gewähr“ mit Prüfempfehlung? Kann WEBklar für Fehler der Muster haften (Abs. 2 verspricht richtige Beschreibung der Technik)?
|
||||
|
||||
### F6: LB Nr. 8: Haftung
|
||||
**Text.** Wie beim Terminplaner: Haftung für entgangenen Gewinn und Folgeschäden nur im Rahmen von § 13 Abs. 1 und 2 AB; keine Haftung für Inhalte, Preise und Rechtstexte des Händlers, Mustertexte, Ausfälle von Stripe, Rückbuchungen und Zahlungsausfälle.
|
||||
**Frage.** Hält die Klausel der Inhaltskontrolle stand (Abs. 3 und 4 sind Ausschlüsse außerhalb des Einflussbereichs, keine Haftungsbegrenzung für eigene Pflichtverletzung)? Reicht der Verweis auf § 13 AB oder braucht der Webshop mit Zahlungsbezug eine eigene Haftungsgrenze (etwa Höchstbetrag)?
|
||||
|
||||
### F7: LB Nr. 9 Abs. 5: Mehraufwand und Stundenkontingent
|
||||
**Text.** Mehraufwand wird nach Freigabe mit 100 € je Stunde berechnet und nicht auf das Stundenkontingent angerechnet.
|
||||
**Frage.** Das ist eine Entscheidung des Inhabers, keine Rechtsfrage; sie ist hier nur zur Kontrolle gelistet: Soll der Händler im Wartungs-Abo den Mehraufwand wahlweise über sein Kontingent abwickeln können (wie beim Terminplaner nach § 4 AB)?
|
||||
|
||||
### F8: LB Nr. 9 Abs. 6: Widerruf der Einrichtung
|
||||
**Text.** Verbraucher können die Bestellung der Einrichtung widerrufen; Verweis auf die gemeinsame Widerrufsbelehrung der Funktionen (Beispiel Terminplaner), Erlöschen nach der Zustimmung bei der Bestellung.
|
||||
**Zitat der gemeinsamen Belehrung (`docs/rechtliches/widerrufsbelehrung-funktionen.md`).**
|
||||
|
||||
> Das Widerrufsrecht gilt nur für Verbraucher. Wir zeigen die Belehrung und das Muster-Widerrufsformular trotzdem allen, die eine Funktion bestellen. Die Belehrung gilt für die Bestellung der Einrichtung einer Funktion im Kundenportal, zum Beispiel des Terminplaners. Hinweise dazu stehen nach dem Muster-Widerrufsformular.
|
||||
|
||||
Die Belehrung nennt als Beispiel nur den Terminplaner, die Bestellung des Webshops verweist auf dieselbe Datei.
|
||||
|
||||
**Frage.** Der Webshop richtet sich nur an Unternehmer (Nr. 1 Abs. 1), die Bestellung kann aber ein Existenzgründer oder ein Verbraucher vor Gewerbebeginn aufgeben. Ist es richtig, allen Bestellern die Belehrung zu zeigen? Muss die gemeinsame Belehrung den Webshop namentlich nennen, oder genügt das „zum Beispiel“ mit dem Terminplaner, weil der Webshop eine Funktion im Sinne des ersten Satzes ist? Eine Änderung der Datei würde auch die Fassung des Terminplaners betreffen (die Kanzlei-Prüfung der Belehrung selbst ist Teil des Pakets Terminplaner).
|
||||
|
||||
### F9: LB Nr. 10: Ende mit dem Vertrag über den Betrieb der Website, Löschfrist, Wiedereinschaltung
|
||||
**Text.** Der Webshop läuft, solange WEBklar die Website des Händlers betreibt (Hosting-Abo, Wartungs-Abo oder ein anderer Vertrag über den Betrieb der Website). Endet dieser Vertrag, endet auch der Webshop; WEBklar schaltet ihn zum Vertragsende ab und informiert den Händler vorher per E-Mail (Abs. 1, gilt auch beim Widerruf des Vertrags). 90 Tage Lesen und Export, danach Löschung; Wiedereinschaltung innerhalb der 90 Tage auf Wunsch ohne Kosten, wenn wieder ein Vertrag über den Betrieb der Website beginnt (Abs. 5); volle Erstattung des Einrichtungspreises, wenn der Vertrag vor der Freischaltung endet (Abs. 6). Technisch ist der Webshop nicht mit den Abo-Daten gekoppelt: Die Bestellung ist nicht an ein Abo gebunden, und das Abschalten zum Vertragsende erledigt das Team von Hand (WOMS, Funktionen, Webshop, „Abschalten“).
|
||||
**Frage.** Ist die Kopplung des Webshops an den Vertrag über den Betrieb der Website wirksam, auch wenn der Händler einen laufenden Betrieb mit offenen Bestellungen und Widerrufen hat? Reicht der Hinweis an den Händler, seine Käufer zu informieren (Abs. 2), und die Vorab-Mail vor dem Abschalten? Sind die Erstattungs- und Wiedereinschaltungsregeln so vereinbar? Ist die Formulierung „anderer Vertrag über den Betrieb der Website“ bestimmt genug (§ 305c, § 307 BGB)?
|
||||
|
||||
### F10: LB Nr. 11 Abs. 2: Beendigung bei Änderungen von Stripe
|
||||
**Text.** WEBklar darf den Webshop (je Website mit vier Wochen, für alle mit drei Monaten Frist) beenden oder einstellen, auch wenn Stripe die Anbindung beendet oder wesentlich ändert.
|
||||
**Frage.** Ist dieser zusätzliche Beendigungsgrund (gegenüber dem Terminplaner) wirksam, oder muss er enger gefasst werden? Was gilt für den bereits gezahlten Einrichtungspreis?
|
||||
|
||||
### F11: LB Nr. 12 Abs. 2: Barrierefreiheit (BFSG)
|
||||
**Text.** Wie beim Terminplaner: keine Zusage einer Konformität (WCAG 2.1 AA, EN 301 549); ob der Händler unter das BFSG fällt, entscheidet er selbst.
|
||||
**Frage.** Ein Online-Shop ist ein Dienstleistungsangebot im elektronischen Geschäftsverkehr. Ist WEBklar mit dem Widget selbst Normadressat oder nur der Händler? Ist die Ausnahme für Kleinstunternehmen (§ 3 Abs. 3 BFSG) richtig dargestellt? Reicht „keine Zusage“, oder sollte WEBklar einen Mindeststandard für das Widget zusagen?
|
||||
|
||||
## Auftragsverarbeitungsvertrag (Anlage B)
|
||||
|
||||
### F12: AVV Vorbemerkung Abs. 4 und Nr. 2: Rollen
|
||||
**Text.** Händler = Verantwortlicher, WEBklar = Auftragsverarbeiter, Stripe = Vertragspartner des Händlers (kein Unterauftragsverarbeiter). WEBklar verarbeitet Bestell- und Widerrufsdaten, versendet Bestell-E-Mails im Namen des Händlers und betreibt den Missbrauchsschutz. Dazu gehört ein gesalzener Hashwert der IP-Adresse in der Bestellung (Obergrenze von drei offenen Bestellungen je IP-Adresse in 30 Minuten; AVV Nr. 3 Abs. 4), gelöscht bei der Löschung oder Anonymisierung der Bestellung.
|
||||
**Frage.** Ist WEBklar für alle diese Vorgänge reiner Auftragsverarbeiter, oder wird es beim Missbrauchsschutz (Arbeitsspeicher, Mengenbegrenzung, IP-Hash in der Bestellung) oder bei der Widerrufsfunktion (Eingangsbestätigung) zum eigenen Verantwortlichen (Art. 4 Nr. 7 oder Art. 26 DSGVO)?
|
||||
|
||||
### F13: AVV Nr. 7 Abs. 4: Stripe und Datenweitergabe
|
||||
**Text.** Stripe ist kein Unterauftragsverarbeiter; Zahlung, Kartendaten und Auszahlung verantworten Händler und Stripe. WEBklar ruft auf Weisung des Händlers die Zahlungs- und Adressangaben der Checkout-Sitzung ab und veranlasst Erstattungen. Daten von Käufern gehen nicht von WEBklar an Stripe.
|
||||
**Frage.** Ist es richtig, den Datenfluss von Stripe zu WEBklar als Verarbeitung im Auftrag des Händlers zu beschreiben? Ist die Abgrenzung (Stripe als Auftragsverarbeiter und teils eigener Verantwortlicher laut DPA von Stripe) für die Datenschutzerklärung des Händlers richtig? Ist der Satz zu Drittlandübermittlungen von Stripe („regeln Kunde und Stripe“) ausreichend?
|
||||
|
||||
### F14: AVV Nr. 9: Fristen für Anonymisierung und Löschung
|
||||
**Text.** Nicht bezahlte Bestellungen (offen oder storniert): Löschung 30 Tage nach dem Anlegen. IP-Hash: gelöscht, sobald die Bestellung bezahlt oder storniert wird, spätestens mit dieser Löschung. Keine Telefonnummer des Käufers. Bezahlte Bestellungen und Widerrufe: Käuferdaten werden 3 Jahre nach dem Ende des Bestelljahres anonymisiert; Positionen, Beträge, Bestellnummer und Status bleiben. Nach dem Abschalten: 90 Tage Lesen und Export, dann Löschung aller Daten (auch des Archivs der Rechtstexte). Versandwarteschlange: Inhalt sofort nach dem Versand leeren, Eintrag nach 7 Tagen löschen, bei Fehlschlag nach 48 Stunden. Sicherung bis zu sieben Tage.
|
||||
**Frage.** Sind 3 Jahre nach Ende des Bestelljahres für die Käuferdaten angemessen (Gewährleistung zwei Jahre, regelmäßige Verjährung drei Jahre ab Jahresende)? Entsteht ein Konflikt mit den Aufbewahrungspflichten des Händlers (zum Beispiel sechs oder acht Jahre nach AO und HGB), wenn WEBklar die Namen schon früher anonymisiert und der Händler den Export nicht gezogen hat? Reicht der Hinweis in Nr. 9 Abs. 8 und LB Nr. 5 Abs. 8, oder soll die Frist einstellbar sein?
|
||||
|
||||
### F15: AVV Nr. 8 Abs. 2: Betroffenenrechte nur auf Weisung im Chat
|
||||
**Text.** Auskunft und Löschung/Anonymisierung einer einzelnen Bestellung geschehen auf Weisung des Händlers in Textform (Chat), nicht über eine Aktion im Portal; der Händler prüft vorher seine Aufbewahrungspflichten.
|
||||
**Frage.** Genügt das als Unterstützung nach Art. 28 Abs. 3 lit. e DSGVO? Welche Reaktionszeit sollte der AVV nennen („unverzüglich“)?
|
||||
|
||||
### F16: AVV Nr. 8 Abs. 5: Archiv der bestätigten Rechtstexte und Prüfsumme
|
||||
**Text.** Bestätigt der Händler seine Rechtstexte, legt WEBklar jede bestätigte Fassung einmal im Archiv ab (Wortlaut, Prüfsumme, Zeitpunkt, bestätigende Person des Händlers; keine Käuferdaten). Die Bestellung speichert die Prüfsumme der Fassung, die der Käufer sah; die Bestellbestätigung G09 zeigt die archivierte Fassung zu dieser Prüfsumme und nicht die beim Versand aktuelle (fehlt sie ausnahmsweise, gilt die aktuell bestätigte). WEBklar löscht das Archiv mit den übrigen Daten des Webshops 90 Tage nach dem Abschalten (AVV Nr. 9 Abs. 6), nicht früher; es ersetzt nicht die Aufbewahrung des Händlers (AVV Nr. 9 Abs. 8). Der Wortlaut bleibt also auch über die Anonymisierung der Bestellung (drei Jahre nach dem Bestelljahr) hinaus erhalten, solange der Webshop betrieben wird.
|
||||
**Frage.** Genügt das als Nachweis des Händlers für den Vertragsinhalt (Bestellbestätigung mit den vollständigen Texten, § 312f Abs. 2 BGB)? Ist es richtig, die Fassungen bis 90 Tage nach dem Abschalten ohne weitere Frist aufzubewahren (Speicherbegrenzung; die Fassungen enthalten keine Käuferdaten, nur die bestätigende Person des Händlers), oder soll der AVV eine kürzere oder längere Frist nennen?
|
||||
|
||||
### F17: AVV Nr. 3 Abs. 5: Warenkorb im Browser
|
||||
**Text.** Kein Cookie; der Warenkorb (Artikelkennungen und Mengen, kein Personenbezug) liegt im Sitzungsspeicher (sessionStorage) des Tabs, „technisch erforderlich“.
|
||||
**Frage.** Ist das nach § 25 Abs. 2 Nr. 2 TDDDG ohne Einwilligung zulässig? Welche Information muss der Händler dazu in seiner Datenschutzerklärung geben (der Musterabsatz dazu entsteht in Aufgabe J2)?
|
||||
|
||||
### F18: AVV insgesamt: Übernahme der Textbausteine des Terminplaners
|
||||
**Text.** Nummern 5, 6, 7 Abs. 1 bis 3, 10, 11 Abs. 1 und 12 sowie die Anlage sind inhaltlich aus dem AVV Terminplaner übernommen.
|
||||
**Frage.** Gilt die Prüfung des AVV Terminplaner für diese Teile unverändert, oder ist wegen der Zahlungsdaten und der Adressdaten der Käufer (Liefer- und Rechnungsadresse, IP-Hash) eine Anpassung der Maßnahmen in der Anlage nötig?
|
||||
|
||||
## Ablauf, Pflichtangaben, Datenschutz und Ende
|
||||
|
||||
### F19: Vertragsschluss im Widget, § 312j BGB: Platz des Knopfes und Stripe nur als Zahlung
|
||||
**Text.** Die verbindliche Bestellung geschieht im Widget des Händlers mit dem Knopf „Zahlungspflichtig bestellen“ (Abschnitt 3.3). Unmittelbar darüber stehen Positionen, Einzel- und Gesamtpreise, Versandkosten, Gesamtsumme, Lieferart und Lieferzeit, Zahlungsarten, Hinweis auf Widerrufsrecht oder -ausschluss je Artikel, Links auf AGB, Widerrufsbelehrung und Versand & Zahlung, der Gewährleistungshinweis (Abschnitt 3.4) und die Anbieterangaben. Name und Lieferadresse gibt der Käufer erst danach auf der Zahlungsseite von Stripe ein (ohne Telefonnummer, Rechnungsanschrift nur soweit nötig); Stripe Checkout ist nur die Zahlung. Bis zur Zahlung (30 Minuten) ist die Bestellung „offen“ und der Bestand reserviert; zahlt der Käufer nicht, verfällt sie.
|
||||
**Frage.** (a) Erfüllt der Knopf mit der Beschriftung „Zahlungspflichtig bestellen“ im Widget § 312j Abs. 3 BGB, obwohl noch Daten (Name, Adresse) und die Zahlung folgen? (b) Erfüllen die Angaben über dem Knopf § 312j Abs. 2 BGB und Art. 246a § 1 Abs. 1 EGBGB, auch wenn die Lieferadresse noch fehlt? (c) Wann kommt der Vertrag zustande: mit dem Klick, mit der Zahlung oder mit der Bestätigung? Passt das zur Bestellbestätigung und zu den Mustertexten? (d) Ist die Informationspflicht zu Beginn des Bestellvorgangs (§ 312j Abs. 1 BGB: Lieferbeschränkungen und Zahlungsmittel) durch die Zeile ganz oben im Widget erfüllt?
|
||||
**Unsere Annahme.** Ja: Der Händler gibt mit dem Klick im Widget das verbindliche Angebot ab; die Zahlung bei Stripe schließt den Vorgang ab; die Hinweise stehen unmittelbar über dem Knopf.
|
||||
**Alternative.** Den Knopf erst auf der letzten Seite zeigen, nachdem Stripe die Daten erhoben hat (nicht möglich, weil Stripe Checkout die Seite betreibt), oder Name und Adresse im Widget abfragen und Stripe nur für die Zahlung nutzen.
|
||||
|
||||
### F20: Widerrufsfunktion nach § 356a BGB: Ausgestaltung
|
||||
**Text.** Die Schaltfläche „Vertrag widerrufen“ steht in der Fußzeile des Widgets, auf der Standalone-Seite unter `/shop/<siteKey>/widerruf` und, vom Team eingebaut, als Link in der Fußzeile jeder Seite der Website des Händlers (Anleitung `docs/funktionen/shop-einbau.md`, Abschnitt 3). Sie führt ohne Anmeldung zu einem Formular (Name, E-Mail-Adresse, Bestellnummer „falls zur Hand“; ohne Bestellnummer muss die Erklärung Bestelldatum und Artikel nennen), danach zur Schaltfläche „Widerruf bestätigen“. WEBklar speichert den Eingang und sendet dem Erklärenden sofort eine Eingangsbestätigung mit Inhalt, Datum und Uhrzeit (G12) auf einem dauerhaften Datenträger; der Händler erhält die Mail K26. Die Funktion ist auch erreichbar, wenn keine Bestellung zur Angabe gefunden wird; der Händler klärt das. Die Bestellnummer ist ein freiwilliges Feld; fehlt sie, verlangt das Formular Bestelldatum und Artikel in der Erklärung (Freitext), damit der Händler zuordnen kann.
|
||||
**Frage.** (a) Genügt diese Ausgestaltung § 356a BGB (Erreichbarkeit „während der gesamten Widerrufsfrist“, Schaltfläche, Bestätigungsschaltfläche, Eingangsbestätigung)? (b) Reichen Name, E-Mail-Adresse und entweder die Bestellnummer oder Bestelldatum und Artikel in der Erklärung zur Zuordnung („Identifikation“ des Vertrags), oder muss die Bestellung zwingend gefunden werden? (c) Ist der Link auf jeder Seite der Website des Händlers die richtige Verortung („Online-Benutzeroberfläche“), obwohl das Widget nur auf einer Seite läuft, und wer trägt die Pflicht, wenn der Händler den Link entfernt (LB Nr. 5 Abs. 6)? (d) Ist der Zeitstempel in Europe/Berlin mit Eingangsbestätigung ausreichend?
|
||||
**Unsere Annahme.** Ja; die Funktion nimmt jede Erklärung an, die Zuordnung ist Sache des Händlers.
|
||||
**Alternative.** Bestellnummer und E-Mail-Adresse als Pflichtfelder; Zuordnung mit Fehlermeldung bei Nichtfinden (würde Widerrufe abweisen und ist nach unserer Einschätzung riskanter).
|
||||
|
||||
### F21: PAngV, Grundpreis immer je 1 kg, 1 l, 1 m, 1 m²
|
||||
**Text.** Das Portal rechnet den Grundpreis immer auf 1 kg, 1 l, 1 m oder 1 m² um und zeigt ihn als „x € / 1 kg“; die Umrechnung auf 100 g oder 100 ml (die für Waren mit einem Nenngewicht bis 250 g erlaubt wäre) ist nicht vorgesehen. Der Händler gibt Menge und Einheit des Artikels an. Der Grundpreis steht neben dem Gesamtpreis im Artikel und in der Kasse.
|
||||
**Frage.** (a) Ist es zulässig, auch bei kleinen Mengen immer den Grundpreis je 1 kg oder 1 l anzugeben? (b) Reicht die Pflege durch den Händler (Menge und Einheit je Artikel) oder braucht es Ausnahmen (Waren nach Stück, lose Ware, Mengen, bei denen ein Grundpreis entfällt)? (c) Wie ist der Grundpreis bei Varianten mit unterschiedlicher Menge zu zeigen?
|
||||
**Unsere Annahme.** Zulässig und eindeutiger; die Ausnahmen kennt der Händler.
|
||||
|
||||
### F22: Lebensmittel im Fernabsatz: lose Backwaren und Allergene
|
||||
**Text.** Der Händler kann einen Artikel als Lebensmittel markieren; dann ist ein Pflichtfeld „Pflichtangaben“ auszufüllen (Bezeichnung, Zutaten, Allergene hervorgehoben, Menge, gegebenenfalls Hersteller), das vor dem Kauf am Artikel angezeigt wird. Ein typischer Kunde ist eine Bäckerei mit losen (nicht vorverpackten) Waren. Das Widerrufsrecht kann der Händler bei schnell verderblichen Waren ausschließen (§ 312g Abs. 2 Nr. 2 BGB). Die Lieferung und Abholung von Speisen ist in der Recherche als offen markiert.
|
||||
**Frage.** (a) Genügt das Freitextfeld, um Art. 14 LMIV (Pflichtangaben im Fernabsatz vor Vertragsschluss) und die Allergenkennzeichnung nicht vorverpackter Ware zu erfüllen? (b) Muss der Shop die Angaben strukturieren (Allergenliste statt Freitext)? (c) Gilt für Lebensmittel, die der Händler selbst ausliefert oder zur Abholung bereitstellt, § 312 Abs. 2 Nr. 8 BGB oder der Ausschluss nach § 312g Abs. 2 Nr. 2 BGB, und was soll die Muster-Widerrufsbelehrung dazu sagen?
|
||||
**Unsere Annahme.** Freitext genügt, wenn der Händler vollständig angibt; wir weisen ihn in der Leistungsbeschreibung (Nr. 5 Abs. 3) darauf hin.
|
||||
|
||||
### F23: Produktsicherheit (GPSR): Pflichtfelder am Artikel
|
||||
**Text.** Je Artikel erfasst der Händler den Hersteller (Name, Anschrift, E-Mail oder Website; Vorgabe „ich selbst“), bei Herstellern außerhalb der EU die verantwortliche Person in der EU sowie Warn- und Sicherheitshinweise. Das Widget zeigt diese Angaben am Artikel. Eine Produktkennung (Typ, Charge, Seriennummer) und ein Bild jedes Produkts als eigene Pflichtfelder gibt es nicht; Bilder sind optional (bis vier).
|
||||
**Frage.** Erfüllen diese Felder Art. 19 der Verordnung (EU) 2023/988 (GPSR) für Angebote im Fernabsatz? Fehlen Pflichtangaben (Produktkennung, Bild)? Trifft WEBklar als Anbieter des Widgets selbst Pflichten (siehe auch F4)?
|
||||
**Unsere Annahme.** Die Felder decken die Pflichtangaben für die üblichen Waren ab; die Verantwortung bleibt beim Händler.
|
||||
|
||||
### F24: Hinweis auf die gesetzliche Gewährleistung (Verordnung (EU) 2025/1960 Anhang I)
|
||||
**Text.** Seit dem 27.09.2026 ist der harmonisierte Hinweis hervorgehoben zu zeigen. Der Webshop zeigt die Grafik (`public/img/gewaehrleistung-eu.webp`) unmittelbar über dem Knopf „Zahlungspflichtig bestellen“, mit dem Wortlaut als Alternativtext, und führt denselben Text in der Bestellbestätigung. Der Wortlaut steht in Abschnitt 3.4. Der Abgleich der Grafikdatei mit der Originaldatei der Kommission steht noch aus.
|
||||
**Frage.** (a) Entspricht Wortlaut und Gestaltung Anhang I (Größe, Hervorhebung, Position in der Kasse und im Angebot)? (b) Genügt die Einblendung in der Kasse, oder muss der Hinweis schon am Artikel stehen? (c) Genügt der Text in der Bestellbestätigung? (d) Wie ist der Hinweis bei Waren zu halten, die nicht dem Gewährleistungsrecht für Waren unterliegen?
|
||||
**Unsere Annahme.** Kasse und Bestätigung genügen; die Grafik prüfen wir vor dem Livegang gegen die Datei der Kommission.
|
||||
|
||||
### F25: Muster-Rechtstexte des Händlers (AGB, Widerrufsbelehrung, Widerrufsformular, Versand und Zahlung)
|
||||
**Text.** Das Portal befüllt für den Händler Muster nach `docs/funktionen/shop-muster-rechtstexte.md` (Anlage C). Die Widerrufsbelehrung folgt Anlage 1 zu Art. 246a § 1 Abs. 2 S. 2 EGBGB (Fassung 2026 mit dem Baustein zur Widerrufsfunktion), enthält beide Fristbeginn-Alternativen (eine Ware, mehrere getrennt gelieferte Waren), Rücksendekosten zulasten des Käufers (umschaltbar), Abholung und Ausschlussgründe in allgemeiner Form (§ 312g Abs. 2 Nr. 1, 2, 3 BGB). Dazu kommen Muster-AGB für kleine Shops, das Muster-Widerrufsformular (Anlage 2) und Versand- und Zahlungsinformationen, die aus den Einstellungen erzeugt werden. Der Händler bestätigt die Texte mit dem Satz aus Abschnitt 3.2; die Prüfsumme der Texte steht an jeder Bestellung.
|
||||
**Frage.** (a) Erfüllen die Muster, richtig ausgefüllt, die Gesetzlichkeitsfiktion (Art. 246a § 1 Abs. 2 S. 3 EGBGB)? (b) Stimmen die Muster-AGB mit der Technik überein (Vertragsschluss, Eigentumsvorbehalt, Gewährleistung, Streitbeilegung nach § 36 VSBG ohne Link auf die eingestellte OS-Plattform)? (c) Ist es zulässig, Muster als Nebenleistung zu liefern (siehe F5)? (d) Teilsendungen und regelmäßige Lieferung sind nicht vorgesehen: Muss die Belehrung das ausdrücklich sagen?
|
||||
**Unsere Annahme.** Ja, wenn der Händler die Platzhalter richtig ausfüllt; Abweichungen im Einzelfall prüft der Händler (LB Nr. 6).
|
||||
**Alternative.** Nur die Belehrung als Muster, AGB nur als Gliederung.
|
||||
|
||||
### F26: Bestelltexte der Einrichtung: fünf Haken
|
||||
**Text.** Die Bestellung der Einrichtung für 299 € trägt fünf Haken und den Knopf „Zahlungspflichtig bestellen · 299 €“ (Abschnitt 3.1). Alle Besteller sehen die volle Widerrufsbelehrung mit Muster-Widerrufsformular; Haken 4 verlangt den vorzeitigen Beginn mit Erlöschen und Wertersatz (§ 356 Abs. 5 Nr. 2, § 357a Abs. 2 BGB); Haken 5 bündelt die Pflichtinformationen.
|
||||
**Frage.** Wie bei der Bestellung des Terminplaners (dort Q1 bis Q3, Q5, Q19, Q20): Genügen die fünf Haken, einschließlich Haken 3 („Die Zahlungen meiner Käufer laufen über mein eigenes Stripe-Konto …“) und Haken 5? Ist „der Webshop läuft, solange WEBklar die Website betreibt“ im Haken 5 als Information zur Laufzeit ausreichend, auch wenn das Ende des Webshops nicht an ein Abo, sondern an irgendeinen Vertrag über den Betrieb der Website gekoppelt ist (siehe F9)?
|
||||
**Unsere Annahme.** Ja; wir gleichen die Antworten zum Terminplaner an.
|
||||
|
||||
### F27: Bestellbestätigung an den Käufer (G09) und Absender
|
||||
**Text.** G09 enthält Anbieter, Bestellnummer, Positionen, Preise, Versand, Gesamtsumme, Lieferart und Lieferzeit, Adresse, Zahlungsart, Hinweis auf Widerrufsausschlüsse, die vollständigen AGB, die vollständige Widerrufsbelehrung und das Muster-Widerrufsformular im Mailtext, den Link „Vertrag widerrufen“ und den Gewährleistungshinweis. Absender ist die Versandadresse von WEBklar mit dem Namen des Shops, Antworten gehen an den Händler.
|
||||
**Frage.** (a) Erfüllt G09 § 312f Abs. 2 BGB (Bestätigung auf einem dauerhaften Datenträger mit den Vertragsinhalten und den Informationen nach Art. 246a EGBGB)? (b) Ist der Absender (Domain von WEBklar, Name des Shops, Reply-To Händler) transparent genug, oder muss der Händler als Absender erscheinen? (c) Müssen die Mails der Anlage zur Prüfung beigelegt werden? (Sie liegen als gerenderte Fassung nach der Mail-Überarbeitung vor.)
|
||||
**Unsere Annahme.** Ja.
|
||||
|
||||
### F28: Datenschutz: Abschnitt 6d, Stripe-Konto und Muster-Absatz
|
||||
**Text.** (a) Datenschutzerklärung von WEBklar Abschnitt 6d („Webshop auf Websites unserer Kunden“): Rolle Auftragsverarbeiter für Käuferdaten, Verantwortliche für Betriebsdaten und Bestellung der Funktion. (b) Eröffnung des Stripe-Kontos des Händlers: WEBklar übermittelt auf Klick des Händlers dessen E-Mail-Adresse, die Adresse seiner Website und den Namen seines Projekts an Stripe Payments Europe Ltd. (Dublin), speichert Konto-ID und Stand und stützt das auf Art. 6 Abs. 1 lit. b DSGVO. (c) Muster-Absatz für die Datenschutzerklärung der Website des Händlers (`docs/funktionen/shop-datenschutz-absatz.md`, Anlage D): Stripe als eigener Verantwortlicher (Betrugsvorbeugung), Drittlandübermittlung (EU-US Data Privacy Framework), Klarna und SEPA über Stripe, Warenkorb im Sitzungsspeicher, E-Mails, Speicherdauer.
|
||||
**Frage.** (a) Sind Rollen und Rechtsgrundlagen richtig (siehe auch F12, F13)? (b) Ist für die Übermittlung an Stripe bei der Kontoeröffnung eine Einwilligung oder Information des Händlers nötig, die über den Klick und Abschnitt 6d hinausgeht? (c) Reicht der Muster-Absatz für die Informationspflichten nach Art. 13 DSGVO (Verantwortlicher, Zwecke, Rechtsgrundlagen, Empfänger, Drittland, Dauer, Rechte), insbesondere für die Aussage zu Stripe und Klarna? (d) Passt die Speicherdauer (30 Tage unbezahlt, 3 Jahre bis zur Anonymisierung) zur Aufbewahrung des Händlers (siehe F14)?
|
||||
**Unsere Annahme.** Ja; der Muster-Absatz folgt dem AVV Webshop.
|
||||
|
||||
### F29: Ende des Webshops mit dem Vertrag über den Betrieb der Website, Abschalten von Hand
|
||||
**Text.** Der Webshop ist nicht mit Abo-Daten im Programm gekoppelt, die Bestellung hängt nicht an einem Abo, und das Programm schaltet nicht von selbst ab (Entscheidung W5). Das Team schaltet zum Vertragsende von Hand ab und schreibt dem Händler vorher (Anleitung `docs/funktionen/shop-einbau.md`, Abschnitt 8). LB Nr. 10 Abs. 1 verspricht dieses Vorgehen; ein verspätetes Abschalten bleibt ein Organisationsrisiko von WEBklar.
|
||||
**Frage.** Entstehen aus einem verspäteten Abschalten Ansprüche des Händlers oder der Käufer gegen WEBklar (etwa wenn der Shop nach dem Vertragsende weiter bestellbar bleibt)? Wie ist „zum Vertragsende“ in Abs. 1 zu fassen (Tag der Beendigung oder Tag danach)? Genügt die Haftungsregelung der LB Nr. 8 dafür?
|
||||
**Unsere Annahme.** Das Risiko ist gering und beschränkt sich auf die Zeit zwischen Vertragsende und Abschalten; die Haftung ist nach LB Nr. 8 begrenzt.
|
||||
|
||||
## 5. Fundstellen im Code
|
||||
|
||||
Pfade gelten im Repository des Kundenportals (`Webklar-Kundenbereich`); Bezeichner sind Funktionen oder Konstanten und stehen in der genannten Datei. Zeilennummern nennen wir nicht, weil sie sich ändern.
|
||||
|
||||
| Thema | Datei | Funktion/Konstante |
|
||||
|---|---|---|
|
||||
| Fassung, Preis, Betriebsart | `server/config.js` | `shop.fassung`, `SHOP_PREIS`, `SHOP_ENABLED` |
|
||||
| Bestell-Haken und Knopf des Webshops (Register) | `server/config.js` | `shopConsent` |
|
||||
| Betriebsart und Pilotliste | `server/services/shop/schalter.js` | `shopModus`, `shopFuerKunde` |
|
||||
| Anfrage und Freigabe | `server/services/shop/anfrage.js` | – |
|
||||
| Einstellungen, Zahlungsarten, Versand | `server/services/shop/einstellungen.js` | `STANDARD_EINSTELLUNGEN` |
|
||||
| Artikel, Produktsicherheit, Lebensmittel, Grundpreis | `server/services/shop/produkte.js`, `server/services/shop/geld.js` | `normalisiereProdukt`, `grundpreisText` |
|
||||
| Berechnung der Kasse (Preise nur aus der Datenbank) | `server/services/shop/preise.js` | `berechne` |
|
||||
| Muster-Rechtstexte, Bestätigung, Gewährleistungshinweis | `server/services/shop/rechtstexte.js` | `musterWiderrufsbelehrung`, `musterWiderrufsformular`, `musterAgb`, `versandUndZahlung`, `BESTAETIGUNG_TEXT`, `gewaehrleistungsHinweis` |
|
||||
| Stripe Connect: Konto, Onboarding, Checkout, Erstattung | `server/services/shop/connect.js` | `kontoAnlegen`, `onboardingLink`, `checkoutAnlegen`, `erstatten`, `connectEreignis` |
|
||||
| Bestellung anlegen (IP-Hash, Stripe-Kasse ohne Telefonnummer), abschließen, Eingangsmail G15, verwerfen, Status, Erstattung, CSV | `server/services/shop/bestellungen.js` | `legeBestellungAn`, `ipHashVon`, `schliesseAb`, `meldeEingang`, `verwirf`, `setzeStatus`, `erstatte`, `csv` |
|
||||
| Archiv der bestätigten Rechtstexte | `server/services/shop/archiv.js` | `archiviereRechtstexte`, `holeArchivierteRechtstexte` |
|
||||
| Zustimmung in Textform bei Freischaltung durch das Team | `server/routes/admin/funktionenAdmin.js`, `server/services/shop/schalter.js`, `server/services/featureConsents.js` | `textformFuerAktivierung`, `shopTextformNoetig`, `speichereZustimmung` |
|
||||
| Widerrufsfunktion der Käufer | `server/services/shop/widerrufe.js` | `normalisiereWiderruf` |
|
||||
| Mails an Käufer und Händler | `server/services/shop/mails.js`, `server/mail-templates/` | `kaeuferWerte`, `haendlerK25`, `haendlerK26`; G09 bis G15, K24 bis K26, T21 |
|
||||
| Öffentliche Schnittstelle des Widgets | `server/routes/publicShop.js` | Routen unter `/api/public/shop/v1/` |
|
||||
| Widget | `public/embed/shop.js` | – |
|
||||
| Löschfristen, Anonymisierung, Abschalten der Daten | `server/services/shop/jobs.js`, `server/services/shop/loeschen.js` | `taeglicherJob`, `anonymisierGrenze`, `loescheShopDaten` |
|
||||
| Kundenlöschung | `server/services/shop/kundenLoeschung.js` | `schliesseShopsVonKunde` |
|
||||
| Einrichtungs-Ticket mit Snippet und Schritten | `server/services/shop/ticketDetails.js` | `shopTicketDetails` |
|
||||
| Rechtsunterlagen je Fassung | `server/services/legalDocs.js` | Satz `shop` |
|
||||
| Widerruf der Bestellung | `server/routes/abo.js`, `public/widerrufen.html` | `ORDERS`, `FEATURE_OF_ORDER`, `orderLabel` |
|
||||
| HTML-Seiten und PDFs der Vertragsunterlagen | `scripts/build-legal-html.mjs`, `scripts/build-legal-pdfs.sh` | Satz und Modus `shop` |
|
||||
| Datenschutzerklärung von WEBklar | `public/datenschutz.html` | `id="webshop"` (Abschnitt 6d) |
|
||||
|
||||
## 6. Nicht Teil der Kanzleiprüfung
|
||||
|
||||
- **Hausrunde:** Datenschutzerklärung von WEBklar (Abschnitt 6d und die Ergänzungen in den Abschnitten 6, 7 und 12), die Hinweise im Kundenportal, die Mailtexte, die Einbauanleitung. Wir stellen nur die Fragen F27 und F28.
|
||||
- **Gemeinsame Widerrufsbelehrung der Funktionen:** wird im Paket des Terminplaners geprüft; hier nur F8.
|
||||
- **Die Rechtstexte der Händler im Einzelfall:** Jeder Händler bleibt für seine Texte verantwortlich (LB Nr. 5 Abs. 2 und Nr. 6). Wir bitten um Prüfung der Muster, nicht jedes Händlers.
|
||||
- **Später:** Plattformgebühr, PayPal, Verkauf an Verbraucher außerhalb von Deutschland und Österreich, digitale Inhalte, Gutscheine, Abonnements, Rechnungserstellung durch WEBklar. Dafür gibt es keine Texte.
|
||||
- **Steuerrecht der Händler** (Umsatzsteuer, Aufbewahrung): nur soweit F14 und F21 betroffen sind.
|
||||
|
||||
## 7. Checkliste für den Inhaber vor `SHOP_ENABLED=on`
|
||||
|
||||
Diese Punkte erledigt der Inhaber; kein Punkt ist Aufgabe der Kanzlei. Die Reihenfolge entspricht dem Ablauf bis zum ersten fremden Betrieb.
|
||||
|
||||
### 7.1 Vor der Beauftragung der Kanzlei
|
||||
|
||||
- [ ] Die PDFs der Anlagen A und B am Bildschirm ansehen (Kopfzeile, „Seite x von y“, Tabelle der Anlage des AVV); wir konnten sie nur über ihre HTML-Seiten prüfen.
|
||||
- [ ] Bestätigen, dass WEBklar Kleinunternehmer nach § 19 UStG bleibt (Preis 299 € ohne Umsatzsteuer).
|
||||
- [ ] Entscheiden, wie das Team den Vertragsende-Termin eines Händlers nicht verpasst (Erinnerung im Ticket, Kalender oder Abo-Liste), da der Webshop von Hand abgeschaltet wird (F29, `docs/funktionen/shop-einbau.md` Abschnitt 8).
|
||||
- [ ] Entscheiden, ob Mehraufwand wahlweise über das Stundenkontingent abgerechnet werden darf (F7).
|
||||
- [ ] Die Grafik `public/img/gewaehrleistung-eu.webp` mit der Originaldatei der Kommission abgleichen (F24).
|
||||
|
||||
### 7.2 Hetzner und Stripe (nur im Konto prüfbar)
|
||||
|
||||
- [ ] Auftragsverarbeitungsvertrag mit Hetzner für die Cloud **und** für das Webhosting (E-Mail-Versand) abgeschlossen; Anlage 1 nennt „Käufer“ und „Bestelldaten“ als Betroffene und Datenarten.
|
||||
- [ ] Datensicherung eingeschaltet (AVV Nr. 9 Abs. 7 verspricht sieben Tagessicherungen).
|
||||
- [ ] Stripe-Dashboard von WEBklar: Connect aktivieren (Plattformprofil: Verkäufer verkaufen eigene Waren, Direct Charges, keine Gebühr), Branding für das Onboarding setzen.
|
||||
- [ ] Prüfen, dass der Vertrag mit Stripe die Nutzung von Connect für diesen Zweck erlaubt (F3).
|
||||
|
||||
### 7.3 Technik
|
||||
|
||||
- [ ] `node scripts/shop-setup.mjs --check`, dann `node scripts/shop-setup.mjs` (fünf Collections, darunter das Archiv der bestätigten Rechtstexte) und `node scripts/stripe-products.mjs --anlegen` (Produkt `shopSetup`), vor dem Code-Deploy.
|
||||
- [ ] Connect-Webhook in Stripe anlegen (Ereignisse: `checkout.session.completed`, `checkout.session.async_payment_succeeded`, `checkout.session.async_payment_failed`, `checkout.session.expired`, `account.updated`, `charge.refunded`), Geheimnis als `STRIPE_CONNECT_WEBHOOK_SECRET` in `/home/webklar/apps/.env`, Zeilen `SHOP_ENABLED` und `SHOP_PILOT_EMAILS` in der Compose-Datei.
|
||||
- [ ] Code nach `main` (nach dem Terminplaner), WOMS-Zweig nach `test`, Deploy zu einer ruhigen Zeit.
|
||||
- [ ] PDF der Datenschutzerklärung mit `scripts/build-legal-pdfs.sh datenschutz` (nicht `all`), PDFs des Webshops mit `scripts/build-legal-pdfs.sh shop`; Prüfsummen mit den Tests abgleichen.
|
||||
|
||||
### 7.4 Uploads in WOMS „Rechtliches“ und Mailtexte
|
||||
|
||||
- [ ] In WOMS „Rechtliches“ Karten „Leistungsbeschreibung Webshop“ und „AVV Webshop“ bereitstellen (Setup-Skript von WOMS) und die Fassung 2026-10-05 hochladen; die neue Datenschutzerklärung (Stand 05.10.2026, `docs/WEBklar-Datenschutzerklaerung.pdf`) als neue Version hochladen.
|
||||
- [ ] Mailtexte G09 bis G15, K24 bis K26, T21 in der Mail-Überarbeitung auf den endgültigen Wortlaut bringen, danach gerendert als Anlage E nachreichen (F27).
|
||||
|
||||
### 7.5 Pilot, Beauftragung und Freigabe
|
||||
|
||||
- [ ] Pilot mit dem Testkonto (`SHOP_ENABLED=pilot`): Anfrage, Freigabe, Bestellung, Einbau auf der Test-Website, Stripe-Konto, Testkauf für 1 € mit Erstattung, Widerruf über „Vertrag widerrufen“, Abschalten und Wiedereinschalten.
|
||||
- [ ] Die Kanzlei mit der Prüfung des Kernpakets und den Fragen F1 bis F29 beauftragen (Anlagen A bis E beilegen; Umfang und Frist vereinbaren).
|
||||
- [ ] Den Befund einarbeiten: Texte ändern, neues Fassungsdatum setzen (`config.shop.fassung`), HTML und PDFs neu erzeugen (`node scripts/build-legal-html.mjs shop`, `scripts/build-legal-pdfs.sh shop`), Prüfsummen und Tests nachziehen; dieses Paket nachziehen.
|
||||
- [ ] Die Fassung ausdrücklich freigeben; erst danach `SHOP_ENABLED=on` setzen.
|
||||
- [ ] Vor dem ersten fremden Betrieb: der Händler bestätigt seine Rechtstexte (Abschnitt 3.2), wir fügen den Datenschutz-Absatz (Anlage D) ein, Testkauf mit dem Händler.
|
||||
@@ -0,0 +1,456 @@
|
||||
# Analyse-Übersicht neu („Cockpit“) Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Die Analyse-Seite im Kundenportal zeigt eine Übersicht im „Cockpit“-Stil: Klartext-Zusammenfassung, Kennzahlen mit Vorzeitraum-Vergleich und Mini-Verlauf, Kontakt-Aktionen, Datenbasis; Heatmap und Einstellungen in eigenen Reitern.
|
||||
|
||||
**Architecture:** Der Collector (`analytics/src/stats.js`) liefert zusätzlich Vorzeitraum, `firstDay`, Tageswerte und Durchschnitte; der Portal-Server reicht das durch. Im Browser: reine Funktionen (`analyse-view.js`), reine HTML-Bausteine (`analyse-cards.js`), DOM-Verdrahtung (`analyse.js`), Stil (`analyse.css`). Die Analyse-Oberfläche zieht aus `funktionen.js` aus.
|
||||
|
||||
**Tech Stack:** Node 24 + `node:sqlite` (Collector), Node 20 + Express (Portal), Vanilla-JS-ES-Module im Browser, `node --test`, Playwright (`playwright-core` aus `/root/.npm/_npx/db89d7302a373f10/node_modules/playwright-core`).
|
||||
|
||||
**Spec:** `docs/superpowers/specs/2026-10-04-analyse-uebersicht-design.md`
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- Arbeitsort: Worktree `/home/webklar/apps/kundenbereich.webklar.com/dev-analyse-neu`, Branch `feature/analyse-uebersicht`. `node_modules` ist ein Symlink auf `../build/node_modules` und bleibt untracked: **nie `git add -A`**, immer Dateien einzeln adden.
|
||||
- Keine neuen Abhängigkeiten. Keine Änderung an `analytics/client/*`, `public/rechtliches/*`, Banner, AVV, Leistungsbeschreibung, Datenschutztexten, WOMS, Schema, `.env`.
|
||||
- Nur vorhandene Daten (Spec U4). Klartext nur durch feste Regeln im Browser (U5).
|
||||
- Texte der Zustände Einschalten und Ausschalten bleiben **wörtlich** wie heute (bestehende Tests in `test/funktionenTiles.test.js` prüfen sie).
|
||||
- Neue Texte: Deutsch, Du-Form klein („du“, „deine“), Zahlen `de-DE` („6.930“, „7,8“).
|
||||
- Element-IDs bleiben: `#analyse-body`, `#an-stage`, `#an-preview`, `#an-avv`, `#an-on`, `#an-error`, `#an-color`, `#an-hint`, `#an-off`, `#an-off-*`, `#an-heat-page`.
|
||||
- Alle Werte aus Daten (Seitenpfade, Hosts, Projektnamen) werden in HTML maskiert (`esc`).
|
||||
- Akzent nur unter `.an`: `--an-acc: #2dd4bf`, `--an-acc-strong: #5eead4`, `--an-acc-soft: rgba(45,212,191,.1)`, weniger `#fda4af`, Karte `rgba(14,16,20,.92)`, Rand `rgba(255,255,255,.09)`, Radius 16 px, Abstand 14 px.
|
||||
- Reiter-Schlüssel: `'uebersicht' | 'heatmap' | 'einstellungen'`. Zeiträume: 7, 30, 90.
|
||||
- Portal-Tests laufen auf Node 20 (Produktion) und 24; Collector-Tests auf Node 24.
|
||||
- Commit-Nachrichten enden mit `Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>`.
|
||||
|
||||
## Review Focus
|
||||
|
||||
1. Antwort eines **alten Collectors** ohne `previous`, `firstDay`, `from/to` und ohne `series[].visitors` (Deploy-Reihenfolge): Seite rendert ohne Chips und ohne Fehler, Mini-Verläufe nur, wo Werte da sind → Test in Task 4.
|
||||
2. **Website ohne einen einzigen Aufruf** (alles 0, `consentRate` 0, `avgSec` null): keine `NaN`/`Infinity`/`undefined` im HTML, Leerzustand im Block „Das Wichtigste“, Ring 0 % → Test in Task 4.
|
||||
3. **Seitenpfad oder Host mit HTML** (`/<img src=x onerror=alert(1)>`, Host `a"b.de`): überall maskiert (Hinweise, Ranglisten, Heatmap-Auswahl) → Tests in Task 3 und Task 4.
|
||||
4. **Schnelles Umschalten** von Zeitraum/Website: eine langsame ältere Antwort überschreibt die neuere nicht → Playwright-Check mit verzögerter Antwort in Task 5.
|
||||
5. **Sehr langer Pfad** auf dem Handy (402 px): keine waagrechte Scrollleiste, Text gekürzt → Playwright-Check in Task 5.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Collector liefert Vorzeitraum, firstDay, Tageswerte, Durchschnitte; Portal liefert activatedAt
|
||||
|
||||
**Files:**
|
||||
- Modify: `analytics/src/stats.js` (gesamte Datei, 69 Zeilen)
|
||||
- Modify: `analytics/test/stats.test.js` (Test „Stats-Endpunkt: Form …“ ab Zeile 118 anpassen, neue Tests anhängen)
|
||||
- Modify: `test/analyticsContract.test.js:42-50`
|
||||
- Modify: `server/routes/funktionen.js:51-75` (`analyseView`)
|
||||
- Modify: `test/analyseRoutes.test.js:115` (+ neuer Test)
|
||||
|
||||
**Interfaces:**
|
||||
- Produces: Antwort von `computeStats(db, site, range, now)` (und damit `GET /a/admin/stats/:key` und `GET /api/funktionen/analyse/stats`) mit den Feldern aus Spec Abschnitt 1:
|
||||
`range`, `from`, `to`, `firstDay`, `totals { pv, visitors, contacts, consentRate, engaged, avgSec, avgScroll }`, `series [{ day, pv, visitors, contacts, avgSec }]`, `pages`, `refs`, `countries`, `devices`, `contacts`, `banner`, `previous { from, to, complete, totals {…gleich…}, series […gleich…], contacts [{key,count}] }`.
|
||||
- Produces: `p.analyse.activatedAt` (ISO-String oder `null`) in `GET /api/funktionen`.
|
||||
|
||||
- [ ] **Step 1: Failing Tests im Collector schreiben** (`analytics/test/stats.test.js`, Helfer `mk`, `admin`, `KEY`, `NOW` = 10.10.2026 12:00 Berlin, `D`, `H` existieren)
|
||||
|
||||
Bestehenden Test „Stats-Endpunkt: Form …“ anpassen:
|
||||
```js
|
||||
assert.deepEqual(Object.keys(j).sort(), ['banner', 'contacts', 'countries', 'devices', 'firstDay', 'from', 'pages', 'previous', 'range', 'refs', 'series', 'to', 'totals']);
|
||||
assert.deepEqual(j.totals, { pv: 4, visitors: 2, contacts: 1, consentRate: 2 / 4, engaged: 1, avgSec: 5, avgScroll: 5 });
|
||||
assert.deepEqual(j.series[5], { day: '2026-10-09', pv: 3, visitors: 1, contacts: 0, avgSec: 5 });
|
||||
assert.deepEqual(j.series[6], { day: '2026-10-10', pv: 1, visitors: 1, contacts: 1, avgSec: null });
|
||||
assert.equal(j.from, '2026-10-04'); assert.equal(j.to, '2026-10-10'); assert.equal(j.range, 7);
|
||||
```
|
||||
Neue Tests:
|
||||
```js
|
||||
test('Stats: Vorzeitraum = die range Kalendertage davor, complete nach firstDay', async () => {
|
||||
// pv am 30.09. (im Vorzeitraum 27.09.–03.10.), en am 01.10., ct am 02.10., pv am 26.09. (vor dem Vorzeitraum)
|
||||
// erst ohne 26.09.: firstDay '2026-09-30', complete false; dann mit 26.09.: firstDay '2026-09-26', complete true
|
||||
assert.equal(j.previous.from, '2026-09-27'); assert.equal(j.previous.to, '2026-10-03');
|
||||
assert.equal(j.previous.series.length, 7); assert.equal(j.previous.series[3].day, '2026-09-30');
|
||||
assert.equal(j.previous.totals.pv, 1); // 26.09. zählt nicht mit
|
||||
assert.deepEqual(j.previous.contacts, [{ key: 'tel', count: 1 }]);
|
||||
assert.equal(j.totals.pv, 0); // aktueller Zeitraum leer
|
||||
});
|
||||
test('Stats: firstDay aus daily und Rohdaten, leer ohne Daten', async () => { /* '' ohne Ereignisse; nach runNightly weiterhin gleich */ });
|
||||
test('Stats: avgSec/avgScroll gerundet, null ohne en; Tageswert avgSec', async () => {
|
||||
// zwei en am selben Tag: sec 30/scroll 50 und sec 91/scroll 100 → avgSec 61 (60,5 gerundet), avgScroll 75, engaged 2
|
||||
});
|
||||
test('Stats: Vorzeitraum über die Zeitumstellung lückenlos und ohne Überlappung', async () => {
|
||||
const NOV = Date.UTC(2026, 10, 10, 10); // range 30
|
||||
// series[0].day '2026-10-12', series[29].day '2026-11-10', previous.series[29].day '2026-10-11', previous.series[0].day '2026-09-12'
|
||||
// alle 60 Tage verschieden
|
||||
});
|
||||
test('Stats: Vorzeitraum aus daily und live, nie doppelt', async () => { /* Ereignis im Vorzeitraum vor und nach runNightly: previous.totals.pv bleibt 1 */ });
|
||||
```
|
||||
Die Ereignisse der Tests mit `ev({ ts, type, … })` einfügen; `runNightly(db, { now: NOW })` verdichtet Tage < heute.
|
||||
|
||||
- [ ] **Step 2: Tests laufen lassen, sie schlagen fehl**
|
||||
|
||||
Run: `cd analytics && node --test test/stats.test.js`
|
||||
Expected: FAIL (fehlende Felder `previous`, `firstDay` …)
|
||||
|
||||
- [ ] **Step 3: `computeStats(db, site, range, now = Date.now())` in `analytics/src/stats.js` umbauen**
|
||||
|
||||
Aufteilen in eine Sammel-Funktion für eine beliebige Tagesliste und eine Formungs-Funktion:
|
||||
- `dayList(endDay, range)` bildet Kalendertage per `Date.UTC(y, m-1, d-i)` (wie heute). Aktueller Zeitraum: `dayList(dayOf(now), range)`. Vorzeitraum: `dayList(<Tag vor days[0]>, range)` – kalendarisch, nie `now - range*DAY_MS`.
|
||||
- `collect(db, site, days, now)`: `daily` für `day IN days` und noch nicht in `agg_state` stehende Tage live aus `events_raw` (Abfrage begrenzt auf Mitternacht UTC des ersten Tags − 2 Tage bis letzter Tag + 2 Tage und `ts <= now`, dann nach `dayOf(ts)` gefiltert; `aggregateRows` aus `jobs.js` wie heute). Ergebnis je Tag: `pv_total`, `visitors`, `ct_kind`-Summe, `en_count`, `en_sec_sum`, `scroll_sum` sowie die Top-Dimensionen über alle Tage.
|
||||
- Durchschnitte: `avgSec = Math.round(en_sec_sum / en_count)`, `avgScroll = Math.round(scroll_sum / en_count)`, beide `null` bei `en_count = 0`; `engaged = en_count`.
|
||||
- `firstDay`: Minimum aus `SELECT MIN(day) FROM daily WHERE site = ?` und `dayOf(SELECT MIN(ts) FROM events_raw WHERE site = ?)`, `''` wenn beides fehlt. `previous.complete = firstDay !== '' && firstDay <= previous.from`.
|
||||
- `pages`, `refs`, `countries`, `devices`, `contacts`, `banner`, `consentRate` bleiben exakt wie bisher (nur für den aktuellen Zeitraum); `previous.contacts` = `top(ct_kind)` des Vorzeitraums.
|
||||
|
||||
- [ ] **Step 4: Collector-Tests grün**
|
||||
|
||||
Run: `cd analytics && npm test`
|
||||
Expected: PASS (bisher 137 Tests + neue), 0 fail
|
||||
|
||||
- [ ] **Step 5: Vertragstest ergänzen** (`test/analyticsContract.test.js` nach Zeile 50)
|
||||
|
||||
```js
|
||||
assert.equal(stats.range, 7)
|
||||
assert.equal(stats.previous.series.length, 7)
|
||||
assert.ok('firstDay' in stats && 'avgSec' in stats.totals)
|
||||
assert.ok(stats.series.every((d) => 'visitors' in d && 'contacts' in d && 'avgSec' in d))
|
||||
```
|
||||
Run: `node --test test/analyticsContract.test.js` → PASS
|
||||
|
||||
- [ ] **Step 6: Failing Test für activatedAt** (`test/analyseRoutes.test.js`)
|
||||
|
||||
Zeile 115: erwartetes Objekt um `activatedAt: null` ergänzen. Im Test nach erfolgreichem `activate` (Zeile ~128–135): `assert.match(view.activatedAt, /^\d{4}-\d{2}-\d{2}T/)`.
|
||||
Run: `node --test test/analyseRoutes.test.js` → FAIL
|
||||
|
||||
- [ ] **Step 7: `analyseView` in `server/routes/funktionen.js` gibt `activatedAt: doc?.state === 'active' ? doc.activatedAt || null : null` zurück**
|
||||
|
||||
Run: `node --test test/analyseRoutes.test.js` → PASS
|
||||
|
||||
- [ ] **Step 8: Gesamte Portal-Suite**
|
||||
|
||||
Run: `npm test`
|
||||
Expected: PASS, 0 fail (Ausgangslage 2259 pass)
|
||||
|
||||
- [ ] **Step 9: Commit**
|
||||
|
||||
```bash
|
||||
git add analytics/src/stats.js analytics/test/stats.test.js test/analyticsContract.test.js server/routes/funktionen.js test/analyseRoutes.test.js
|
||||
git commit -m "Analyse: Vorzeitraum, firstDay, Tageswerte und Durchschnitte in der Statistik; activatedAt in der Ansicht"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: Analyse-Oberfläche zieht aus funktionen.js aus (ohne sichtbare Änderung)
|
||||
|
||||
**Files:**
|
||||
- Create: `public/fn-util.js`
|
||||
- Create: `public/analyse.js`
|
||||
- Modify: `public/funktionen.js` (Zeilen 6–7 Importe, 28–61 Helfer, 101–110 Analyse-Zustand, 611–980 Analyse-Abschnitt)
|
||||
- Modify: `public/analyse-view.js` (Zustand und Auswahl-Helfer dazu)
|
||||
- Modify: `public/app.js:1920`
|
||||
- Modify: `test/funktionenTiles.test.js:8-16`, `test/analyseView.test.js:50-59`
|
||||
|
||||
**Interfaces:**
|
||||
- Produces: `public/fn-util.js` exportiert `esc(value): string`, `safeUrl(url): string`, `request(path, { method, body, raw }): Promise<object>` (unverändert aus `funktionen.js` verschoben).
|
||||
- Produces: `public/analyse-view.js` exportiert zusätzlich `analyseOf(project)`, `analyseState(a)`, `pickAnalyseProject(projects)` (verschoben) und `export const analyseSel = { projectId: '', range: 30, tab: 'uebersicht' }` (gemeinsamer Zustand für Kachel und Analyse; ersetzt das bisherige `analyseSel` in `funktionen.js`).
|
||||
- Produces: `public/analyse.js` exportiert `initAnalyse(root, { projectId })`, `renderAnalyseActivation(stage, project, reload)`, `renderAnalyseOff(box, projectId, reload)` (Verhalten unverändert).
|
||||
- Importstil: `funktionen.js` importiert absolut (`'/fn-util.js'`, `'/analyse-view.js'`), `analyse.js` und `analyse-view.js` relativ (`'./fn-util.js'`, `'./analyse-view.js'`, `'./geraete.js'`) – so lassen sie sich in Node direkt importieren und sind im Browser dieselben Module.
|
||||
|
||||
- [ ] **Step 1: Tests auf die neue Aufteilung umstellen (schlagen fehl)**
|
||||
|
||||
`test/funktionenTiles.test.js`: zusätzlich `.replace("'/fn-util.js'", `'${fileUrl('fn-util.js')}'`)`; `initAnalyse`, `renderAnalyseOff` aus `await import(fileUrl('analyse.js'))` statt aus `funktionen.mjs`.
|
||||
`test/analyseView.test.js` („verdrahtet“):
|
||||
```js
|
||||
assert.match(app, /analyse: \(\) => import\('\/analyse\.js'\)/)
|
||||
const an = readFileSync(new URL('../public/analyse.js', import.meta.url), 'utf8')
|
||||
assert.doesNotMatch(an, /\bconfirm\(/)
|
||||
const fn = readFileSync(new URL('../public/funktionen.js', import.meta.url), 'utf8')
|
||||
assert.doesNotMatch(fn, /function renderAnalyseStats|function renderAnalyseHeat/)
|
||||
```
|
||||
Run: `node --test test/funktionenTiles.test.js test/analyseView.test.js` → FAIL
|
||||
|
||||
- [ ] **Step 2: Verschieben**
|
||||
|
||||
Abschnitt `/* --- Analyse --- */` (Zeile 611 bis Dateiende) und die Analyse-Importe aus `funktionen.js` nach `public/analyse.js`; `esc`/`safeUrl`/`request` nach `fn-util.js`; `analyseOf`/`analyseState`/`pickAnalyseProject` und `analyseSel` nach `analyse-view.js`. `funktionen.js` behält Kacheln und News und setzt beim Klick auf die Analyse-Kachel `analyseSel.projectId` (importiert). Vorher prüfen, wer die Funktionen sonst nutzt: `grep -a -rn "initAnalyse\|renderAnalyse\|analyseSel" public server test`.
|
||||
|
||||
- [ ] **Step 3: `app.js:1920` lädt `/analyse.js`**
|
||||
|
||||
`analyse: () => import('/analyse.js').then((m) => m.initAnalyse(document.getElementById('analyse-body'))),`
|
||||
|
||||
- [ ] **Step 4: Tests grün, Suite grün**
|
||||
|
||||
Run: `npm test` → PASS, 0 fail
|
||||
|
||||
- [ ] **Step 5: Sichtprüfung, dass nichts anders aussieht**
|
||||
|
||||
Run: `node test/ui/analyse.ui.mjs /tmp/claude-0/-root/072dd97e-d807-40b8-b757-afe9463e87f8/scratchpad/ui-task2`
|
||||
Expected: alle Zeilen `OK`, keine `FAIL`.
|
||||
|
||||
- [ ] **Step 6: Commit**
|
||||
|
||||
```bash
|
||||
git add public/fn-util.js public/analyse.js public/funktionen.js public/analyse-view.js public/app.js test/funktionenTiles.test.js test/analyseView.test.js
|
||||
git commit -m "Analyse: Oberfläche in eigenes Modul analyse.js, gemeinsame Helfer in fn-util.js"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 3: Reine Funktionen für Vergleich und Klartext
|
||||
|
||||
**Files:**
|
||||
- Modify: `public/analyse-view.js`
|
||||
- Create: `test/analyseInsights.test.js`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: Statistik-Form aus Task 1; `esc` aus `./fn-util.js`.
|
||||
- Produces (alle in `public/analyse-view.js`, ohne DOM):
|
||||
- `fmtNum(n): string` – `de-DE`, ganzzahlig.
|
||||
- `fmtDuration(sec): string` – `'1:48'`, `'0:07'`; `null`/ungültig → `'–'`.
|
||||
- `dayLabel(day): string` – `'2026-09-19'` → `'Sa., 19.09.'`.
|
||||
- `periodLabel(from, to): string` – `'5. Sep. – 4. Okt.'` (Monatskürzel Jan., Feb., März, Apr., Mai, Juni, Juli, Aug., Sep., Okt., Nov., Dez.); leere Eingabe → `''`.
|
||||
- `compareFrom(stats): { ok: boolean, from: string }` – `ok` wenn `stats.previous?.complete && stats.previous.totals.pv >= 20`; `from` = `'TT.MM.'` von `firstDay + (2 × range − 1)` Tagen nur wenn `previous` vorhanden, `!complete` und `firstDay`; sonst `''`.
|
||||
- `delta(cur, prev, { kind = 'count' } = {}): { dir: 'up'|'down'|'flat', text: string, aria: string } | null` – Regeln Spec 2.4; `null`, wenn `cur` oder `prev` keine Zahl ist.
|
||||
- `contactRate(contacts, visitors): number | null` – Kontakt-Aktionen pro 100 Besucher, eine Nachkommastelle; `null` wenn `visitors < 30`.
|
||||
- `refName(host): string` – Spec 2.1 (Google, Bing, Instagram, Facebook, DuckDuckGo, Ecosia, sonst Host; `''` → `'Direkt oder unbekannt'`).
|
||||
- `weekdayStats(series, firstDay): { best: { weekday, name, avg } | null, avg: number, peak: { day, pv } | null } | null` – über Tage ≥ `firstDay` (alle, wenn `''`); `null` bei < 7 solchen Tagen; `best` nur wenn Schnitt ≥ 1,2 × Gesamtschnitt; `peak` nur wenn ≥ 1,8 × Gesamtschnitt.
|
||||
- `summarize(stats): { lead: string, items: [{ icon: 'contact'|'day'|'source'|'device'|'page', html: string }] } | null` – Spec 2.3; `null` bei `totals.pv < 30`; `lead` und `html` sind fertiges, maskiertes HTML mit `<b>`.
|
||||
- `chartModel(series, opts)` bekommt die Option `compare` (Array `{ pv }`): gemeinsame Skala über beide Reihen, zusätzliches Feld `prevLine` (Pfad, `''` ohne `compare`), `points[i].prev` (Zahl oder `null`).
|
||||
|
||||
- [ ] **Step 1: Failing Tests schreiben** (`test/analyseInsights.test.js`, Testdaten-Helfer `stats(over)` baut eine vollständige Antwort mit 30 Tagen)
|
||||
|
||||
```js
|
||||
test('delta: Prozent ab 100, sonst absolut, flat, Sekunden, null', () => {
|
||||
assert.deepEqual(delta(6930, 6080), { dir: 'up', text: '▲ 14 %', aria: 'plus 14 Prozent gegenüber dem Zeitraum davor' })
|
||||
assert.equal(delta(188, 143).text, '▲ 31 %') // beide ≥ 100 → Prozent
|
||||
assert.equal(delta(45, 33).text, '▲ 12')
|
||||
assert.equal(delta(30, 33).text, '▼ 3')
|
||||
assert.deepEqual(delta(7, 7), { dir: 'flat', text: '± 0', aria: 'unverändert gegenüber dem Zeitraum davor' })
|
||||
assert.equal(delta(1004, 1000).text, '± 0 %') // |0,4 %| < 1
|
||||
assert.equal(delta(108, 114, { kind: 'sec' }).text, '▼ 6 s')
|
||||
assert.equal(delta(200, 135, { kind: 'sec' }).text, '▲ 1:05')
|
||||
assert.equal(delta(null, 3), null)
|
||||
})
|
||||
test('fmtDuration, dayLabel, periodLabel, fmtNum', () => {
|
||||
assert.equal(fmtDuration(108), '1:48'); assert.equal(fmtDuration(7), '0:07'); assert.equal(fmtDuration(null), '–')
|
||||
assert.equal(dayLabel('2026-09-19'), 'Sa., 19.09.')
|
||||
assert.equal(periodLabel('2026-09-05', '2026-10-04'), '5. Sep. – 4. Okt.')
|
||||
assert.equal(fmtNum(6930), '6.930')
|
||||
})
|
||||
test('compareFrom: ok, Datum bei neuer Website, nichts ohne previous', () => {
|
||||
assert.deepEqual(compareFrom(stats()), { ok: true, from: '' })
|
||||
assert.deepEqual(compareFrom(stats({ firstDay: '2026-09-20', previous: { ...prev, complete: false } })), { ok: false, from: '18.11.' }) // range 30: +59 Tage
|
||||
assert.deepEqual(compareFrom({ totals: { pv: 50 }, series: [] }), { ok: false, from: '' })
|
||||
assert.equal(compareFrom(stats({ previous: { ...prev, totals: { ...prev.totals, pv: 19 } } })).ok, false)
|
||||
})
|
||||
test('contactRate und refName', () => {
|
||||
assert.equal(contactRate(188, 2410), 7.8); assert.equal(contactRate(5, 29), null)
|
||||
assert.equal(refName('www.google.de'), 'Google'); assert.equal(refName('l.facebook.com'), 'Facebook'); assert.equal(refName(''), 'Direkt oder unbekannt'); assert.equal(refName('blog.example'), 'blog.example')
|
||||
})
|
||||
test('weekdayStats: stärkster Wochentag, Spitzentag, < 7 Tage null', () => { /* Samstage 2× Schnitt → best.name 'Samstag'; ein Tag 3× Schnitt → peak */ })
|
||||
test('summarize: Wachstum mit Prozent', () => {
|
||||
const s = summarize(stats())
|
||||
assert.equal(s.lead, 'Deine Website wurde in den letzten 30 Tagen <b>6.930-mal</b> aufgerufen – <b>14 % mehr</b> als in den 30 Tagen davor.')
|
||||
assert.equal(s.items[0].icon, 'contact')
|
||||
assert.equal(s.items[0].html, '188-mal hat dich jemand direkt kontaktiert, meist per Telefon (96). Das sind 45 mehr als zuvor.')
|
||||
assert.ok(s.items.length <= 4)
|
||||
})
|
||||
test('summarize: Rückgang, etwa gleich, kleine Zahlen absolut', () => { /* '– <b>9 % weniger</b> als', '– etwa so oft wie in den 30 Tagen davor.', '– <b>12 Aufrufe mehr</b> als' */ })
|
||||
test('summarize: ohne Vergleich und neu gestartet', () => {
|
||||
// previous fehlt: '…in den letzten 30 Tagen <b>X-mal</b> aufgerufen.'
|
||||
// firstDay im Zeitraum (complete false): 'Seit dem Start der Analyse am 20.09. wurde deine Website <b>X-mal</b> aufgerufen.'
|
||||
})
|
||||
test('summarize: zu wenig Daten → null; keine Kontakte → Hinweis nur ab 30 Besuchern', () => {
|
||||
assert.equal(summarize(stats({ totals: { ...t, pv: 29 } })), null)
|
||||
// contacts 0, visitors 40: 'Von den Besuchern mit Zustimmung hat noch niemand über die Website angerufen oder geschrieben.'
|
||||
})
|
||||
test('summarize: Herkunft, Gerät, Seite und Reihenfolge, höchstens 4', () => {
|
||||
// refs google.com 880 von 2270 → 'Die meisten, die über einen Link kamen, kommen von Google (880).'
|
||||
// devices m 4590/6930 → '66 % der Aufrufe kommen vom Handy.'
|
||||
// pages / dann /preise → 'Am meisten angesehen: die Startseite, danach /preise (1.310).'
|
||||
// alle 5 Bedingungen wahr → Seite fällt weg
|
||||
})
|
||||
test('summarize: Pfade und Hosts werden maskiert', () => {
|
||||
const s = summarize(stats({ pages: [{ key: '/<img src=x>', count: 900 }], refs: [{ key: 'a"b.de', count: 900 }] }))
|
||||
assert.ok(!JSON.stringify(s).includes('<img'))
|
||||
assert.ok(JSON.stringify(s).includes('<img'))
|
||||
})
|
||||
test('chartModel mit compare: gemeinsame Skala, prevLine', () => { /* top über beide Reihen; ohne compare prevLine '' und bestehender Test unverändert */ })
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Tests laufen, schlagen fehl**
|
||||
|
||||
Run: `node --test test/analyseInsights.test.js` → FAIL (Exporte fehlen)
|
||||
|
||||
- [ ] **Step 3: Funktionen in `public/analyse-view.js` implementieren** (Signaturen oben; `esc` aus `'./fn-util.js'`; Wochentagsnamen Sonntag…Samstag; Spitzentag-Satz „Spitzentag war Sa., 19.09. mit 371 Aufrufen.“ hängt am Wochentag-Hinweis, wenn beides zutrifft)
|
||||
|
||||
- [ ] **Step 4: Tests grün, Suite grün**
|
||||
|
||||
Run: `node --test test/analyseInsights.test.js test/analyseView.test.js && npm test` → PASS
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add public/analyse-view.js test/analyseInsights.test.js
|
||||
git commit -m "Analyse: Vergleich zum Vorzeitraum und Klartext-Regeln als reine Funktionen"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 4: HTML-Bausteine der Cockpit-Ansicht
|
||||
|
||||
**Files:**
|
||||
- Create: `public/analyse-cards.js`
|
||||
- Create: `test/analyseCards.test.js`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: Task 3 (`summarize`, `delta`, `compareFrom`, `contactRate`, `refName`, `fmtDuration`, `fmtNum`, `periodLabel`, `dayLabel`, `chartModel`, `barRows`, `countryName`, `flagEmoji`, `DEVICE_LABELS`), `esc`.
|
||||
- Produces (alle geben HTML-Strings zurück, ohne DOM; relative Importe):
|
||||
- `headerHtml({ project, usable, range, showRange, devMode, stamp })` – Kopfleiste `.an-top`; Auswahl `<select id="an-project">` nur bei `usable.length > 1`; Zeitraum-Knöpfe `.an-seg button[data-range]` nur bei `showRange`; Status „Analyse aktiv · Stand: heute, 19:42 Uhr“ (`stamp` = `'19:42'`) bzw. gelb „Im Dev-Modus wird nicht gemessen.“
|
||||
- `tabsHtml(tab)` – `role="tablist"`, Knöpfe `[data-tab="uebersicht|heatmap|einstellungen"]`, `aria-selected`.
|
||||
- `overviewHtml(stats)` – setzt zusammen: `summaryHtml`, `kpisHtml`, Zeile `.an-row2` (`chartHtml` + `contactsHtml`), `rankCardHtml('seen', …)` + `rankCardHtml('who', …)`, `basisHtml` + `heatTeaserHtml`, Fußzeile „Länderdaten: IP-Geolocation by DB-IP“ (Link wie heute).
|
||||
- `summaryHtml(stats)`, `kpisHtml(stats)`, `sparkHtml(values)`, `chartHtml(stats)`, `contactsHtml(stats)`, `rankCardHtml(card, stats, { tab, expanded } = {})` mit `card` `'seen'` (Reiter `pages` | `refs`) oder `'who'` (`devices` | `countries`), `basisHtml(stats)`, `heatTeaserHtml()` (Knopf `[data-tab="heatmap"]`), `skeletonHtml()`, `errorHtml(message)` (Knopf `#an-retry`), `settingsHtml(project)` (Status, Unterlagen, leere `<section class="an-card" id="an-off">`).
|
||||
- Texte exakt aus Spec 2.1–2.4 (Leerzustand „Sobald ein paar mehr Besuche da sind, steht hier das Wichtigste über deine Website in zwei, drei Sätzen.“, Quote „Auf 100 Besucher kommen 7,8 Kontakt-Aktionen (davor 6,5).“, „Noch keine Kontakt-Aktionen. Sie zählen, wenn jemand auf deiner Website auf Telefonnummer, E-Mail-Adresse oder WhatsApp tippt.“, Datenbasis-Sätze, „Noch hat niemand über das Banner entschieden.“, „Vergleich ab TT.MM.“).
|
||||
|
||||
- [ ] **Step 1: Failing Tests schreiben** (`test/analyseCards.test.js`; Datenhelfer wie in Task 3)
|
||||
|
||||
```js
|
||||
test('overviewHtml: volle Daten – Kacheln mit Chips, Vorzeitraum-Linie, Kontakte, Quote', () => {
|
||||
const h = overviewHtml(full)
|
||||
assert.match(h, /▲ 14 %/); assert.match(h, /vs\. 6\.080/); assert.match(h, /class="an-prev"/)
|
||||
assert.match(h, /Ø Zeit pro Seite/); assert.match(h, /1:48/); assert.match(h, /Ø 64 % gescrollt/)
|
||||
assert.match(h, /Auf 100 Besucher kommen 7,8 Kontakt-Aktionen \(davor 6,5\)\./)
|
||||
assert.match(h, /61 % haben dem Banner zugestimmt \(2\.410 von 3\.950 Entscheidungen\)/)
|
||||
})
|
||||
test('overviewHtml: alter Collector ohne previous/firstDay – keine Chips, kein Fehler', () => {
|
||||
const h = overviewHtml({ totals: { pv: 612, visitors: 214, contacts: 18, consentRate: 0.62 }, series: [{ day: '2026-09-17', pv: 20 }], pages: [], refs: [], countries: [], devices: [], contacts: [], banner: { shown: 1, accept: 1, reject: 0 } })
|
||||
assert.doesNotMatch(h, /▲|▼|undefined|NaN/); assert.doesNotMatch(h, /class="an-prev"/)
|
||||
})
|
||||
test('overviewHtml: Website ohne Aufrufe – Leerzustand, keine NaN/Infinity', () => {
|
||||
const h = overviewHtml(zero)
|
||||
assert.match(h, /Sobald ein paar mehr Besuche da sind/); assert.match(h, /Noch hat niemand über das Banner entschieden\./)
|
||||
assert.doesNotMatch(h, /NaN|Infinity|undefined|null/)
|
||||
})
|
||||
test('kpisHtml: neue Website zeigt "Vergleich ab 18.11."', () => {})
|
||||
test('contactsHtml: Arten mit 0 in beiden Zeiträumen entfallen, Leertext ohne Kontakte', () => {})
|
||||
test('rankCardHtml: Startseite statt /, Anteil in %, 6 Zeilen, expanded bis 20, Herkunft mit refName, Pfad maskiert', () => {
|
||||
const h = rankCardHtml('seen', stats({ pages: [{ key: '/<img src=x>', count: 9 }] }))
|
||||
assert.match(h, /<img src=x>/); assert.doesNotMatch(h, /<img src=x>/)
|
||||
})
|
||||
test('headerHtml: Auswahl nur bei mehreren Websites, Zeitraum nur bei showRange, Dev-Hinweis, Name maskiert', () => {})
|
||||
test('settingsHtml: aktiv seit TT.MM.JJJJ, Farbfeld, Fassung, Links AVV und Leistungsbeschreibung, #an-off', () => {})
|
||||
test('errorHtml und skeletonHtml', () => { assert.match(errorHtml('Auswertung gerade nicht erreichbar'), /id="an-retry"/) })
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Tests laufen, schlagen fehl**
|
||||
|
||||
Run: `node --test test/analyseCards.test.js` → FAIL
|
||||
|
||||
- [ ] **Step 3: `public/analyse-cards.js` implementieren**
|
||||
|
||||
Klassen-Präfix `an-` (Karten `.an-card`, Kacheln `.an-kpi`, Chips `.an-delta.up|down|flat` mit `aria-label`, Mini-Verlauf `.an-spark`, Diagramm `.an-chart` mit `.an-area`, `.an-line`, gestrichelt `.an-prev` (nur wenn `compareFrom(stats).ok`), Legende „<range> Tage“ / „Zeitraum davor“ (zweiter Eintrag nur mit Vergleich), Punkte mit `data-i`, Daten für die Blase als `data-day`/`data-pv`/`data-prev` am Punkt). Mini-Verläufe: Seitenaufrufe `series[].pv`, Besucher `series[].visitors`, Kontakte `series[].contacts`, Zeit `series[].avgSec` (fehlende Werte als 0, ganz fehlende Reihe → kein Verlauf). Symbole als Inline-SVG (gleicher Stil wie `ICONS` in `funktionen.js`, `stroke`-basiert), keine Emoji.
|
||||
|
||||
- [ ] **Step 4: Tests grün, Suite grün**
|
||||
|
||||
Run: `node --test test/analyseCards.test.js && npm test` → PASS
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add public/analyse-cards.js test/analyseCards.test.js
|
||||
git commit -m "Analyse: HTML-Bausteine der Cockpit-Übersicht"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 5: Cockpit verdrahten – Rahmen, Reiter, Übersicht, Heatmap, Einstellungen, Stil
|
||||
|
||||
**Files:**
|
||||
- Modify: `public/analyse.js`
|
||||
- Create: `public/analyse.css`
|
||||
- Modify: `public/dashboard.html` (Zeile 24: `<link rel="stylesheet" href="/analyse.css">` nach `funktionen.css`)
|
||||
- Modify: `public/funktionen.css:140-218` (Analyse-Abschnitt entfernen; was die Einschalten-Ansicht und Heatmap brauchen, zieht nach `analyse.css`)
|
||||
- Modify: `test/ui/analyse.ui.mjs`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: Task 2 (`analyseSel`, `request`, `esc`, `safeUrl`, bestehende Heatmap-Funktionen in `analyse.js`), Task 3, Task 4 (alle Bausteine).
|
||||
- Produces: `initAnalyse(root, { projectId })` rendert `<div class="an">` mit `headerHtml` + (nur Zustand `active`) `tabsHtml` + `<div id="an-stage">`. Bestehende Exporte bleiben.
|
||||
|
||||
Verhalten:
|
||||
- Zustand `active`: Reiter aus `analyseSel.tab`. Übersicht: erst `skeletonHtml()`, dann `overviewHtml(stats)`; Fehler → `errorHtml(…)`, `#an-retry` lädt neu; Zähler `statsRequestId` verwirft veraltete Antworten (wie heute). Zeitraum-Knöpfe setzen `analyseSel.range` und laden nur die Übersicht neu. `#an-project` wechselt die Website (Reiter bleibt). Ranglisten-Umschalter und „Alle anzeigen“ rendern nur ihre Karte neu (`rankCardHtml`). `[data-tab="heatmap"]` im Teaser wechselt den Reiter.
|
||||
- Diagramm-Blase: Zeigen/Tippen auf `.an-chart` wählt den nächsten Punkt (`data-i`) und zeigt `.an-tip` mit „Sa., 19.09.: 371 Aufrufe (davor 210)“ (ohne Vorwert ohne Klammer); verlassen → ausblenden.
|
||||
- Heatmap-Reiter: `renderAnalyseHeat` wie heute; die Seitenliste kommt aus der zuletzt geladenen Statistik (sonst einmal `range=30` laden). iframes entstehen erst beim Öffnen des Reiters.
|
||||
- Einstellungen-Reiter: `settingsHtml(project)`, danach `renderAnalyseOff(stage.querySelector('#an-off'), projectId, reload)`.
|
||||
- Andere Zustände (Einschalten, gesperrt, nicht verfügbar, unbekannt, keine nutzbare Website): Kopfleiste ohne Zeitraum und ohne Reiter, Inhalt im Cockpit-Rahmen, Texte unverändert. `root.querySelector` kann in Node-Tests für andere Selektoren als `#an-stage` `null` liefern – überall `?.` verwenden.
|
||||
- `analyse.css`: Werte aus Global Constraints und Spec 2.5; Raster Desktop wie Entwurf A (`/root/reports/analyse-ueberarbeitung-2026-10-04/entwurf-a-cockpit.png`); `.an-row2` mit `align-items: stretch`, Diagramm füllt die Höhe; ≤ 880 px: Kacheln 2 × 2, alles einspaltig, Reiter `overflow-x: auto`, Zeitraum volle Breite, lange Namen mit `text-overflow: ellipsis`; `@media (prefers-reduced-motion: reduce)` ohne Übergänge.
|
||||
|
||||
- [ ] **Step 1: UI-Test erweitern (schlägt fehl)** – `test/ui/analyse.ui.mjs`
|
||||
|
||||
Neue Szenarien in `SCENARIOS`/Statistik: `full` (30 Tage, Werte wie Fixture „Boulderhalle Überhang“: pv 6.930/Vorzeitraum 6.080, visitors 2.410/2.210, contacts 188/143, avgSec 108/114, avgScroll 64, consentRate 0,61, banner 3.950/2.410/1.540, `firstDay` vor dem Vorzeitraum), `neu` (`firstDay` im Zeitraum, `previous.complete` false), `wenig` (pv 12), `dev` (`devMode: true`), `alt` (ohne `previous`), `langerPfad` (Seite `/` + 120 Zeichen), `fehler` (Statistik 503). Prüfungen je Viewport 1440 × 1000 und 402 × 874:
|
||||
```
|
||||
OK aktiv: Block "Das Wichtigste" mit "14 % mehr"
|
||||
OK aktiv: 4 Kacheln, 3 Chips ▲, 1 Chip ▼
|
||||
OK Reiter Heatmap: vorher 0 iframes, nach Klick 2 iframes
|
||||
OK Reiter Einstellungen: "Analyse ausschalten" sichtbar, "aktiv seit"
|
||||
OK Zeitraum 7 → Anfrage range=7, Reiter bleibt Übersicht
|
||||
OK verzögerte erste Antwort (range=30, 1,5 s) überschreibt die spätere (range=7) nicht
|
||||
OK neu: "Vergleich ab" statt Chips, Satz "Seit dem Start der Analyse"
|
||||
OK wenig: Leerzustand
|
||||
OK dev: "Im Dev-Modus wird nicht gemessen."
|
||||
OK alt: keine Chips, keine pageerror
|
||||
OK fehler: "Auswertung gerade nicht erreichbar" + Neu laden
|
||||
OK langer Pfad, 402 px: document.scrollingElement.scrollWidth <= innerWidth
|
||||
OK Einschalten: Banner-Vorschau, Häkchen schaltet Knopf frei (wie bisher)
|
||||
OK keine pageerror in allen Zuständen
|
||||
```
|
||||
Screenshots je Zustand und Viewport nach dem Ordner aus `argv[2]`.
|
||||
Run: `node test/ui/analyse.ui.mjs /tmp/claude-0/-root/072dd97e-d807-40b8-b757-afe9463e87f8/scratchpad/ui-task5` → FAIL
|
||||
|
||||
- [ ] **Step 2: `analyse.js` verdrahten, `analyse.css` schreiben, `dashboard.html` und `funktionen.css` anpassen**
|
||||
|
||||
- [ ] **Step 3: UI-Test grün**
|
||||
|
||||
Run: `node test/ui/analyse.ui.mjs /tmp/claude-0/-root/072dd97e-d807-40b8-b757-afe9463e87f8/scratchpad/ui-task5`
|
||||
Expected: nur `OK`-Zeilen.
|
||||
|
||||
- [ ] **Step 4: Screenshots ansehen und mit Entwurf A vergleichen**
|
||||
|
||||
Desktop- und Handy-Bilder von `full`, `neu`, Einschalten und Einstellungen öffnen (Read-Tool). Erwartet: Aufbau wie Entwurf A, Diagramm und Kontaktkarte gleich hoch, keine abgeschnittenen Texte, keine Überlappungen, Kacheln auf dem Handy 2 × 2. Abweichungen beheben und Step 3 wiederholen.
|
||||
|
||||
- [ ] **Step 5: Suite grün**
|
||||
|
||||
Run: `npm test` → PASS, 0 fail
|
||||
|
||||
- [ ] **Step 6: Commit**
|
||||
|
||||
```bash
|
||||
git add public/analyse.js public/analyse.css public/dashboard.html public/funktionen.css test/ui/analyse.ui.mjs
|
||||
git commit -m "Analyse: Cockpit-Übersicht mit Reitern Übersicht, Heatmap, Einstellungen"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 6: Abschlussprüfung auf Node 20 und Bilder für den Nutzer
|
||||
|
||||
**Files:**
|
||||
- keine Code-Dateien (nur Prüfung); Bilder nach `/root/reports/analyse-ueberarbeitung-2026-10-04/screens/`
|
||||
|
||||
- [ ] **Step 1: Portal-Suite auf Node 20**
|
||||
|
||||
Run: `docker run --rm -v /home/webklar/apps/kundenbereich.webklar.com:/home/webklar/apps/kundenbereich.webklar.com:ro -w /home/webklar/apps/kundenbereich.webklar.com/dev-analyse-neu node:20-alpine node --test test/*.test.js 2>&1 | tail -8`
|
||||
Expected: `fail 0` (bekannte Ausnahme laut Notizen: nur `sharp`-abhängige Tests, falls `sharp` für musl fehlt – dann diese benennen, nicht ignorieren).
|
||||
|
||||
- [ ] **Step 2: Collector-Suite auf Node 24**
|
||||
|
||||
Run: `cd analytics && npm test` → `fail 0`
|
||||
|
||||
- [ ] **Step 3: Bilder für den Nutzer**
|
||||
|
||||
Run: `node test/ui/analyse.ui.mjs /root/reports/analyse-ueberarbeitung-2026-10-04/screens`
|
||||
Expected: nur `OK`; Bilder `full`, `neu`, Heatmap, Einstellungen, Einschalten je Desktop/Handy liegen im Ordner.
|
||||
|
||||
- [ ] **Step 4: Branch-Zustand festhalten**
|
||||
|
||||
Run: `git status --short` (nur `?? node_modules`) und `git log --oneline origin/main..HEAD` (Spec, Plan, Tasks 1–5).
|
||||
|
||||
---
|
||||
|
||||
## Livegang (nur mit OK des Nutzers, nicht Teil der Tasks)
|
||||
|
||||
- **L1** Nutzer pusht: `git -C /home/webklar/apps/kundenbereich.webklar.com/dev-analyse-neu push origin feature/analyse-uebersicht:main` (vorher ggf. auf `origin/main` rebasen und `npm test` erneut).
|
||||
- **L2** Prüfen: `build/` auf dem neuen Stand; `docker ps` zeigt `webklar-kundenbereich-server` und `webklar-analytics` frisch gestartet; `curl -s https://project.webklar.com/a/health`; im Container Stats einer echten aktiven Website abrufen (über `analyticsClient.getStats(siteKey, 30)` in `docker exec -w /app webklar-kundenbereich-server node -e …`) → Felder `previous`, `firstDay`, `totals.avgSec` vorhanden; `https://project.webklar.com/analyse.js` und `/analyse.css` 200.
|
||||
- **L3** Nutzer öffnet Funktionen → Analyse einer Website mit aktiver Analyse und testet Reiter, Zeitraum, Handy.
|
||||
- **L4** Notizen aktualisieren (Memory `analyse-uebersicht-neu.md`).
|
||||
@@ -0,0 +1,346 @@
|
||||
# Webshop (Funktion `shop`) – Umsetzungsplan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: superpowers:subagent-driven-development. Steps use checkbox (`- [ ]`) syntax.
|
||||
|
||||
**Goal:** Kunden fragen im Portal einen Webshop an, das Team gibt frei, der Kunde bestellt die Einrichtung (299 € einmalig),
|
||||
verbindet sein eigenes Stripe-Konto (Connect), pflegt Artikel/Versand/Rechtstexte; Käufer bestellen im Widget auf der
|
||||
Website und zahlen über Stripe Checkout direkt an den Kunden; Bestellungen, Versand, Erstattung, Widerrufe im Portal.
|
||||
|
||||
**Spec:** `docs/superpowers/specs/2026-10-05-webshop-design.md` (Entscheidungen W1–W12). **Fakten:**
|
||||
`/root/reports/webshop-2026-10-05/fakten-server.md`, `fakten-frontend.md`, `fakten-woms.md`, `recherche-recht-stripe.md`
|
||||
(jeder Implementer liest die Abschnitte, die sein Task nennt).
|
||||
|
||||
**Worktrees:** Portal `/home/webklar/apps/kundenbereich.webklar.com/dev-webshop` (Branch `feature/webshop` von
|
||||
`feature/terminplaner-tp2` af63b69; `node_modules` ist ein Symlink, nie `git add -A`). WOMS: neuer Worktree
|
||||
`/home/webklar/apps/ticket.webklar.com/dev-webshop`, Branch `feature/webshop` von `feature/terminplaner-tp2` (Block I).
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- Nichts pushen, nichts in Produktion schreiben, keine Live-Stripe-Aufrufe, keine echten Mails. Dateien explizit stagen.
|
||||
- **Speicher:** jeder Node-Lauf mit `node --max-old-space-size=1024` und `timeout` (Einzeltests `timeout 300`); nie die ganze
|
||||
Suite parallel mehrfach; vor schweren Läufen `free -m`. Ganze Suite nur der Controller.
|
||||
- Node-20-tauglich. `config.*` bei jedem Aufruf lesen (Tests ändern Werte).
|
||||
- Tests: `node --test test/<datei>.test.js`; Mocks aus `test/support/` (`startAppwriteMock`, `startFakeStripe`, `mailSink`,
|
||||
`useTestEnv` vor dem ersten Import von config, `startTestApp`, `customerCookie`); nie echte Dienste.
|
||||
- Pitfall Shell: `grep` ist ein ugrep-Wrapper, der Dateien still überspringt → `command grep -a`.
|
||||
- Kundentexte (Portal) Du-Form; Texte für Käufer (Widget, Käufer-Mails, Muster-Rechtstexte) **Sie-Form**; Rechtstexte für
|
||||
WEBklar-Kunden (LB/AVV) Sie-Form wie die vorhandenen. Fremdtext nur per `textContent`; `href` nur geprüfte https-URLs.
|
||||
- Neue Server-Dienste unter `server/services/shop/`, Front-End-Dateien `public/shop*.js`, `public/shop.css` (nie `termine*`).
|
||||
- Geld immer in ganzen Cent (`Number.isInteger`), nie Gleitkomma-Summen; Preise aus der Datenbank, nie aus dem Browser.
|
||||
- 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.
|
||||
|
||||
- **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)
|
||||
|
||||
- `config.shop` (A1): `{ mode: SHOP_ENABLED ('off'|'pilot'|'on', Standard 'off'), pilotEmails: SHOP_PILOT_EMAILS (Liste, '*' = alle),
|
||||
fassung: '2026-10-05', uploadDir: SHOP_UPLOAD_DIR || <cwd>/upload-data/shop, connectWebhookSecret: STRIPE_CONNECT_WEBHOOK_SECRET || '',
|
||||
bestellAblaufMinuten: 30, maxArtikel: 500, maxBilder: 4, maxVarianten: 30, anonymisierenJahre: 3, loeschTageNachAbschalten: 90,
|
||||
unbezahltLoeschTage: 30 }`. `config.shopSetup = { key:'shopSetup', name:'Webshop – Einrichtung', amountCents:29900,
|
||||
lookupKey:'webklar_shop_einrichtung_29900', oneTime:true, priceNickname:'Webshop-Einrichtung 299 €' }`.
|
||||
`config.collections.shopConfig|shopProducts|shopOrders|shopWiderrufe` (Env-Override `APPWRITE_COLLECTION_SHOP_*`).
|
||||
- `server/services/shop/schalter.js` (A1): `shopModus()`, `shopFuerKunde(email) ⇒ boolean` (Muster `termine/schalter.js`).
|
||||
- `server/services/shop/schema.js` (A2): `SHOP_COLLECTIONS` (reine Daten, kein config-Import; Muster `termine/schema.js`), `SHOP_DATETIMES`.
|
||||
- Register `config.features.shop` (A3) und `STANDARD_HAKEN.shop` (A3).
|
||||
- `server/services/shop/geld.js` (C1, rein): `euro(cent) ⇒ '12,50 €'` (Tausenderpunkt, geschütztes Leerzeichen vor €),
|
||||
`parseEuro(text|number) ⇒ cent|null`, `grundpreisText({ preisCent, menge, einheit }) ⇒ string|''`.
|
||||
- `server/services/shop/einstellungen.js` (C2): `STANDARD_EINSTELLUNGEN`, `normalisiereEinstellungen(eingabe) ⇒ { einstellungen } | { fehler: {feld: text} }`.
|
||||
- `server/services/shop/produkte.js` (C3): `normalisiereProdukt(eingabe, { einstellungen }) ⇒ { produkt } | { fehler }`,
|
||||
`produktOeffentlich(doc, { einstellungen, reserviert }) ⇒ object`, CRUD `listeProdukte(shopId)`, `holeProdukt(shopId, id)`,
|
||||
`speichereProdukt(shop, id|null, produkt)`, `loescheProdukt(shop, id)`, `sortiere(shop, ids)`.
|
||||
- `server/services/shop/preise.js` (C4, rein): `berechne({ produkte, positionen, lieferart, land, einstellungen, reserviert }) ⇒ { ok:true, ... } | { ok:false, fehler }` (Felder unten in C4).
|
||||
- `server/services/shop/rechtstexte.js` (C5): `musterWiderrufsbelehrung(einstellungen)`, `musterWiderrufsformular(einstellungen)`,
|
||||
`musterAgb(einstellungen)`, `versandUndZahlung(einstellungen)`, `normalisiereRechtstexte(eingabe) ⇒ { rechtstexte } | { fehler }`,
|
||||
`rechtstexteHash(rechtstexte, einstellungen)`.
|
||||
- `server/services/shop/kontext.js` (C6): `ladeShopFuerProjekt(customerId, projectId) ⇒ { doc /*portalFeatures*/, shop /*shopConfig, geparst*/ } | null`,
|
||||
`shopPerSiteKey(siteKey) ⇒ ctx | null` (30-s-Cache, `leereCache(siteKey)`), `bereitschaft(ctx) ⇒ { bereit, punkte: [{ id, ok, text }] }`,
|
||||
`istGeoeffnet(ctx) ⇒ boolean`. ctx = `{ doc, shop: { id, customerId, projectId, siteKey, einstellungen, rechtstexte, rechtstexteBestaetigt, stripeAccountId, stripeStatus, geoeffnet, anfrage... }, project }`.
|
||||
- `server/services/shop/connect.js` (D1): `kontoAnlegen({ customer, project }) ⇒ accountId`, `onboardingLink(accountId, { returnUrl, refreshUrl }) ⇒ url`,
|
||||
`kontoStatus(accountId) ⇒ stripeStatus`, `checkoutAnlegen(accountId, params) ⇒ { id, url }`, `sessionHolen(accountId, id)`,
|
||||
`erstatten(accountId, { paymentIntent, betragCent, grund }) ⇒ refund`, `connectEreignis(rawBody, signatur) ⇒ event` (wirft 400/503).
|
||||
Alle Aufrufe mit `{ stripeAccount: accountId }` als Request-Option über `getStripe()`.
|
||||
- `server/services/shop/bestellungen.js` (E1–E3): `legeBestellungAn(ctx, eingabe, { origin, now })`, `schliesseAb({ accountId, session, now }) ⇒ { order, neu }`
|
||||
(idempotent, einzige Stelle für Status `bezahlt`), `verwirf(orderId, grund)`, `setzeStatus(shop, orderId, aktion, extra)`,
|
||||
`erstatte(shop, orderId, { betragCent, grund })`, `listeBestellungen(shopId, filter)`, `bestellungKunde(order)`, `bestellungOeffentlich(order)`, `csv(shopId, von, bis)`,
|
||||
`reserviert(shopId, now) ⇒ Map<schluessel, menge>` (Schlüssel `produktId` oder `produktId:variantId`).
|
||||
- Bestellstatus: `offen` (wartet auf Zahlung) → `bezahlt` → `versendet` | `abholbereit` → `abgeschlossen`; `storniert` (nicht bezahlt / abgelaufen / Zahlung fehlgeschlagen);
|
||||
Zusatzfelder in `daten.erstattungen[]` (Teil-/Vollerstattung, Status `erstattet` wenn voll erstattet).
|
||||
- Mails (E4): Käufer **G09** Bestellbestätigung, **G10** Versand, **G11** Abholbereit, **G12** Eingang Widerruf, **G13** Erstattung,
|
||||
**G14** Zahlung fehlgeschlagen; Händler **K25** Neue Bestellung, **K26** Widerruf eingegangen (beide `bccTeam:false`),
|
||||
**K24** Anfrage-Entscheidung (Blöcke `angenommen`, `abgelehnt`); Team **T21** Neue Webshop-Anfrage (Betreff `[Webshop] …`).
|
||||
Käufer-Mails über die Terminplaner-Warteschlange (`termine/outbox.js einreihen`, `featureId` = portalFeatures-ID des Shops, `bookingId` = Bestell-ID).
|
||||
- Öffentliche API (F1–F3) unter `/api/public/shop/v1/:siteKey`: `GET /` (Konfiguration + Artikel), `GET /bild/:datei`, `POST /bestellung`,
|
||||
`GET /bestellung/:id?t=`, `POST /widerruf`. Kundenroute `/api/shop/*` (B, C7, D2, E5). Team `/api/team/shop/*` (B2). Connect-Webhook
|
||||
`POST /api/shop/stripe-webhook` (D3). Seiten `GET /shop/:siteKey`, `GET /shop/:siteKey/widerruf` (H2).
|
||||
|
||||
---
|
||||
|
||||
## Vorab (Controller)
|
||||
|
||||
- [ ] V1 Ledger `.superpowers/sdd/2026-10-05-webshop/progress.md` anlegen; Basislauf der betroffenen Testdateien (nicht die ganze Suite):
|
||||
`featureRegistry, funktionenCheckout, siteFeatures, mailTemplates, legalDocs, buildLegalHtml, stripeBilling, stripeProductsScript, funktionenTiles, termineView`.
|
||||
|
||||
---
|
||||
|
||||
## Block A: Grundlagen
|
||||
|
||||
### Task A1: Konfiguration und Schalter
|
||||
**Dateien:** `server/config.js`, `server/services/shop/schalter.js` (neu), `test/shopSchalter.test.js` (neu).
|
||||
- `shopSetup` (Katalog) und `config.shop` wie oben; `config.collections.shop*`; Export auf `config`.
|
||||
- `schalter.js`: `shopModus()` liest `config.shop.mode` (unbekannt ⇒ 'off'); `shopFuerKunde(email)`: 'on' ⇒ true, 'pilot' ⇒ E-Mail (klein) in `pilotEmails` oder `'*'` enthalten, sonst false.
|
||||
- `stripeBilling.js`: `PRODUCT_METADATA.shopSetup = 'shop-einrichtung'`, `catalogFor('shopSetup')`; `scripts/stripe-products.mjs` KEYS/catalogEntry um `shopSetup`;
|
||||
Tests `stripeProductsScript.test.js`, `stripeBilling.test.js` gezielt erweitern (Muster termineSetup).
|
||||
- Tests: Modi, Pilotliste, Katalogwerte.
|
||||
|
||||
### Task A2: Schema und Setup-Skript
|
||||
**Dateien:** `server/services/shop/schema.js` (neu), `scripts/shop-setup.mjs` (neu, Muster `scripts/termine-setup.mjs`, `--check`), `test/support/schema.js` (`schemaKeys` für die vier Collections), `test/shopSetup.test.js` (neu, Muster `test/termineSetup.test.js`).
|
||||
Collections (alle ohne Client-Rechte, Felder `required:false`):
|
||||
- `shopConfig`: customerId 36, projectId 36, siteKey 16, anfrage str 4000, anfrageStatus 16, anfrageAt dt, anfrageEntschiedenAt dt, anfrageEntschiedenVon 255,
|
||||
anfrageBegruendung 1000, stripeAccountId 64, stripeStatus 4000, einstellungen 20000, rechtstexte 100000, rechtstexteBestaetigtAt dt,
|
||||
rechtstexteBestaetigtVon 255, rechtstexteHash 64, geoeffnet bool, geoeffnetAt dt, version int. Indexe: idx_customer(customerId), idx_sitekey(siteKey), idx_anfrage(anfrageStatus), idx_account(stripeAccountId).
|
||||
- `shopProducts`: shopId 36, customerId 36, aktiv bool, sort int, name 120, produkt 30000. Indexe idx_shop(shopId, sort).
|
||||
- `shopOrders`: shopId 36, customerId 36, bestellnummer 16, status 16, tokenHash 64, stripeSessionId 128, paymentIntentId 64, summeCent int,
|
||||
erstelltAt dt, ablaufAt dt, bezahltAt dt, kaeuferEmail 320, bestellung 60000, anonymisiertAt dt. Indexe idx_shop_status(shopId,status), idx_nummer(shopId,bestellnummer), idx_session(stripeSessionId), idx_erstellt(shopId, erstelltAt).
|
||||
- `shopWiderrufe`: shopId 36, customerId 36, bestellnummer 32, name 200, email 320, nachricht 2000, eingangAt dt, bestaetigungAt dt, bearbeitetAt dt, orderId 36. Index idx_shop(shopId, eingangAt).
|
||||
- Test: Skript legt im Mock an, `--check` meldet Lücken, zweiter Lauf idempotent.
|
||||
|
||||
### Task A3: Register-Eintrag `shop` und Haken
|
||||
**Dateien:** `server/config.js` (`features.shop`), `server/services/featureRegistry.js` (`STANDARD_HAKEN.shop`), `server/services/legalDocs.js`
|
||||
(`SAETZE.shop = { lb:{slug:'leistungsbeschreibung-webshop',pdf:'Leistungsbeschreibung-Webshop'}, avv:{slug:'avv-webshop',pdf:'AVV-Webshop'}, widerruf:{ wie termine } }`,
|
||||
`FUNKTION_SATZ.shop='shop'`, `aktuelleFassung('shop') = config.shop.fassung`), Tests `featureRegistry.test.js`, `legalDocs.test.js`, `siteFeatures.test.js`,
|
||||
`funktionenRoutes.test.js`, `featureFreischaltung.test.js`, `funktionenTermine.test.js` (Stellen mit festem `['news','termine']` laut fakten-woms §3 → `['news','termine','shop']`).
|
||||
Registereintrag (Muster `termine`; Rechtsunterlagen-Getter wie `termineRechtsunterlage`, aber mit `config.shop.fassung`; config importiert keine Dienste):
|
||||
`key:'shop'`, `product:'shopSetup'`, `label:{ name:'Webshop', kurz:'Shop', keine:'keinen Webshop' }`, `priceLabel:'299 €'`, `setupEstimate:'meist innerhalb von 2–3 Wochen'`,
|
||||
`consent` (Fassung = `config.shop.fassung`; `knopf:'Zahlungspflichtig bestellen · 299 €'`; Items in dieser Reihenfolge, alle `haken:true`):
|
||||
1. `unterlagen`: „Ich stimme der Leistungsbeschreibung Webshop und dem Auftragsverarbeitungsvertrag Webshop in der Fassung <F> zu.“
|
||||
2. `verantwortung`: „Ich verkaufe als Unternehmer in eigenem Namen. Für meine Produkte, Preise, Produktangaben und Rechtstexte (AGB, Widerrufsbelehrung, Impressum, Datenschutzerklärung) bin ich selbst verantwortlich; WEBklar stellt Muster bereit und berät nicht rechtlich. Ich verkaufe keine der in der Leistungsbeschreibung ausgeschlossenen Waren.“
|
||||
3. `stripe`: „Die Zahlungen meiner Käufer laufen über mein eigenes Stripe-Konto zu den Bedingungen von Stripe. WEBklar erhält und verwahrt kein Geld meiner Käufer und erhebt keine Umsatzbeteiligung.“
|
||||
4. `beginn`: Wortlaut wie Terminplaner `beginn`, mit „Einrichtung des Webshops“.
|
||||
5. `information`: „Ich habe die Pflichtinformationen zu dieser Bestellung gelesen: Leistung (Einrichtung des Webshops für diese Website: Einbau des Shop-Widgets, Verwaltung im Portal, Muster-Rechtstexte), Gesamtpreis 299 € einmalig (<ustHinweis>), voraussichtliche Dauer meist innerhalb von 2–3 Wochen, keine laufende Gebühr von WEBklar; der Webshop läuft, solange WEBklar die Website betreibt. Die Widerrufsbelehrung mit Muster-Widerrufsformular habe ich gelesen.“
|
||||
`returnHash:'webshop'`, `returnProjekt:true`, `ticket: { topic: (p) => 'Webshop einbauen – <Projekt>', details: async (ctx) => (await import('./services/shop/ticketDetails.js')).shopTicketDetails(ctx) }`,
|
||||
`mails:{ funktion:'Webshop', formen:{ dein:'Dein Webshop', nom:'der Webshop', akk:'den Webshop', gen:'des Webshops', pron:'ihn' }, knopf:'Webshop öffnen',
|
||||
danach:'Danach kannst Du im Portal Artikel anlegen, Dein Stripe-Konto verbinden und den Shop öffnen.', bereit:'Dein Webshop ist eingebaut. Sobald Stripe-Konto, Artikel und Rechtstexte fertig sind, kannst Du ihn im Portal öffnen.',
|
||||
preisHinweis:'einmalig, <ustHinweis>; keine laufende Gebühr von WEBklar, Stripe-Gebühren zahlst Du direkt an Stripe', anleitung:false }`,
|
||||
`requiresWartungsAbo:false`, `ticketFreischaltung:true`, `disabledMessage:'Der Webshop ist für diese Website abgeschaltet. Schreib uns im Chat, wenn Du ihn wieder möchtest.'`,
|
||||
`orderedConflictMessage:'Der Webshop ist für dieses Projekt schon bestellt.'`, `unavailableMessage:'Der Webshop ist noch nicht verfügbar.'`.
|
||||
`server/services/shop/ticketDetails.js` (neu): Kopf (Funktion, Projekt, Domain, Kunde, siteKey), Anfrage-Kurzfassung (Warenart, Versand/Abholung, Artikelzahl; aus shopConfig, Lesefehler ⇒ ohne), Snippet
|
||||
`<div data-webklar-shop="<siteKey>"></div>` + `<script src="https://project.webklar.com/embed/shop.js" defer></script>`, Schritte (Shop-Seite + Menüpunkt anlegen, Snippet einsetzen, auf **allen** Seiten im Footer Link „Vertrag widerrufen“ → `https://project.webklar.com/shop/<siteKey>/widerruf`, Datenschutz-Absatz Webshop (`docs/funktionen/shop-datenschutz-absatz.md`) einfügen, Impressum prüfen, Vorschau `https://project.webklar.com/shop/<siteKey>` prüfen, Ticket schließen ⇒ Freischaltung), „Zeit im Arbeitsblatt, nicht auf Abo-Stunden“, Verweis WOMS (`anleitungsLink`).
|
||||
Haken `STANDARD_HAKEN.shop`: `verfuegbar: (email) => shopFuerKunde(email)`, `bestellpruefung` (dynamisch import `./shop/anfrage.js` `anfrageOffenePunkte({customerId, projectId})` ⇒ `[]` wenn Anfrage `angenommen`, sonst `['Deine Webshop-Anfrage ist noch nicht freigegeben.']`), `unterlagenBereit: () => featureLegalReady('shop')`, `belehrung: termine-Belehrung (gleiche Quelle)`, kein `freischaltbar` (geschlossenes Ticket genügt).
|
||||
Für A3 genügt in `shop/anfrage.js` ein Stub mit `anfrageOffenePunkte`, den Block B ausbaut. `featureConsentTexte.test.js`: Hash-Pin für shop ergänzen (Muster termine).
|
||||
Tests: Registerwerte, `oeffentlicheAnsicht('shop')`, Haken, legalPaths('shop'), `featureLegalReady('shop')` false solange Dateien fehlen (Block J legt sie an), Ticket-Texte.
|
||||
|
||||
---
|
||||
|
||||
## Block B: Anfrage („auf Anfrage“)
|
||||
|
||||
### Task B1: Anfrage-Dienst und Kundenroute
|
||||
**Dateien:** `server/services/shop/anfrage.js`, `server/routes/shop.js` (neu, Mount `/api/shop` in `server/index.js` neben `/api/termine`), `server/middleware/kundenansichtGuard.js`, `test/shopAnfrage.test.js`.
|
||||
- `shopConfig`-Dokument-ID = portalFeatures-ID (`ensureFeatureDoc(customerId, projectId, 'shop')` liefert sie); `ensureShopConfig(featureDoc)` legt es an (siteKey kopiert).
|
||||
- `normalisiereAnfrage(eingabe) ⇒ { anfrage } | { fehler }`: `warenart` ∈ `waren|lebensmittel|kleidung|kunst|sonstiges`, `beschreibung` 10–1500 Zeichen, `artikelzahl` ∈ `bis20|bis100|mehr`,
|
||||
`versand` bool, `abholung` bool (mind. eins), `unternehmer === true`, `keineAusgeschlossenen === true`, `nachricht` ≤ 1000.
|
||||
- `stelleAnfrage({ customerId, projectId, eingabe, person, now })`: Projekt gehört Kunde (`assertOwnProject(..., {forNewOrder:true})`), Schalter `shopFuerKunde`, nur wenn Status `''|abgelehnt|zurueckgezogen` (sonst 409),
|
||||
speichert JSON + Status `offen` + `anfrageAt`, schickt **T21** an das Team (`sendAdminNotice`, Fehler nur loggen).
|
||||
- `zieheZurueck` (nur `offen`), `anfrageAnsicht(shop)`, `anfrageOffenePunkte({customerId, projectId})`.
|
||||
- Routen (requireSession, viewAs ⇒ 403 für POST): `GET /api/shop/status?projectId=` ⇒ `{ verfuegbar, anfrage: {status, anfrage, at, begruendung} | null, state, ... }` (Grundgerüst, C7 erweitert),
|
||||
`POST /api/shop/anfrage {projectId, anfrage}`, `POST /api/shop/anfrage/zurueckziehen {projectId}`. Rate-Limit 20/h je Kunde (`createWriteLimiter`).
|
||||
- Vorlage `server/mail-templates/T21 Neue Webshop-Anfrage.html` (Muster T16-Layout; Betreff `[Webshop] Neue Anfrage: {{kunde_name}}`; Platzhalter kunde_name, email, projekt, warenart, beschreibung, artikelzahl, lieferung, nachricht, woms_url);
|
||||
`test/mailTemplates.test.js`: EXPECTED, `[Webshop]` in die T-Betreff-Regex, TEAM_FOOTER.
|
||||
|
||||
### Task B2: Team-Entscheidung und K24
|
||||
**Dateien:** `server/routes/team/shop.js` (neu, Mount in `server/routes/team/index.js` als `/shop`), `server/services/shop/anfrage.js`, `server/mail-templates/K24 Webshop-Anfrage.html`, Tests.
|
||||
- `GET /api/team/shop/:customerId` ⇒ je Projekt `{ projectId, projectName, anfrage, stripe: {accountId?, status}, geoeffnet, zahlen: { artikel, offeneBestellungen } }`.
|
||||
- `POST /api/team/shop/:customerId/anfrage { projectId, entscheidung: 'annehmen'|'ablehnen', begruendung }` (Begründung Pflicht bei ablehnen, ≤ 1000) unter `withLocks`;
|
||||
nur aus `offen`; speichert Entscheidung, Person (`req.session.name/email`), Zeit; **K24** an den Kunden (Blöcke `angenommen` mit Link `dashboard.html#webshop?projekt=<id>` und Preis 299 €, `abgelehnt` mit Begründung).
|
||||
- Tests: Annehmen/Ablehnen, falscher Zustand 409, Mail-Inhalte, Bestell-Haken liefert danach `[]`.
|
||||
|
||||
---
|
||||
|
||||
## Block C: Shop-Daten und Kundenverwaltung (Server)
|
||||
|
||||
### Task C1: Geld und Grundpreis (rein)
|
||||
`server/services/shop/geld.js` + `test/shopGeld.test.js`: `euro`, `parseEuro` ('12,5' / '12,50 €' / '1.234,56' / 12.5 ⇒ Cent; negative, > 100000 €, mehr als 2 Nachkommastellen ⇒ null),
|
||||
`grundpreisText({preisCent, menge, einheit})`: Einheiten `g|kg|ml|l|m|m2`; Bezug 1 kg / 1 l / 1 m / 1 m² (g/ml ⇒ kg/l); Ergebnis „(12,50 € / 1 kg)“; ohne Menge ''; Rundung kaufmännisch auf Cent.
|
||||
|
||||
### 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),
|
||||
`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|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'`.
|
||||
`speichereEinstellungen(shop, einstellungen)` erhöht `version`, leert Kontext-Cache. Fehlerobjekt je Feld (deutsche Texte, Du-Form).
|
||||
|
||||
### Task C3: Produkte (rein + CRUD)
|
||||
`server/services/shop/produkte.js` + Tests. Produktfelder: `name` 1–120, `kategorie` ≤ 40, `kurz` ≤ 300, `beschreibung` ≤ 5000, `preisCent` (> 0, ≤ 10 000 000),
|
||||
`steuersatz` ∈ 0|7|19 (bei Kleinunternehmer ignoriert ⇒ 0), `grundpreis {menge, einheit} | null`, `bestand` int ≥ 0 | null (unbegrenzt), `aktiv`,
|
||||
`varianten { name ≤ 30, werte: [{ id (8 Zeichen, server), name ≤ 40, preisCent, bestand|null }] ≤ 30 } | null` (mit Varianten gilt Preis/Bestand der Variante),
|
||||
`bilder: [imageId] ≤ 4`, `lieferart: 'beides'|'abholung'`, `widerruf: 'gesetzlich'|'verderblich'|'individuell'|'hygiene'`,
|
||||
`sicherheit { hersteller: 'selbst' | { name, anschrift, kontakt }, euVerantwortlicher? { name, anschrift, kontakt }, hinweise ≤ 2000 }`,
|
||||
`lebensmittel { aktiv, pflichtangaben (Pflicht wenn aktiv, ≤ 4000) }`.
|
||||
CRUD auf `shopProducts` (Feld `produkt` = JSON), Grenze `config.shop.maxArtikel`; `produktOeffentlich` liefert nur Anzeigefelder + `preisText`, `grundpreisText`,
|
||||
`verfuegbar` (Bestand minus Reservierung > 0 bzw. je Variante), `ausverkauft`, `widerrufText` (z. B. „Vom Widerruf ausgeschlossen: schnell verderbliche Ware“), Bild-URLs
|
||||
`/api/public/shop/v1/<siteKey>/bild/<imageId>-s.webp` und `-l.webp`.
|
||||
|
||||
### Task C4: Warenkorb-Berechnung (rein)
|
||||
`server/services/shop/preise.js` + Tests (viele Fälle). `berechne({ produkte /*Map id⇒produkt mit id*/, positionen: [{ produktId, varianteId?, menge }], lieferart: 'versand'|'abholung', land, einstellungen, reserviert })`:
|
||||
Prüft: 1–50 Positionen, Menge 1–99 ganzzahlig, Produkt aktiv, Variante passt, Bestand (minus `reserviert`) reicht, `abholung` nur wenn aktiv; `versand` nur wenn aktiv, Land erlaubt und **kein** Artikel `lieferart:'abholung'`;
|
||||
Mindestbestellwert. Versandkosten = `kostenCent[land]`, 0 ab `freiAbCent` (Zwischensumme ≥ Schwelle) oder bei Abholung. Steuer: bei Kleinunternehmer keine; sonst je Satz enthaltene USt = round(brutto * satz / (100 + satz)) je Satz über die Summe der Positionen,
|
||||
Versandkosten anteilig nach Bruttowert der Sätze (Rest-Cent auf den höchsten Satz). Ergebnis `{ ok:true, positionen:[{ produktId, varianteId, name, varianteText, menge, einzelCent, summeCent, steuersatz, widerruf, einzelText, summeText }], zwischensummeCent, versandCent, gesamtCent, steuer:[{ satz, betragCent, text }], texte:{ zwischensumme, versand, gesamt, steuerHinweis } }`
|
||||
(`steuerHinweis`: „Gemäß § 19 UStG wird keine Umsatzsteuer berechnet.“ oder „inkl. 19 % USt: 3,19 €; inkl. 7 % USt: …“), Fehler `{ ok:false, fehler: { code, text, produktId? } }` mit Sie-Texten für Käufer.
|
||||
|
||||
### Task C5: Muster-Rechtstexte des Händlers
|
||||
`server/services/shop/rechtstexte.js` + `docs/funktionen/shop-muster-rechtstexte.md` (Quelle, gleicher Wortlaut, Kopf „Muster – keine Rechtsberatung“) + Tests (Pins).
|
||||
- `musterWiderrufsbelehrung(e)`: gesetzliches Muster für Waren (Anlage 1 zu Art. 246a § 1 Abs. 2 S. 2 EGBGB) in aktueller Fassung **inkl. Hinweis auf die Widerrufsfunktion** (nach `recherche-recht-stripe.md` B2/B3),
|
||||
Anbieter-Name/Anschrift/E-Mail eingesetzt, Variante Rücksendekosten (Käufer trägt unmittelbare Kosten / Händler trägt), Fristbeginn für eine Ware bzw. mehrere getrennt gelieferte Waren (ein Text mit beiden Varianten laut Gestaltungshinweis),
|
||||
Ausschlussgründe als Absatz „Ausschluss des Widerrufsrechts“ (§ 312g Abs. 2 Nr. 1, 2, 3 BGB).
|
||||
- `musterWiderrufsformular(e)` (Anlage 2). `musterAgb(e)`: kurze Muster-AGB (Geltung, Vertragspartner, Vertragsschluss: Bestellung durch Klick „Zahlungspflichtig bestellen“, Annahme durch Bestellbestätigung per E-Mail nach Zahlungseingang; Preise und Versandkosten; Zahlung über Stripe, Zahlungsarten; Lieferung, Lieferzeit, Liefergebiet, Abholung; Eigentumsvorbehalt; Widerrufsrecht Verweis; Gewährleistung gesetzlich; Vertragstext wird nicht vom Anbieter gespeichert, Bestellbestätigung enthält ihn; Sprache Deutsch; Streitbeilegung: keine Bereitschaft/Verpflichtung zur Teilnahme an Verbraucherschlichtung (§ 36 VSBG)).
|
||||
- `versandUndZahlung(e)` (erzeugt, nicht frei editierbar). `normalisiereRechtstexte({ agb ≤ 30000, widerrufsbelehrung ≤ 20000, widerrufsformular ≤ 5000, impressumUrl, datenschutzUrl (https) })`, `rechtstexteHash`.
|
||||
|
||||
### Task C6: Kontext und Bereitschaft
|
||||
`server/services/shop/kontext.js` + Tests: Laden per Projekt und per siteKey (`findActiveBySiteKey(siteKey,'shop')` + shopConfig + Projekt), JSON-Felder sicher parsen (kaputt ⇒ Standard), Cache 30 s.
|
||||
`bereitschaft(ctx)` Punkte: `einbau` (state active), `stripe` (`stripeStatus.charges_enabled`), `rechtstexte` (bestätigt und Hash = aktueller Hash), `einstellungen` (normalisiert ok), `artikel` (≥ 1 aktiv und lieferbar), jeweils mit Du-Text.
|
||||
`istGeoeffnet(ctx)` = `shop.geoeffnet && alle Punkte außer rechtstexte-Hash-Gleichheit ok && rechtstexteBestaetigt vorhanden` (nach Spec 4.6: geändert, aber früher bestätigt ⇒ bleibt offen mit der zuletzt bestätigten Fassung; dafür speichert C7 beim Bestätigen eine Kopie `rechtstexteBestaetigt` in `rechtstexte` JSON: `{ entwurf, bestaetigt }`).
|
||||
|
||||
### Task C7: Kundenrouten Verwaltung
|
||||
`server/routes/shop.js` + Tests (`test/shopRoutes.test.js`). Alle mit requireSession + eigenes Projekt + Funktion `setup|active` (lesend auch `disabled`), Rate-Limit 300/h je Kunde, Kundenansicht: Lesen ja; Schreiben von Artikeln/Einstellungen/Rechtstexten erlaubt (Guard-Liste ergänzen), **nicht** Stripe, Öffnen, Erstatten.
|
||||
- `GET /api/shop/status?projectId=` (vollständig): Funktion (state, ticket), Anfrage, Bereitschaft, geoeffnet, stripe (Status ohne Geheimnisse), Zahlen.
|
||||
- `GET|PUT /api/shop/einstellungen`, `GET /api/shop/produkte`, `POST /api/shop/produkte`, `PUT /api/shop/produkte/:id`, `DELETE /api/shop/produkte/:id`, `POST /api/shop/produkte/sortierung {ids}`.
|
||||
- `GET /api/shop/rechtstexte` (inkl. `muster` zum Zurücksetzen und `versandUndZahlung`), `PUT /api/shop/rechtstexte` (speichert Entwurf), `POST /api/shop/rechtstexte/bestaetigen { hash }` (Haken-Text aus Spec 4.6; speichert Kopie, Zeit, Person, Hash).
|
||||
- `POST /api/shop/oeffnen { projectId, offen: bool }` (öffnen nur wenn `bereitschaft.bereit`; schließen immer).
|
||||
- Bilder: `POST /api/shop/bild?projectId=` (octet-stream ≤ 8 MB, Muster news.js) über `server/services/shop/bilder.js` (aus `newsImages.js` verallgemeinert: Wurzel `config.shop.uploadDir/<siteKey>/`, 1600/480 px WebP, Sidecar, `claim` beim Produkt-Speichern, `purgeUnclaimed` 24 h, `deleteShopImage`, `deleteShopImages(siteKey)`); Vorschau `GET /api/shop/bild/:projectId/:datei`.
|
||||
|
||||
---
|
||||
|
||||
## Block D: Stripe Connect
|
||||
|
||||
### Task D1: Connect-Dienst + Fake-Stripe
|
||||
`server/services/shop/connect.js`, `test/support/fakeStripe.js` (Header `Stripe-Account` aufzeichnen; `POST /v1/accounts`, `GET /v1/accounts/:id`, `POST /v1/account_links`, Checkout-Sessions je Konto, `POST /v1/refunds`; vorhandenes Verhalten unverändert), `test/shopConnect.test.js`.
|
||||
- `kontoAnlegen`: `accounts.create({ country:'DE', email, business_profile:{ url: liveUrl|undefined, name: projectName }, controller:{ fees:{payer:'account'}, losses:{payments:'stripe'}, stripe_dashboard:{type:'full'}, requirement_collection:'stripe' }, capabilities:{ card_payments:{requested:true}, transfers:{requested:true} }, metadata:{ webklar:'shop', customerId, projectId } })` – genaue Parameter nach `recherche-recht-stripe.md` A1 prüfen.
|
||||
- `onboardingLink`: `accountLinks.create({ account, type:'account_onboarding', refresh_url, return_url, collection_options:{fields:'eventually_due'} })`.
|
||||
- `kontoStatus`: `accounts.retrieve` ⇒ `{ charges_enabled, payouts_enabled, details_submitted, currently_due: [...], disabled_reason, geprueftAt }`.
|
||||
- `checkoutAnlegen(accountId, params)` = `checkout.sessions.create(params, { stripeAccount: accountId, idempotencyKey })`; `sessionHolen`; `erstatten` = `refunds.create({ payment_intent, amount, reason:'requested_by_customer', metadata }, { stripeAccount })`.
|
||||
- `connectEreignis(raw, sig)`: 503 wenn `config.shop.connectWebhookSecret` leer, sonst `getStripe().webhooks.constructEvent(raw, sig, secret)`.
|
||||
|
||||
### Task D2: Kundenrouten Stripe
|
||||
`server/routes/shop.js`: `POST /api/shop/stripe/einrichten { projectId }` (kein viewAs; legt Konto an falls keins, speichert `stripeAccountId` unter Sperre, liefert `{ url }` Onboarding),
|
||||
`POST /api/shop/stripe/link { projectId }` (neuer Onboarding-Link), `POST /api/shop/stripe/aktualisieren { projectId }` (Status neu lesen + speichern). Rücksprung `dashboard.html#webshop?stripe=zurueck&projekt=<id>`, refresh `...?stripe=neu&projekt=<id>`. Tests mit Fake-Stripe.
|
||||
|
||||
### Task D3: Connect-Webhook
|
||||
`server/routes/shopWebhook.js` (Mount `POST /api/shop/stripe-webhook` in index.js **vor** `/api/shop`-Router, nutzt `req.rawBody` des globalen Parsers), Tests mit signierten Events (eigenes Test-Secret).
|
||||
Ereignisse: `account.updated` ⇒ `stripeStatus` des Shops mit diesem Konto (Index `idx_account`); `checkout.session.completed` (payment_status `paid`) und `checkout.session.async_payment_succeeded` ⇒ `schliesseAb({ accountId: event.account, session })`;
|
||||
`checkout.session.async_payment_failed` ⇒ `verwirf(..., 'zahlung_fehlgeschlagen')` + G14; `checkout.session.expired` ⇒ `verwirf(..., 'abgelaufen')`; `charge.refunded` ⇒ Erstattung nachtragen (nur wenn noch nicht über das Portal gebucht, über `refund.id`).
|
||||
Nur Sessions mit `metadata.wkShop === '1'` werden behandelt; unbekannte ⇒ 200 ohne Wirkung. Fehler ⇒ 500 (Stripe wiederholt), alles idempotent.
|
||||
|
||||
---
|
||||
|
||||
## Block E: Bestellungen
|
||||
|
||||
### Task E1: Bestellung anlegen (Server-Kern)
|
||||
`server/services/shop/bestellungen.js` + Tests. `legeBestellungAn(ctx, { positionen, lieferart, land, rueckUrl }, { now })`:
|
||||
unter `withLocks(['shop-bestand:'+shopId])`: `istGeoeffnet` (sonst 409 `geschlossen`), Produkte laden, `reserviert(shopId, now)` (Summe der `offen`-Bestellungen mit `ablaufAt > now`), `berechne`; Bestellnummer `JJ-XXXXXX` (base32 ohne 0/1/O/I, eindeutig im Shop), Token 32 Byte (nur Hash speichern),
|
||||
Dokument `offen` mit Schnappschuss `{ positionen, summen, lieferart, land, einstellungenAuszug (Anbieter, Lieferzeit, Zahlungsarten), rechtstexteHash der bestätigten Fassung, verlauf:[{at, was:'angelegt'}] }`, `ablaufAt = now + 30 min`;
|
||||
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>' }`,
|
||||
`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; Klarna/SEPA nicht freigeschaltet ⇒ einmal mit nur `card` wiederholen und dem Händler einen Hinweis in `stripeStatus.hinweis` speichern.
|
||||
|
||||
### 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);
|
||||
setzt `bezahlt`, `bezahltAt`, `paymentIntentId`, Käuferdaten aus `customer_details` (name, email, phone, address) und `shipping_details`/`collected_information.shipping_details`, zieht Bestand ab (Produkte unter `shop-bestand`-Sperre), schickt G09 (Käufer) und K25 (Händler). Rückgabe `{ order, neu }`.
|
||||
`verwirf(orderId, grund)` nur aus `offen`. Minutenjob (`server/services/shop/jobs.js` `minutenJob(now)`): `offen` mit `ablaufAt < now - 5 min` ⇒ Session bei Stripe lesen: `complete`+`paid` ⇒ `schliesseAb`, `expired`/`open` ⇒ (open: `checkout.sessions.expire`) `verwirf('abgelaufen')`; Lesefehler ⇒ nächster Lauf.
|
||||
|
||||
### Task E3: Händler-Aktionen, Erstattung, CSV
|
||||
`setzeStatus(shop, orderId, aktion)`: `versendet { versanddienst?, sendungsnummer? }` (aus bezahlt) ⇒ G10; `abholbereit` (aus bezahlt, Lieferart abholung) ⇒ G11; `abgeschlossen` (aus bezahlt|versendet|abholbereit); `storniert` (nur offen).
|
||||
`erstatte(shop, orderId, { betragCent, grund })`: nur bezahlt/versendet/abholbereit/abgeschlossen, Betrag ≤ Rest; `erstatten()` mit Idempotenz-Schlüssel; Eintrag in `erstattungen[]`, voll ⇒ Status `erstattet`; G13 an Käufer. Fehler ⇒ 502 mit Du-Text.
|
||||
`csv(shopId, von, bis)`: `;`-getrennt, UTF-8 mit BOM, Spalten Bestellnummer, Datum, Status, Name, E-Mail, Lieferart, Adresse, Positionen, Versand, Gesamt, Erstattet; Formel-Injektion verhindern (Zellen mit `= + - @` voranstellen `'`).
|
||||
Kundenrouten: `GET /api/shop/bestellungen?projectId=&status=&seite=`, `GET /api/shop/bestellungen/:id`, `POST /api/shop/bestellungen/:id/aktion { aktion, ... }`, `POST /api/shop/bestellungen/:id/erstatten`, `GET /api/shop/bestellungen.csv?projectId=&von=&bis=`, `GET /api/shop/widerrufe`, `POST /api/shop/widerrufe/:id/erledigt`.
|
||||
|
||||
### Task E4: Mails
|
||||
Vorlagen G09–G14 (Muster `G03 …`: Gästelayout, `{{farbe}}`, `{{betrieb_name}}`, Sie-Form, kein WEBklar-Logo; Fußzeile mit Anbieter-Impressumsdaten, Link „Vertrag widerrufen“ `{{widerruf_url}}`), K24 (B2), K25, K26 (K-Layout, Du-Form), T21 (B1).
|
||||
**G09** Bestellbestätigung: Betreff „Ihre Bestellung {{bestellnummer}} bei {{betrieb_name}}“; Anbieter (Name, Anschrift, E-Mail), Bestellnummer, Datum, Positionen (vorgerenderter Text/HTML-Tabelle aus dem Server, Platzhalter `positionen_html` wird **nicht** escaped? – Nein: die Vorlage-Engine escaped immer; deshalb Positionen als Textblock `{{positionen}}` mit Zeilenumbrüchen), Zwischensumme, Versand, Gesamt, Steuerhinweis, Lieferart + Adresse/Abholort, Lieferzeit, Zahlungsart, Widerrufsausschlüsse (Block), **vollständige AGB, Widerrufsbelehrung, Muster-Widerrufsformular** (Blöcke mit Text), Link Widerrufsfunktion.
|
||||
`server/services/shop/mails.js`: `anKaeufer(art, ctx, order, extra, now)` ⇒ `einreihen({ featureId: ctx.doc.$id, bookingId: order.$id, art, an, fromName: shopname, replyTo: anbieter.email, template: art, values, blocks })` (Outbox `art` ≤ 16; die Outbox prüft Budget; Grenze 3 Mails/h je Empfänger über `limits.darfEmpfaenger` beachten – für G09 nicht anwenden, G09 ist Vertragspflicht);
|
||||
`anHaendler(template, ctx, values, blocks)` ⇒ `sendCustomerMail(kundenEmail, {..., bccTeam:false})`. Outbox-Erweiterung: `GAST_ARTEN`/Budgetklassen so ändern, dass G09–G14 als Gästemails zählen (G09, G12 Vorrang); der Mail-Job läuft auch, wenn `TERMINE_ENABLED=off` aber `SHOP_ENABLED≠off` (in `termine/jobs.js` die Bedingung für den Mailteil erweitern, sonst unverändert). `test/mailTemplates.test.js` (EXPECTED, GUEST-Liste + Platzhaltertabellen, K-Liste).
|
||||
|
||||
### Task E5: Widerrufe (§ 356a BGB)
|
||||
`server/services/shop/widerrufe.js` + Tests: `erfasseWiderruf(ctx, { name, email, bestellnummer, nachricht }, { now, ip })`: Pflicht Name (2–200), E-Mail, Bestellnummer (≤ 32; muss nicht existieren); passende Bestellung (Nummer + E-Mail gleich) ⇒ `orderId` + Verlauf; speichert, schickt sofort **G12** Eingangsbestätigung (Inhalt der Erklärung, Datum und Uhrzeit Berlin, Bestellnummer) und **K26** an den Händler; Antwort `{ ok, eingang: 'TT.MM.JJJJ, HH:MM Uhr' }`. Grenzen `darfWiderrufsversuch` (10/IP/h) + 20/Shop/h.
|
||||
|
||||
---
|
||||
|
||||
## Block F: Öffentliche API
|
||||
|
||||
### Task F1: Lesen
|
||||
`server/routes/publicShop.js` (Mount `/api/public/shop/v1` **vor** dem globalen Parser, eigenes `express.json({limit:16kb})`, Antwortmuster wie `publicTermine.js schreibe()`), Tests `test/publicShop.test.js`.
|
||||
`GET /:siteKey` ⇒ `{ aktiv:false }` (unbekannt/inaktiv/aus/nicht verfügbar, kein Orakel) oder `{ aktiv:true, offen: istGeoeffnet, shop:{ name, akzent, anbieter (Name, Anschrift, E-Mail, Telefon), lieferzeit, lieferlaender:[{code,name}], abholung:{aktiv, adresse, hinweis}, zahlungsarten:[Texte], versandText, mindestbestellText, kleinunternehmer, steuerHinweisKurz }, kategorien, produkte:[produktOeffentlich], rechtstexte:{ agb, widerrufsbelehrung, widerrufsformular, versandUndZahlung, impressumUrl, datenschutzUrl } (bestätigte Fassung), widerrufUrl, formToken }`.
|
||||
Rechtstexte nur, wenn eine bestätigte Fassung existiert. `GET /:siteKey/bild/:datei` (`^[a-z0-9-]{1,64}-(s|l)\.webp$`, Bild muss zu einem Produkt des Shops gehören, Cache 1 Tag, nosniff). Grenzen `darfAnfrage` 120/IP/min, 600/Shop/min. CORS GET `*`.
|
||||
|
||||
### Task F2: Bestellung und Status
|
||||
`POST /:siteKey/bestellung { positionen, lieferart, land, rueckUrl, formToken, kontakt_url (Honeypot) }`: Herkunftsprüfung wie `erlaubteHerkunft` (Origin-Host ∈ Projektdomains + Portal-Host), Formtoken (HMAC, ≥ 3 s), Honeypot ⇒ scheinbarer Erfolg ohne Wirkung (`{ url: rueckUrl }`), Grenzen 10/IP/10 min + 60/Shop/h, Mindestantwortzeit 300 ms; ⇒ `{ url }` (Stripe) oder Fehler `{ fehler:{ _: text }, code }`.
|
||||
Vorab-Berechnung ohne Anlegen: `POST /:siteKey/pruefen { positionen, lieferart, land }` ⇒ Ergebnis von `berechne` (Texte), damit die Kasse die Zusammenfassung **vom Server** zeigt.
|
||||
`GET /:siteKey/bestellung/:id?t=` ⇒ Token prüfen (timingSafeEqual auf Hash) ⇒ bei `offen` einmal `sessionHolen` + ggf. `schliesseAb`; Antwort `{ status, bestellnummer, gesamtText, lieferart, email (maskiert) }`.
|
||||
|
||||
### Task F3: Widerruf und Seiten
|
||||
`POST /:siteKey/widerruf { name, email, bestellnummer, nachricht, formToken, kontakt_url }` (Origin: Projektdomains + Portal-Host; Formtoken; Grenzen; Mindestantwortzeit) ⇒ `{ ok:true, eingang }`.
|
||||
Funktioniert auch bei geschlossenem, aber `active`/`disabled`-Shop (Widerrufsrecht läuft weiter); nur `off`/unbekannt ⇒ `{aktiv:false}`.
|
||||
|
||||
---
|
||||
|
||||
## Block G: Portal-Oberfläche
|
||||
|
||||
### Task G1: Verdrahtung, Kachel, Bestellseite verallgemeinern
|
||||
`public/dashboard.html` (`/shop.css`, `<section class="kb-view" id="view-webshop">` mit Kopf „Webshop“ + `#webshop-body`), `public/app.js` (`FEATURE_LABELS.shop`, `VIEW_HASHES.webshop`, `viewRefreshers.webshop` ⇒ `import('/shop.js').then(m => m.initShop(...))`, `APP_PARENT.webshop = 'funktionen'` am Ende), `public/funktionen.js` (Kachel „Webshop“: Zustände laut Spec-Tabelle 2; Text „Dein eigener Online-Shop auf Deiner Website. Zahlungen direkt auf Dein Stripe-Konto. 299 € einmalig.“; Klick ⇒ `#webshop` mit Wunschprojekt), `public/funktion-bestellen.js` (Checkout-Pfad und Zusammenfassung über Parameter `key`/`zusammenfassung`, Termine-Verhalten unverändert; Tests bleiben grün), Tests (`funktionenTiles.test.js`, neue statische Verdrahtungstests).
|
||||
|
||||
### Task G2: Hülle `#webshop`, Anfrage, Status
|
||||
`public/shop.js` (Hülle nach `termine.js`: Rückkehr lesen `?bestellt=1`, `?stripe=…`, Projektwahl, Zustände: nicht verfügbar ⇒ Infokarte; keine Anfrage ⇒ Anfrageformular (`public/shop-anfrage.js`); Anfrage offen/abgelehnt ⇒ Karte; freigegeben + `off` ⇒ Bestellseite (`funktion-bestellen.js` mit key `shop`, Belehrung/Unterlagen aus `GET /api/funktionen` `features.shop` + `legal.shop`); `ordered` ⇒ Statuskarte „Zahlung fortsetzen“; `setup|active|disabled` ⇒ Reiter), `public/shop-dom.js` (Helfer: `h`, `request`, Felder – Kopie der termine-dom-Helfer, nicht importieren), `public/shop.css` (Tokens `--kb-*`, Reiterleiste 5 Spalten, mobil scrollbar), Test `test/shopView.test.js` mit `installFakeDom`.
|
||||
|
||||
### Task G3: Reiter Übersicht + Stripe
|
||||
`public/shop-tab-uebersicht.js`: Checkliste (Bereitschaft vom Server), Schalter „Shop geöffnet“, Stripe-Karte (Einrichten / Angaben ergänzen / Status aktualisieren / Dashboard-Link `https://dashboard.stripe.com/`), Vorschau-Link `https://project.webklar.com/shop/<siteKey>`, Einbau-Snippet nur lesen, Kennzahlen. Test.
|
||||
|
||||
### Task G4: Reiter Artikel
|
||||
`public/shop-tab-artikel.js`: Liste mit Hoch/Runter, Aktiv-Schalter, Editor (alle Felder aus C3, Varianten-Editor, Bild-Upload mit Vorschau/Entfernen/Reihenfolge, Lebensmittel-/Sicherheits-Abschnitte einklappbar, Grundpreis), Speichern mit Feldfehlern. Test.
|
||||
|
||||
### Task G5: Reiter Bestellungen
|
||||
`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
|
||||
`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.
|
||||
|
||||
---
|
||||
|
||||
## Block H: Widget und öffentliche Seiten
|
||||
|
||||
### Task H1: `public/embed/shop.js`
|
||||
ES5-IIFE nach `embed/termine.js` (Light DOM, `@layer wks`, Präfix `wks-`, `data-webklar-shop`, `data-api`, `--wks-akzent`, `credentials:'omit'`, 20 s Timeout, Ausweichtext bleibt bei Fehlern). Ansichten: Raster (Kategorien-Filter), Detail (Galerie, Beschreibung, Preis + Grundpreis + Steuerhinweis, Varianten, Menge, Lieferhinweis, Widerrufsausschluss, Produktsicherheit, Lebensmittelangaben), Warenkorb (Zustand im Speicher **und** `sessionStorage` mit try/catch, nur IDs/Mengen), Kasse (Lieferart/Land, Zusammenfassung **vom Server** `/pruefen`, Pflichtinformationen, Links AGB/Widerrufsbelehrung/Versand, Hinweis „Name, Adresse und Zahlung geben Sie im nächsten Schritt bei Stripe ein“, Knopf **„Zahlungspflichtig bestellen“**), Rückkehr (`wk_bestellung`/`wk_t` ⇒ Status anzeigen, `wk_abbruch` ⇒ Warenkorb; danach `history.replaceState` bereinigen), Widerruf (Schaltfläche „Vertrag widerrufen“ ⇒ Formular ⇒ Knopf **„Widerruf bestätigen“** ⇒ Eingangsanzeige mit Zeit). Kopfzeile: Lieferländer + Zahlungsarten (§ 312j Abs. 1). Fußzeile: AGB · Widerrufsbelehrung · Versand & Zahlung · Vertrag widerrufen · Impressum · Datenschutz. Geschlossen ⇒ „Der Shop ist gerade geschlossen.“ + Widerruf weiter erreichbar. Rechtstexte als Overlay/`<details>` mit `textContent` (Absätze per `\n\n`). Keine Zahlen-/Datumsformatierung im Widget (alles vom Server). Statische Tests `test/shopEmbedStatic.test.js` (kein `innerHTML` mit Fremddaten, kein `eval`, Knopftexte vorhanden).
|
||||
|
||||
### Task H2: Seiten und Demo
|
||||
`public/shop-seite.html` (neutrale Seite mit Widget; Server-Route `GET /shop/:siteKey` und `GET /shop/:siteKey/widerruf` (siteKey-Regex, sonst 404) liefert die Datei; `?ansicht=widerruf` öffnet direkt den Widerruf), `public/embed/shop-demo.html`, Test der Route.
|
||||
|
||||
---
|
||||
|
||||
## Block I: WOMS (eigener Worktree)
|
||||
|
||||
### Task I1: Funktionen-Karte + Anfrage
|
||||
WOMS `src/lib/funktionenView.js` (`FUNKTIONEN` + `shop` mit Bestätigungstexten; `aktionen` für shop wie termine: setup ⇒ abnahme/ticket/disable), `src/lib/teamApi.js` (`getShop`, `entscheideShopAnfrage`), `src/components/customer/CustomerFunktionen.jsx` (Webshop-Panel je Projekt: Anfrage lesen, „Anfrage annehmen“/„Ablehnen“ mit Begründung, Stripe-Status, Kennzahlen), `scripts/setup-legal.mjs` (`DOCUMENTS` + `leistungsbeschreibung-webshop`, `avv-webshop`; Test-Anzahl anpassen), Tests `npm test` + `nice npx vite build`.
|
||||
|
||||
---
|
||||
|
||||
## Block J: Rechtstexte, Jobs, Aufräumen
|
||||
|
||||
### Task J1: Leistungsbeschreibung + AVV Webshop
|
||||
`docs/rechtliches/leistungsbeschreibung-webshop.md`, `docs/rechtliches/avv-webshop.md` (Inhalt Spec 10; Muster LB/AVV Terminplaner; Fassung `config.shop.fassung`), `scripts/build-legal-html.mjs` und `scripts/build-legal-pdfs.sh` um Satz/Modus `shop` erweitern, HTML + PDF + `.sha256` erzeugen (Chrome headless wie vorhanden, `timeout 120`), Tests `legalDocs`, `buildLegalHtml` erweitern; danach `featureLegalReady('shop') === true`.
|
||||
|
||||
### Task J2: Datenschutz, Widerruf der Funktion, Doku
|
||||
`public/datenschutz.html` Abschnitt „Webshop“ (Stand-Datum, PDF neu über `build-legal-pdfs.sh datenschutz`), `server/routes/abo.js` (`ORDERS`, `FEATURE_OF_ORDER`, `orderLabel` um `shop`) + `public/widerrufen.html` (Option „Webshop (Einrichtung)“), `docs/funktionen/shop-einbau.md`, `docs/funktionen/shop-datenschutz-absatz.md` (Muster für die Händler-Website), `docs/rechtliches/webshop-kanzlei-paket.md` (Fragen + Fundstellen), Tests.
|
||||
|
||||
### Task J3: Jobs, Aufbewahrung, Kundenlöschung
|
||||
`server/services/shop/jobs.js` (`minutenJob` aus E2 + Bilder-Aufräumen stündlich; `taeglicherJob`: Käuferdaten anonymisieren 3 Jahre nach Ende des Bestelljahres, unbezahlte/stornierte nach 30 Tagen; Shops `disabled` seit > 90 Tagen: Produkte, Bestellungen, Widerrufe, Bilder, shopConfig löschen, Dokument bleibt `disabled`; seitenweise, Deckel je Lauf), `server/index.js` (Jobs registrieren, nur wenn `shopModus() !== 'off'` oder es Shop-Dokumente gibt), `server/services/customerAdmin.js` (Kundenlöschung: `disableCustomerFeatures(customerId,'shop',…)`, offenen Shop schließen), `retention.js` nur falls nötig. Tests.
|
||||
|
||||
---
|
||||
|
||||
## Block K: Abschluss (Controller)
|
||||
- [ ] 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.
|
||||
- [ ] 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.
|
||||
@@ -23,7 +23,7 @@ Die Funktion ersetzt die Kachel „Terminplaner · Kommt bald“ (`public/funkti
|
||||
| # | Entscheidung |
|
||||
|---|---|
|
||||
| E1 | Abrechnung: **einmalige Einrichtung + nur solange ein Wartungs-Abo läuft** (wie Website-Analyse). Endet das Abo, endet der Terminplaner. Hosting-Abo reicht nicht. |
|
||||
| E2 | Einrichtungspreis **149 €** (brutto, einmalig, Stripe Checkout wie News). |
|
||||
| E2 | Einrichtungspreis **149 €** (ursprünglich „brutto“, seit TP2 ohne USt nach § 19 UStG; einmalig, Stripe Checkout wie News). |
|
||||
| E3 | Umfang V1: **Personen und Plätze** (Einzelperson; Team mit Wahl der Person oder „egal“; buchbare Plätze wie Kabine, Hebebühne). Pro Leistung: **sofort verbindlich** oder **Anfrage** (Betrieb bestätigt). Gruppen/Kurse/Tische später. |
|
||||
| E4 | Der Gast **bestätigt seine E-Mail-Adresse per Link**, bevor der Termin gilt; der Slot ist so lange 30 Minuten reserviert. |
|
||||
| E5 | **Ein gemeinsames Widget**, je Kunde gestaltet (Farben, Schrift, Rundungen, Texte, Position) + offene, versionierte Schnittstelle für Sonderfälle. |
|
||||
|
||||
@@ -0,0 +1,245 @@
|
||||
# Analyse-Übersicht neu („Cockpit“) – Design
|
||||
|
||||
Stand: 2026-10-04 · Status: Aufbau, Design-Richtung und Abschnitte 1–3 vom Nutzer im Chat freigegeben, Spec zur Durchsicht
|
||||
Baut auf der Website-Analyse auf (Spec `2026-10-01-website-analyse-design.md`, live seit 01.10.2026).
|
||||
Recherche: `/root/reports/analyse-ueberarbeitung-2026-10-04/recherche.md` (30 Tools, 25 Features, UI-Muster).
|
||||
Entwürfe: `/root/reports/analyse-ueberarbeitung-2026-10-04/entwurf-a-cockpit.png` (gewählt), `entwurf-b-bericht.png`.
|
||||
Regel des Nutzers: bauen → live (Nutzer pusht `main`) → Nutzer testet live.
|
||||
|
||||
## Ziel
|
||||
|
||||
Die Analyse-Seite im Kundenportal (Funktionen → Analyse) soll für kleine Betriebe ohne Analytics-Kenntnisse
|
||||
**auf einen Blick** sagen, wie die Website läuft: ein Satz in Klartext, Vergleich zum Vorzeitraum,
|
||||
Kontakt-Aktionen als Hauptzahl. Das Interface darf sich vom restlichen Portal abheben, muss aber
|
||||
aufgeräumt und einheitlich sein.
|
||||
|
||||
## Entscheidungen (vom Nutzer, 04.10.2026)
|
||||
|
||||
| # | Entscheidung |
|
||||
|---|---|
|
||||
| U1 | Umfang: nur das Paket **„Übersicht neu“** (Kennzahlen mit Vorzeitraum-Vergleich und Mini-Verlauf, Klartext-Zusammenfassung, Kontakt-Aktionen als Hauptzahl, Datenbasis-Hinweis). **Nicht** in diesem Projekt: Besucher-Details (Wochentag × Uhrzeit, Kanäle, Zeit je Seite), Website-Check (Erreichbarkeit, SSL, Links, SEO), Bericht per Mail. |
|
||||
| U2 | Design-Richtung **A „Cockpit“**: dunkel wie das Portal, eigene Akzentfarbe Türkis nur innerhalb der Analyse, leuchtender Block „Das Wichtigste“. |
|
||||
| U3 | Aufbau mit drei Reitern **Übersicht · Heatmap · Einstellungen**; Heatmap und „Ausschalten“ hängen nicht mehr unten an der Übersicht. |
|
||||
| U4 | Nur **vorhandene Daten**. Keine Änderung an Messskript (`w.js`, `h.js`), Banner, AVV, Leistungsbeschreibung, Datenschutz-Zusatzseite oder Abo-Bedingungen. |
|
||||
| U5 | Klartext entsteht durch **feste Regeln im Browser**, keine KI, kein externer Dienst. |
|
||||
|
||||
Rechtliche Einordnung (keine Rechtsberatung): Der Vergleich nutzt die Tageswerte des Vorzeitraums (bis zu 180
|
||||
Tage zurück; Tageswerte werden 25 Monate aufbewahrt). Durchschnitte und Tageswerte für Besucher, Kontakte und
|
||||
Verweildauer stammen aus bereits erhobenen und verdichteten Daten (`daily`: `visitors`, `ct_kind`,
|
||||
`en_count`, `en_sec_sum`, `scroll_sum`). Die Leistungsbeschreibung (§ 5: „Kennzahlen und Diagramme für die
|
||||
letzten 7, 30 oder 90 Tage“) deckt das ab; kein neuer Text, keine neue Einwilligung.
|
||||
|
||||
## 1. Daten: Statistik-Antwort des Collectors
|
||||
|
||||
`GET /a/admin/stats/:siteKey?range=7|30|90` (interner Listener, `analytics/src/stats.js`) liefert **zusätzlich**
|
||||
zu den bisherigen Feldern (alle bisherigen Felder bleiben unverändert, damit der Wechsel abwärtskompatibel ist):
|
||||
|
||||
```js
|
||||
{
|
||||
range: 30,
|
||||
from: '2026-09-04', to: '2026-10-03', // Berliner Kalendertage des Zeitraums: abgeschlossene Tage (to = gestern)
|
||||
today: { day: '2026-10-04', pv, visitors, contacts }, // heute bis jetzt, live, nicht im Zeitraum enthalten
|
||||
firstDay: '2026-08-20', // frühester Berliner Tag mit Daten dieser Website ('' = keine)
|
||||
totals: {
|
||||
pv, visitors, contacts, consentRate, // wie bisher
|
||||
engaged, // Zahl der en-Ereignisse (Seitenbesuche mit Zustimmung und Abschluss)
|
||||
avgSec, // Ø Sekunden pro Seitenbesuch = en_sec_sum / en_count, gerundet; null wenn engaged = 0
|
||||
avgScroll, // Ø Scrolltiefe in % = scroll_sum / en_count, gerundet; null wenn engaged = 0
|
||||
},
|
||||
series: [{ day, pv, visitors, contacts, avgSec }], // bisher nur {day, pv}; avgSec je Tag null ohne en
|
||||
pages, refs, countries, devices, contacts, banner, // unverändert
|
||||
previous: {
|
||||
from: '2026-08-06', to: '2026-09-04', // die `range` Kalendertage direkt vor `from`
|
||||
complete: true, // firstDay !== '' && firstDay <= previous.from
|
||||
totals: { pv, visitors, contacts, consentRate, engaged, avgSec, avgScroll },
|
||||
series: [{ day, pv, visitors, contacts, avgSec }],
|
||||
contacts: [{ key, count }], // je Art, für die Veränderung pro Zeile
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Regeln:
|
||||
- Tageslisten werden **kalendarisch** gebildet (Berliner Tage, wie `dayList`), nicht durch Abziehen von
|
||||
`range × 24 h` – sonst verrutscht der Vorzeitraum bei der Zeitumstellung um einen Tag.
|
||||
- Gemeinsame Hilfsfunktion sammelt die Dimensionen für eine beliebige Tagesliste: verdichtete Tage aus `daily`,
|
||||
nicht verdichtete Tage live aus `events_raw` (wie heute). Kein Tag wird doppelt gezählt.
|
||||
- `firstDay` = kleinster Tag aus `daily` und `events_raw` dieser Website (`MIN(day)` bzw. `dayOf(MIN(ts))`).
|
||||
- `visitors` bleibt die Summe der Tages-Besucher (Salz wechselt täglich, kein tagesübergreifender Abgleich).
|
||||
- Der Portal-Server reicht die Antwort unverändert durch (`GET /api/funktionen/analyse/stats`).
|
||||
|
||||
Portal-Server, einzige weitere Änderung: `analyseView` (Route `GET /api/funktionen`) gibt zusätzlich
|
||||
`activatedAt` aus dem Analyse-Dokument zurück (`null`, wenn nicht aktiv).
|
||||
|
||||
Nachtrag 04.10.2026 (Entscheidung im Final-Review, F1): Der Zeitraum besteht aus **abgeschlossenen** Tagen
|
||||
(endet gestern), damit der angebrochene heutige Tag nicht gegen volle Vortage verglichen wird (sonst zeigte z. B.
|
||||
der 7-Tage-Blick tagsüber fälschlich „weniger“). Der heutige Stand steht getrennt in `today` und in der Kopfleiste
|
||||
(„Heute bisher: 42 Aufrufe“). Der Satz „direkt kontaktiert“ zählt nur Telefon, E-Mail und WhatsApp (F2).
|
||||
|
||||
## 2. Oberfläche
|
||||
|
||||
### 2.1 Aufbau der Seite (Zustand „aktiv“)
|
||||
|
||||
```
|
||||
[ Kopfleiste: ● Website-Name · „Analyse aktiv · Heute bisher: 42 Aufrufe · Stand: 19:42 Uhr“ (Website-Auswahl) [7 | 30 | 90 Tage] ]
|
||||
[ Reiter: Übersicht | Heatmap | Einstellungen ]
|
||||
Übersicht:
|
||||
[ Das Wichtigste · 5. Sep. – 4. Okt. — Klartext-Satz, darunter bis zu 4 Hinweise mit Symbol ]
|
||||
[ Seitenaufrufe ] [ Besucher* ] [ Kontakt-Aktionen* ] [ Ø Zeit pro Seite* ] je: Zahl, ▲/▼-Chip, „vs. <Vorwert>“, Mini-Verlauf
|
||||
[ Seitenaufrufe pro Tag (Linie + gestrichelter Vorzeitraum) ] [ Kontakt-Aktionen: Anrufe, E-Mails, WhatsApp, Links nach außen + Quote ]
|
||||
[ Was angesehen wird: Seiten | Herkunft ] [ Wer zu Besuch ist: Geräte | Länder ]
|
||||
[ Datenbasis: Ring mit Zustimmungsquote + Erklärung ] [ Heatmap: Vorschaubild + „Öffnen“ → Reiter Heatmap ]
|
||||
Fußzeile: „Länderdaten: IP-Geolocation by DB-IP“
|
||||
```
|
||||
|
||||
- **Kopfleiste**: Statuspunkt (türkis = aktiv). Bei `devMode` statt „Analyse aktiv“ gelb: „Im Dev-Modus wird
|
||||
nicht gemessen.“ „Stand“ = Ladezeitpunkt der Daten. Bei mehreren Websites ein Auswahlfeld in der Leiste
|
||||
(ersetzt das bisherige `fn-project`-Panel). Der Zeitraum-Schalter erscheint nur im Reiter Übersicht
|
||||
(die Heatmap zeigt fest 30 Tage).
|
||||
- **Reiter**: `role="tablist"`, Zustand bleibt beim Wechsel von Zeitraum oder Website erhalten
|
||||
(`analyseSel.tab`), der URL-Hash bleibt `#analyse`. Auf dem Handy horizontal wischbar.
|
||||
- **Das Wichtigste**: siehe 2.3. Unter 30 Seitenaufrufen im Zeitraum: „Sobald ein paar mehr Besuche da sind,
|
||||
steht hier das Wichtigste über deine Website in zwei, drei Sätzen.“
|
||||
- **Kennzahlen-Kacheln** (4): Seitenaufrufe, Besucher*, Kontakt-Aktionen*, Ø Zeit pro Seite* (Format `m:ss`,
|
||||
darunter klein „Ø 64 % gescrollt“). Chip mit Veränderung (2.4). Mini-Verlauf aus `series` (Fläche + Linie,
|
||||
ohne Achsen). Ohne Vergleich steht statt Chip „Vergleich ab TT.MM.“ (2.4).
|
||||
- **Diagramm**: Seitenaufrufe pro Tag, aktuelle Linie türkis mit Fläche, Vorzeitraum gestrichelt grau (nach
|
||||
Tagesindex ausgerichtet; nur wenn `previous.complete`). Legende „30 Tage“ / „Zeitraum davor“. Antippen oder
|
||||
Überfahren eines Tages zeigt eine Blase „Sa., 19.09.: 371 Aufrufe (davor 210)“. SVG behält `<title>`.
|
||||
- **Kontakt-Aktionen**: Summe im Kopf, Zeilen Anrufe (`tel`), E-Mails (`mail`), WhatsApp (`wa`), Links nach außen
|
||||
(`out`) mit Symbol, Zahl und Veränderungs-Chip; Arten mit 0 in beiden Zeiträumen entfallen. Darunter die
|
||||
Quote: „Auf 100 Besucher kommen 7,8 Kontakt-Aktionen (davor 6,5).“ (erst ab 30 Besuchern; Kontakt-Aktionen sind
|
||||
Klicks, keine Personen – daher „pro 100 Besucher“, nicht „x % der Besucher“). Ohne Kontakte: „Noch keine
|
||||
Kontakt-Aktionen. Sie zählen, wenn jemand auf deiner Website auf Telefonnummer, E-Mail-Adresse oder
|
||||
WhatsApp tippt.“
|
||||
- **Ranglisten** in zwei Karten mit kleinem Umschalter: „Was angesehen wird“ (Seiten | Herkunft), „Wer zu Besuch
|
||||
ist“ (Geräte | Länder). Zeile = Balken als Hintergrund des Namens, Zahl, Anteil in %. Erst 6 Zeilen, „Alle
|
||||
anzeigen“ klappt bis 20 auf. `/` heißt „Startseite“. Herkunft mit lesbaren Namen für bekannte Hosts
|
||||
(google.* → Google, bing.com → Bing, instagram.com → Instagram, facebook.com/m.facebook.com/l.facebook.com →
|
||||
Facebook, duckduckgo.com → DuckDuckGo, ecosia.org → Ecosia), sonst der Host; `''` → „Direkt oder unbekannt“.
|
||||
- **Datenbasis** (ersetzt Banner-Liste und Sternchen-Fußnote): Ring mit `consentRate`, Text „61 % haben dem
|
||||
Banner zugestimmt (2.410 von 3.950 Entscheidungen). Seitenaufrufe zählen wir vollständig; Besucher,
|
||||
Kontakt-Aktionen und Zeit pro Seite (*) nur bei Zustimmung.“ Ohne Entscheidungen: „Noch hat niemand über das
|
||||
Banner entschieden.“
|
||||
- **Heatmap-Karte**: kleines statisches Vorschaubild (CSS-Verlauf, keine echte Website), Satz, Knopf „Öffnen“
|
||||
wechselt in den Reiter Heatmap.
|
||||
|
||||
### 2.2 Reiter Heatmap und Einstellungen, andere Zustände
|
||||
|
||||
- **Heatmap**: die bestehende Heatmap in Geräterahmen (`renderAnalyseHeat`, `geraete.js/.css`, iframes,
|
||||
postMessage-Zähler, 20-s-Rückfall „In neuem Tab öffnen“) unverändert im Verhalten, optisch im Cockpit-Stil
|
||||
(Seitenauswahl und Computer/Tablet-Schalter in einer Leiste). Die iframes laden erst, wenn der Reiter
|
||||
geöffnet wird.
|
||||
- **Einstellungen**: Karte „Status“ (aktiv seit `activatedAt`, Banner-Farbe als Farbfeld, Fassung
|
||||
`avvVersion`), Karte „Unterlagen“ (Links AVV und Leistungsbeschreibung aus `legal`), Karte „Analyse
|
||||
ausschalten“ (`renderAnalyseOff`, Texte und Ablauf unverändert).
|
||||
- **Einschalten** (`renderAnalyseActivation`), **gesperrt**, **nicht verfügbar**, **unbekannt**, **noch nicht
|
||||
freigeschaltet**: gleicher Cockpit-Rahmen (Kopfleiste ohne Zeitraum und Reiter). Alle Texte, Links, Häkchen,
|
||||
Farbwahl und Banner-Vorschau bleiben **wörtlich** wie heute (Rechtsrunde 01./02.10.).
|
||||
- **Laden**: Platzhalter-Kacheln in der Form der echten Kacheln statt „Lade…“. **Fehler 503**: Karte
|
||||
„Auswertung gerade nicht erreichbar“ mit Knopf „Neu laden“.
|
||||
|
||||
### 2.3 Klartext-Regeln („Das Wichtigste“)
|
||||
|
||||
Reine Funktion `summarize(stats)` → `{ lead, items: [{ icon, text }] }` in `public/analyse-view.js`
|
||||
(ohne DOM, Node-testbar). Zahlen deutsch formatiert. Nur bei `totals.pv >= 30`, sonst Leerzustand (2.1).
|
||||
|
||||
**Satz** (`lead`):
|
||||
- Vergleich möglich (2.4): „Deine Website wurde in den letzten N Tagen **X-mal** aufgerufen – **Y % mehr** /
|
||||
**Y % weniger** als in den N Tagen davor.“ Bei |Veränderung| < 5 %: „… – etwa so oft wie in den N Tagen
|
||||
davor.“ Bei kleinen Zahlen (2.4) absolut: „… – **12 Aufrufe mehr** als in den N Tagen davor.“
|
||||
- Kein Vergleich: „Deine Website wurde in den letzten N Tagen **X-mal** aufgerufen.“ Liegt `firstDay` im
|
||||
Zeitraum: „Seit dem Start der Analyse am TT.MM. wurde deine Website **X-mal** aufgerufen.“
|
||||
|
||||
**Hinweise** (`items`, in dieser Reihenfolge, nur wenn die Bedingung erfüllt ist, höchstens 4):
|
||||
1. Kontakte (`contacts >= 1`): „N-mal hat dich jemand direkt kontaktiert, meist per Telefon (96).“ + bei
|
||||
Vergleich und Vorwert ≥ 1: „Das sind 45 mehr als zuvor.“ / „… weniger …“ / „Genauso oft wie zuvor.“
|
||||
Bei 0 Kontakten und `visitors >= 30`: „Von den Besuchern mit Zustimmung hat noch niemand über die Website
|
||||
angerufen oder geschrieben.“ (Kontakt-Aktionen zählen nur bei Zustimmung.)
|
||||
2. Wochentag (mindestens 7 Tage mit Daten im Zeitraum und `pv >= 70`): Ø Aufrufe je Wochentag über die Tage
|
||||
ab `firstDay`; stärkster Wochentag, wenn sein Schnitt ≥ 1,2 × Gesamtschnitt: „Samstag ist im Schnitt dein
|
||||
stärkster Tag.“ Zusätzlich Spitzentag, wenn ≥ 1,8 × Tagesschnitt: „Spitzentag war Sa., 19.09. mit 371
|
||||
Aufrufen.“ (beides in einem Hinweis, wenn beides zutrifft).
|
||||
3. Herkunft (erster Eintrag in `refs` mit Host, Anteil ≥ 20 % der Herkunfts-Summe): „Die meisten, die über
|
||||
einen Link kamen, kommen von Google (880).“
|
||||
4. Gerät (`m` oder `d` ≥ 55 % der Geräte-Summe): „66 % der Aufrufe kommen vom Handy.“ / „… vom Computer.“
|
||||
5. Seite (`pages[0]`): „Am meisten angesehen: die Startseite, danach /preise (1.310).“ bzw. „Am meisten
|
||||
angesehen: /preise (1.310 Aufrufe).“
|
||||
|
||||
### 2.4 Vergleich und Veränderungs-Chips
|
||||
|
||||
- Vergleich möglich, wenn `previous.complete` und `previous.totals.pv >= 20`. Sonst kein Chip; Kacheln zeigen
|
||||
„Vergleich ab TT.MM.“ mit TT.MM. = `firstDay` + 2 × range Tage; ohne `firstDay` gar nichts. Fehlt
|
||||
`previous` ganz (alter Collector), wie „kein Vergleich“ ohne Datum.
|
||||
- Funktion `delta(cur, prev, { kind })` → `{ dir: 'up'|'down'|'flat', text, aria }`:
|
||||
- Zählwerte: Prozent (ganzzahlig, „▲ 14 %“), wenn `prev >= 100` und `cur >= 100`; sonst absolut („▲ 12“,
|
||||
„▼ 3“). Gleich → „± 0“ (`flat`). |Prozent| < 1 → `flat` „± 0 %“.
|
||||
- Sekunden: absolut, „▲ 6 s“ / „▼ 1:05“ (ab 60 s als m:ss).
|
||||
- `aria`: „plus 14 Prozent gegenüber dem Zeitraum davor“ usw. Farbe nie alleiniger Träger (Pfeile ▲▼).
|
||||
- Farben: mehr = türkis, weniger = rosé, gleich = grau. Bewusst **keine** Wertung „gut/schlecht“ (weniger
|
||||
Zeit pro Seite kann gut sein).
|
||||
|
||||
### 2.5 Optik („Cockpit“)
|
||||
|
||||
- Alles unter einem Wurzelelement `.an` mit eigenen CSS-Variablen; außerhalb ändert sich nichts.
|
||||
Akzent `--an-acc: #2dd4bf`, stark `#5eead4`, weich `rgba(45,212,191,.1)`; weniger `#fda4af`;
|
||||
Karten `rgba(14,16,20,.92)`, Rand `rgba(255,255,255,.09)`, Radius 16 px, Abstände 14 px.
|
||||
- Kartentitel einheitlich: 13 px, fett, Großbuchstaben mit Sperrung, gedämpft; Zahlen `tabular-nums`.
|
||||
- „Das Wichtigste“: Verlauf türkis → violett, türkiser Rand, Satz 21 px.
|
||||
- Desktop: Raster wie Entwurf A (4 Kacheln; Diagramm 1,65 : 1 Kontakte; 2 × 2 Karten). Diagramm und
|
||||
Kontaktkarte gleich hoch, das Diagramm füllt die Höhe.
|
||||
- Handy (≤ 880 px, App-Ansicht): Kopfleiste zweizeilig, Zeitraum-Schalter volle Breite, Kacheln 2 × 2, Hinweise
|
||||
einspaltig, alle Karten untereinander. Keine waagrechte Seiten-Scrollleiste.
|
||||
- `prefers-reduced-motion`: keine Übergänge.
|
||||
|
||||
## 3. Code-Aufteilung
|
||||
|
||||
`public/funktionen.js` (980 Zeilen) wird entlastet:
|
||||
- **`public/analyse.js`** (neu): gesamte Analyse-Oberfläche – `initAnalyse`, `renderAnalyseActivation`,
|
||||
`renderAnalyseOff`, Übersicht, Heatmap, Einstellungen. `app.js` lädt `/analyse.js` für die Ansicht `analyse`.
|
||||
- **`public/fn-util.js`** (neu): `esc`, `safeUrl`, `request` – bisher lokal in `funktionen.js`, jetzt von
|
||||
beiden Modulen importiert.
|
||||
- **`public/analyse-view.js`**: bekommt die reinen Funktionen `summarize`, `delta`, `fmtDuration`,
|
||||
`contactRate`, `compareFrom`, `refName`, `weekdayStats` sowie `analyseOf`, `analyseState`,
|
||||
`pickAnalyseProject` (bisher in `funktionen.js`, von der Kachel und der Analyse gebraucht).
|
||||
- **`public/analyse.css`** (neu, in `dashboard.html` eingebunden): alle `.an`-Regeln; die alten
|
||||
`.an-*`-Regeln in `funktionen.css` entfallen, soweit sie nur die Analyse betreffen.
|
||||
- Kachel „Analyse“ auf der Funktionen-Seite bleibt in `funktionen.js` (unverändert im Verhalten).
|
||||
- Bestehende Element-IDs, die Tests und Abläufe nutzen, bleiben: `#analyse-body`, `#an-stage`, `#an-preview`,
|
||||
`#an-avv`, `#an-on`, `#an-error`, `#an-off-*`, `#an-heat-page`.
|
||||
|
||||
## 4. Tests
|
||||
|
||||
- **Collector** (`analytics/test/stats.test.js`): `previous` mit richtigen Tagen und Summen; `complete`
|
||||
false/true je nach `firstDay`; `firstDay` aus `daily` und Rohdaten; `series` mit visitors/contacts/avgSec;
|
||||
`avgSec`/`avgScroll` null ohne en; Vorzeitraum über eine Zeitumstellung (Ende Oktober) lückenlos und ohne
|
||||
Überlappung; nichts doppelt (verdichtet + live). Vertragstest `test/analyticsContract.test.js` um die neuen
|
||||
Felder ergänzen.
|
||||
- **Portal, rein** (`test/analyseInsights.test.js`, neu): `summarize` für Wachstum, Rückgang, gleich, kleine
|
||||
Zahlen, ohne Vergleich, neu gestartet, zu wenig Daten, ohne Kontakte, Wochentag/Spitzentag, Herkunft, Gerät,
|
||||
höchstens 4 Hinweise; `delta` (Prozent/absolut/flat/Sekunden/aria); `compareFrom`; `contactRate`;
|
||||
`refName`; HTML-Maskierung aller Werte, die aus Daten kommen (Seitenpfade, Hosts).
|
||||
- **Portal, bestehend**: `funktionenTiles.test.js` und `analyseView.test.js` auf `analyse.js`/`fn-util.js`
|
||||
umstellen; alle bisherigen Text-Prüfungen (Einschalten, Ausschalten) laufen unverändert weiter.
|
||||
`analyseRoutes.test.js`: `activatedAt` in der Ansicht.
|
||||
- **Browser** (`test/ui/analyse.ui.mjs`, erweitert; Playwright mit gemockter API): Desktop 1440 und Handy 402 ×
|
||||
Zustände aktiv (volle Daten), aktiv neu (ohne Vergleich), wenig Daten, Dev-Modus, Einschalten, fehlende
|
||||
Angaben, 503; Reiterwechsel; Heatmap-iframes erst nach Reiterwechsel; keine `pageerror`; keine waagrechte
|
||||
Scrollleiste. Screenshots nach `/root/reports/analyse-ueberarbeitung-2026-10-04/screens/`.
|
||||
- Gesamte Suiten: `npm test` (Portal) und `analytics: npm test` grün; Node 20 (Portal) und Node 24 (Collector).
|
||||
|
||||
## 5. Livegang
|
||||
|
||||
1. Nutzer pusht `feature/analyse-uebersicht` nach `main` (mein Push nach `main` ist im Auto-Modus gesperrt).
|
||||
2. `deploy.sh` startet Portal und – weil `analytics/` geändert – den Collector neu. Keine Schema-Änderung,
|
||||
keine `.env`-Änderung.
|
||||
3. Prüfung durch mich: Collector `/a/health`, Stats-Abruf einer echten aktiven Website im Container
|
||||
(neue Felder vorhanden, alte unverändert), Portal-Bundle enthält `analyse.js`, Ansicht lädt ohne Fehler.
|
||||
4. Nutzer testet live mit einer Website mit aktiver Analyse.
|
||||
|
||||
Rückweg: Portal und Collector sind gegenseitig abwärtskompatibel (alte Oberfläche ignoriert neue Felder, neue
|
||||
Oberfläche kommt ohne `previous` aus); Zurückrollen = Revert-Commit auf `main`.
|
||||
|
||||
## Nicht in diesem Projekt
|
||||
|
||||
Besucher-Details, Website-Check, Bericht per Mail (U1); Änderungen an Messskript, Banner und Rechtstexten (U4);
|
||||
WOMS; Anpassen der Banner-Farbe nach dem Einschalten; Export.
|
||||
@@ -0,0 +1,349 @@
|
||||
# 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` | 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;
|
||||
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-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 |
|
||||
| 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) |
|
||||
Reference in new issue
Block a user