50,00 € pro Monat · Mindestlaufzeit 12 Monate, auch nach einem Wechsel · für alle Websites und Web-Anwendungen, die wir erstellt oder zur Betreuung übernommen haben
+ 50,00 € pro Monat · Endpreis, gemäß § 19 UStG ohne Umsatzsteuer · Mindestlaufzeit 12 Monate, auch nach einem Wechsel · für alle Websites und Web-Anwendungen, die wir erstellt oder zur Betreuung übernommen haben
- Betrieb der Website bzw. Web-Anwendung auf unserem Server in Deutschland mit SSL-Zertifikat, Überwachung der Erreichbarkeit und Updates von Betriebssystem und Server-Software (§ 3 Abs. 2)
@@ -366,12 +366,12 @@
Hinweise zum Widerruf bei Wechsel und Zusatzspeicher
- Diese Hinweise sind nicht Teil der Widerrufsbelehrung. Sie erklären, wie sich das Widerrufsrecht auf die einzelnen Bestellungen auswirkt.
+ Diese Hinweise gehören zu diesen Bedingungen, nicht zur gesetzlichen Widerrufsbelehrung. Sie erklären, wie sich das Widerrufsrecht auf die einzelnen Bestellungen auswirkt.
- - Das Widerrufsrecht gilt für jede Bestellung gesondert: für den Abschluss eines Abos, für den Wechsel ins Wartungs-Abo und für jede Bestellung von Zusatzspeicher. Die Frist von vierzehn Tagen beginnt jeweils mit der Bestellung. Unter project.webklar.com/widerrufen.html wählt der Kunde aus, welche Bestellung er widerruft. Solange eine Widerrufsfrist läuft, zeigt das Kundenportal unter „Verträge“ den letzten Tag der Frist an.
- - Widerruf des Abos: Widerruft der Kunde den Abschluss seines Abos, endet damit auch der dazu bestellte Zusatzspeicher.
- - Widerruf des Wechsels: Widerruft der Kunde den Wechsel ins Wartungs-Abo, läuft sein Hosting-Abo zu den bisherigen Bedingungen weiter, als hätte es den Wechsel nicht gegeben, einschließlich seiner ursprünglichen Mindestlaufzeit. Wir erstatten den für das Wartungs-Abo gezahlten Mehrbetrag. Weil der Kunde beim Wechsel den sofortigen Beginn verlangt hat, ziehen wir davon den Wertersatz für die bis zum Widerruf erbrachte Leistung ab (§ 357a Abs. 2 BGB): den anteiligen Mehrpreis des Wartungs-Abos gegenüber dem Hosting-Abo für die Tage vom Wechsel bis zum Widerruf.
- - Widerruf des Zusatzspeichers: Wir stellen bestellten Zusatzspeicher sofort bereit, ohne dass der Kunde das ausdrücklich verlangen muss. Widerruft er die Bestellung, nehmen wir die Pakete zurück und erstatten alles, was er dafür gezahlt hat. Wertersatz verlangen wir nicht. Passen die gespeicherten E-Mails danach nicht mehr in den verbleibenden Speicher, gilt § 10 Abs. 5 Satz 4.
+ - Das Widerrufsrecht gilt für jede Bestellung gesondert: für den Abschluss eines Abos, für den Wechsel ins Wartungs-Abo und für jede Bestellung von Zusatzspeicher. Die Frist von vierzehn Tagen beginnt jeweils mit dem Vertragsschluss (§ 2 Abs. 3 bzw. Abs. 5). Unter project.webklar.com/widerrufen.html wählt der Kunde aus, welche Bestellung er widerruft. Solange eine Widerrufsfrist läuft, zeigt das Kundenportal unter „Verträge“ den letzten Tag der Frist an.
+ - Widerruf des Abos: Widerruft der Kunde den Abschluss seines Abos, endet damit auch der dazu bestellte Zusatzspeicher. Was der Kunde für den Zusatzspeicher gezahlt hat, erstatten wir vollständig; Wertersatz verlangen wir dafür nicht (Nr. 4).
+ - Widerruf des Wechsels: Widerruft der Kunde den Wechsel ins Wartungs-Abo, läuft sein Hosting-Abo zu den bisherigen Bedingungen weiter, als hätte es den Wechsel nicht gegeben, einschließlich seiner ursprünglichen Mindestlaufzeit. Wir erstatten den für das Wartungs-Abo gezahlten Mehrbetrag. Mehrbetrag ist, was der Kunde seit dem Wechsel mehr gezahlt hat, als er ohne den Wechsel für das Hosting-Abo gezahlt hätte. Weil der Kunde beim Wechsel den sofortigen Beginn verlangt hat, ziehen wir davon den Wertersatz für die bis zum Widerruf erbrachte Leistung ab (§ 357a Abs. 2 BGB): den anteiligen Mehrpreis des Wartungs-Abos gegenüber dem Hosting-Abo für die Tage vom Wechsel bis zum Widerruf.
+ - Widerruf des Zusatzspeichers: Wir stellen bestellten Zusatzspeicher sofort bereit, ohne dass der Kunde das ausdrücklich verlangen muss. Widerruft er die Bestellung, nehmen wir die Pakete zurück und erstatten alles, was er dafür gezahlt hat. Wertersatz verlangen wir nicht. Passen die gespeicherten E-Mails danach nicht mehr in den verbleibenden Speicher, nimmt das betroffene Postfach keine neuen E-Mails mehr an, bis wieder Platz ist (§ 3 Abs. 4).
Anlage 1
@@ -379,12 +379,12 @@
zwischen dem Kunden als Verantwortlichem („Kunde“) und der WEBklar GbR als Auftragsverarbeiter („WEBklar“). Der Vertrag wird mit der Bestellung des Hosting-Abos oder des Wartungs-Abos vereinbart; beim Wechsel ins Wartungs-Abo bestätigt der Kunde ihn erneut.
1. Gegenstand und Dauer
- WEBklar verarbeitet personenbezogene Daten im Auftrag des Kunden, soweit das für die Leistungen des Hosting-Abos oder des Wartungs-Abos (Bereitstellung bzw. Betrieb und Pflege der Website, Domain, E-Mail-Postfächer einschließlich Zusatzspeicher, Unterstützung) nötig ist. Der Vertrag gilt für die Dauer des Abos, auch über einen Wechsel zwischen den Abos hinweg, und darüber hinaus, bis alle Daten nach Nummer 8 gelöscht oder zurückgegeben sind.
+ WEBklar verarbeitet personenbezogene Daten im Auftrag des Kunden, soweit das für die Leistungen des Hosting-Abos oder des Wartungs-Abos (Bereitstellung bzw. Betrieb und Pflege der Website, Domain, E-Mail-Postfächer einschließlich Zusatzspeicher, Unterstützung) sowie für gesondert vereinbarte Zusatzleistungen zum Abo, etwa eine Formularlösung, nötig ist. Der Vertrag gilt für die Dauer des Abos, auch über einen Wechsel zwischen den Abos hinweg, und darüber hinaus, bis alle Daten nach Nummer 8 gelöscht oder zurückgegeben sind.
2. Art und Zweck der Verarbeitung, Datenarten, Betroffene
| Art der Verarbeitung | Speichern, Bereitstellen, Übermitteln (Empfang und Versand von E-Mails, Weiterleiten von Kontaktformular-Eingaben per E-Mail an den Kunden), Sichern, Wiederherstellen und Löschen; Einsicht, soweit sie für Wartung und Unterstützung nötig ist |
- | Zweck | Betrieb der Website bzw. Web-Anwendung und der E-Mail-Postfächer des Kunden |
+ | Zweck | Betrieb der Website bzw. Web-Anwendung und der E-Mail-Postfächer des Kunden sowie gesondert vereinbarter Zusatzleistungen |
| Datenarten | Inhalte der Website; Eingaben in Formulare (z. B. Name, Kontaktdaten, Nachricht), soweit die Website solche vorsieht; Inhalte der Postfächer (E-Mails mit Anhängen, Absender, Empfänger, Zeitpunkte); Verbindungsdaten in Server-Protokollen (IP-Adresse, Zeitpunkt, aufgerufene Seite, Browserkennung); Benutzerkonten, soweit die Anwendung solche vorsieht |
| Betroffene | Besucher der Website; Nutzer der Postfächer des Kunden, insbesondere seine Beschäftigten; Absender und Empfänger von E-Mails; Kunden, Interessenten und Beschäftigte des Kunden; weitere Personen, deren Daten der Kunde über seine Website oder seine Postfächer verarbeitet |
@@ -400,7 +400,7 @@
WEBklar trifft die technischen und organisatorischen Maßnahmen nach Art. 32 DSGVO, die in Anlage 2 beschrieben sind. WEBklar darf sie an den Stand der Technik anpassen, sofern das Schutzniveau nicht sinkt.
6. Unterauftragsverarbeiter
- Der Kunde genehmigt die in Anlage 3 genannten Unterauftragsverarbeiter. Will WEBklar einen Unterauftragsverarbeiter hinzuziehen oder ersetzen, teilt es das dem Kunden mindestens vier Wochen vorher in Textform mit. Der Kunde kann aus wichtigem datenschutzrechtlichem Grund widersprechen. Einigen sich die Parteien nicht, können beide das Abo zum Zeitpunkt des Wechsels kündigen. WEBklar verpflichtet jeden Unterauftragsverarbeiter vertraglich auf dieselben Datenschutzpflichten, die sich aus diesem Vertrag ergeben.
+ Der Kunde genehmigt die in Anlage 3 genannten Unterauftragsverarbeiter. Will WEBklar einen Unterauftragsverarbeiter hinzuziehen oder ersetzen, teilt es das dem Kunden mindestens vier Wochen vorher in Textform mit. Der Kunde kann aus wichtigem datenschutzrechtlichem Grund widersprechen. Einigen sich die Parteien nicht, können beide das Abo zu dem Zeitpunkt kündigen, zu dem der neue Unterauftragsverarbeiter eingesetzt werden soll. WEBklar verpflichtet jeden Unterauftragsverarbeiter vertraglich auf dieselben Datenschutzpflichten, die sich aus diesem Vertrag ergeben.
7. Unterstützung des Kunden
diff --git a/test/legalTexts.test.js b/test/legalTexts.test.js
index 1b71213..84410f2 100644
--- a/test/legalTexts.test.js
+++ b/test/legalTexts.test.js
@@ -12,7 +12,7 @@ const V3_SHA256 = '1c9e4b002dcd5d7896bbda24c573e56544b7123e3a88d154e5db77bd2855b
const file = (rel) => new URL(`../${rel}`, import.meta.url)
const read = (rel) => readFileSync(file(rel), 'utf8')
const sha256 = (rel) => createHash('sha256').update(readFileSync(file(rel))).digest('hex')
-const strip = (html) => html.replace(/<[^>]+>/g, ' ').replace(/ /g, ' ').replace(/\s+/g, ' ').trim()
+const strip = (html) => html.replace(/<[^>]+>/g, ' ').replace(/ /g, ' ').replace(/&/g, '&').replace(/\s+/g, ' ').trim()
// HTML-Ausschnitt von einem Anker bis zum naechsten, danach ohne Tags. Die Inhaltsuebersicht wiederholt
// die Ueberschriften, deshalb wird nicht nach dem ersten Text-Treffer, sondern nach id="..." geschnitten.
@@ -106,3 +106,97 @@ test('PDF der Fassung 4 verlinkt, erzeugt und im Build-Skript', () => {
const erzeugt = build.split('\n').filter((l) => l.startsWith('pdf '))
assert.ok(!erzeugt.some((l) => l.includes('WEBklar-Wartungs-Abo-Vertragsunterlagen.pdf')), 'Fassung 3 wird nicht neu erzeugt')
})
+
+// --- Korrekturen aus der Prüfung (Review 1) ---
+
+const v4Section = (fromId, toId) => section(read(V4), fromId, toId)
+
+test('Widerrufsfrist beginnt mit dem Vertragsschluss, nicht mit der Bestellung', () => {
+ const hinweise = v4Section('widerruf-hinweise', 'avv')
+ assert.ok(hinweise.includes('mit dem Vertragsschluss (§ 2 Abs. 3 bzw. Abs. 5)'))
+ assert.ok(!hinweise.includes('mit der Bestellung'))
+ // § 2 Abs. 3 = Vertragsschluss beim Abo, Abs. 5 = Vertragsschluss bei Wechsel und Zusatzspeicher
+ const html = read(V4)
+ const p2 = html.slice(html.indexOf('id="p2"'), html.indexOf('id="p3"'))
+ // Absaetze auf oberster Ebene stehen mit genau vier Leerzeichen am Zeilenanfang (die Liste a-e in Abs. 2 mit acht)
+ const absaetze = [...p2.matchAll(/^ - ([\s\S]*?)<\/li>$/gm)].map((m) => strip(m[1]))
+ assert.equal(absaetze.length, 6)
+ assert.ok(absaetze[2].startsWith('Der Vertrag kommt zustande, sobald der Kunde den Vorgang bei Stripe abgeschlossen hat'))
+ assert.ok(absaetze[4].includes('Mit dem Klick auf „Zahlungspflichtig bestellen“ kommt der Vertrag zustande'))
+ assert.ok(hinweise.includes('gehören zu diesen Bedingungen, nicht zur gesetzlichen Widerrufsbelehrung'))
+ assert.ok(hinweise.includes('Mehrbetrag ist, was der Kunde seit dem Wechsel mehr gezahlt hat'))
+ assert.ok(hinweise.includes('Wertersatz verlangen wir dafür nicht (Nr. 4)'))
+})
+
+test('Wechsel, Plus und Minus im Portal nur ohne Kündigung und ohne offene Zahlung', () => {
+ const wechsel = v4Section('wechsel', 'zusatzspeicher'), speicher = v4Section('zusatzspeicher', 'p11')
+ assert.ok(wechsel.includes('nicht gekündigt'))
+ assert.ok(speicher.includes('nicht gekündigt'))
+ assert.ok(speicher.includes('„Nur den E-Mail-Zusatzspeicher“'))
+ // Übersicht: kein unbedingtes "jederzeit" mehr bei Wechsel und Zusatzspeicher
+ const uebersicht = strip(read(V4)).split('Inhalt ')[0]
+ assert.ok(!/jederzeit im Kundenportal/.test(uebersicht))
+ assert.ok(!/Jederzeit zum Ende des Abrechnungsmonats reduzierbar/.test(uebersicht))
+})
+
+test('Bestellübersichten nennen Laufzeit und Kündigungsmöglichkeit (§ 312j Abs. 2 BGB)', () => {
+ assert.ok(v4Section('p2', 'p3').includes('sowie Laufzeit und Kündigungsmöglichkeit'))
+ assert.ok(v4Section('wechsel', 'zusatzspeicher').includes('die Kündigungsmöglichkeit nach Ablauf der neuen Mindestlaufzeit'))
+ assert.ok(v4Section('zusatzspeicher', 'p11').includes('zum Ende jedes Abrechnungsmonats kündbar'))
+})
+
+test('Faire Nutzung der Postfächer, Hetzner benannt, Formulare, AVV-Zusatzleistungen', () => {
+ const leistungen = v4Section('p3', 'p4')
+ assert.ok(leistungen.includes('auch unentgeltlich'))
+ assert.ok(leistungen.includes('Versandgrenzen von Hetzner'))
+ assert.ok(leistungen.includes('(im Folgenden „Hetzner“)'))
+ assert.ok(!/\bder Anbieter\b/.test(leistungen))
+ assert.ok(v4Section('p6', 'p7').includes('für ihn tätige Personen'))
+ const hosting = v4Section('hosting', 'wartung')
+ assert.ok(hosting.includes('sind im Hosting-Abo nicht enthalten; eine Lösung dafür bieten wir auf Anfrage gesondert an'))
+ assert.ok(!hosting.includes('Formulardienst'))
+ const avv = v4Section('avv', 'tom')
+ assert.ok(avv.includes('sowie für gesondert vereinbarte Zusatzleistungen zum Abo, etwa eine Formularlösung,'))
+ assert.ok(avv.includes('sowie gesondert vereinbarter Zusatzleistungen'))
+ assert.ok(avv.includes('zu dem der neue Unterauftragsverarbeiter eingesetzt werden soll'))
+ assert.ok(strip(read(V4)).includes('über „Abo verwalten & Rechnungen“'))
+})
+
+test('Jeder Preis in Absätzen und Listenpunkten mit Hinweis auf § 19 UStG', () => {
+ // Die Übersichtstabelle ist ausgenommen (
), ihre Zeile "Preise" trägt den Hinweis
+ const elemente = [...read(V4).matchAll(/<(li|p)\b[^>]*>([\s\S]*?)<\/\1>/g)].map((m) => strip(m[2]))
+ const mitPreis = elemente.filter((t) => /\d+(,\d\d)? €/.test(t))
+ assert.ok(mitPreis.length >= 7, `Preisstellen: ${mitPreis.length}`)
+ for (const t of mitPreis) assert.ok(t.includes('§ 19 UStG'), `ohne § 19 UStG: ${t.slice(0, 90)}`)
+})
+
+test('Alle internen Links und Querverweise treffen', () => {
+ const html = read(V4)
+ const ids = [...html.matchAll(/ id="([^"]+)"/g)].map((m) => m[1])
+ for (const [, ref] of html.matchAll(/href="#([^"]+)"/g)) assert.equal(ids.filter((i) => i === ref).length, 1, `#${ref}`)
+ // Absätze je Paragraph: - der ol.abs auf oberster Ebene; bei § 3 die Überschriften "(M)"
+ const absaetze = {}
+ for (const [, n, body] of html.matchAll(/
§ (\d+) [^<]*<\/h2>([\s\S]*?)(?=')
+ let depth = 0, count = 0
+ if (start >= 0) for (const [, close, tag] of body.slice(start).matchAll(/<(\/?)(ol|ul|li)\b[^>]*>/g)) {
+ if (tag === 'li') { if (!close && depth === 1) count++; continue }
+ depth += close ? -1 : 1
+ if (depth === 0) break
+ }
+ absaetze[n] = count
+ }
+ assert.equal(Object.keys(absaetze).length, 16)
+ const text = strip(html)
+ const refs = [...text.matchAll(/§ (\d+) Abs\. (\d+)/g)]
+ assert.ok(refs.length >= 15, `Querverweise: ${refs.length}`)
+ for (const [ref, n, a] of refs) assert.ok(Number(a) <= (absaetze[n] ?? 0), `${ref}: § ${n} hat ${absaetze[n] ?? 0} Absätze`)
+ // Leistungsbeschreibung A/B Nr. N: nummerierte der jeweiligen Beschreibung
+ const nummern = (fromId, toId) => [...html.slice(html.indexOf(`id="${fromId}"`), html.indexOf(`id="${toId}"`)).matchAll(/(\d+)\. /g)].map((x) => Number(x[1]))
+ const maxA = Math.max(...nummern('hosting', 'wartung')), maxB = Math.max(...nummern('wartung', 'widerruf'))
+ for (const [ref, ab, nr] of text.matchAll(/Leistungsbeschreibung ([AB]) Nr\. (\d+)/g)) assert.ok(Number(nr) <= (ab === 'A' ? maxA : maxB), ref)
+ for (const [ref, nr] of v4Section('hosting', 'wartung').matchAll(/\bNr\. (\d+)/g)) assert.ok(Number(nr) <= maxA, `A: ${ref}`)
+ for (const [ref, nr] of v4Section('wartung', 'widerruf').matchAll(/\bNr\. (\d+)/g)) assert.ok(Number(nr) <= maxB, `B: ${ref}`)
+})
|