Rechtstexte und B2C-Vertragsabläufe absichern
This commit is contained in:
+24
-15
@@ -94,9 +94,10 @@ mandant-einstellungen.php (Upgrade-/Portal-Buttons)
|
||||
als auch auf der Subscription selbst als Metadaten gesetzt, damit spätere
|
||||
Subscription-Events (die kein Checkout-Session-Objekt mehr enthalten)
|
||||
trotzdem einem Mandanten zugeordnet werden können.
|
||||
- `abo-portal.php`: öffnet das Stripe Customer Portal für den bestehenden
|
||||
Stripe-Kunden (Zahlungsmittel ändern, Abo kündigen) – Self-Service ohne
|
||||
eigene UI dafür.
|
||||
- `abo-portal.php`: öffnet für den bestehenden Stripe-Kunden einen gezielten
|
||||
Customer-Portal-Flow ausschließlich zum Ändern des Zahlungsmittels. Die
|
||||
Portalnavigation ist in diesem Flow ausgeblendet; Tarifwechsel und
|
||||
Kündigungen laufen über die dokumentierten Funktionen der Anwendung.
|
||||
- `stripe-webhook.php`: verifiziert die `Stripe-Signature` nach dem von
|
||||
Stripe dokumentierten HMAC-SHA256-Schema (inklusive Zeitstempel-Toleranz
|
||||
gegen Replay), verarbeitet `checkout.session.completed`,
|
||||
@@ -106,9 +107,9 @@ mandant-einstellungen.php (Upgrade-/Portal-Buttons)
|
||||
unauthentifiziert von außen auf), stattdessen ausschließlich die
|
||||
Signaturprüfung als Echtheitsnachweis.
|
||||
- `mandant-einstellungen.php`: zeigt bei Bedarf einen echten
|
||||
„Jetzt upgraden"-Button (führt zu Stripe Checkout) sowie, sobald ein
|
||||
Stripe-Kunde existiert, „Zahlungsmethode verwalten / Abo kündigen"
|
||||
(führt zum Customer Portal).
|
||||
Bestellablauf (führt bei einer Neubuchung anschließend zu Stripe Checkout)
|
||||
sowie, sobald ein Stripe-Kunde existiert, „Zahlungsmethode verwalten"
|
||||
(gezielter Stripe-Portal-Flow).
|
||||
|
||||
**Bewusst nicht umgesetzt:** automatischer Stufenwechsel bei wachsender
|
||||
Teilnehmerzahl. Stattdessen gilt eine harte Obergrenze (siehe unten):
|
||||
@@ -168,7 +169,8 @@ scripts/check-billing-capacity.php (Regressionstest)
|
||||
`400` abgelehnt.
|
||||
- Customer Portal: mit einem echten, per API angelegten Stripe-Test-Kunden
|
||||
liefert `abo-portal.php` einen echten 302-Redirect zu
|
||||
`billing.stripe.com`.
|
||||
`billing.stripe.com`; vor Produktivfreigabe den neuen eingeschränkten
|
||||
Zahlungsarten-Flow erneut im Testmodus prüfen.
|
||||
- Alle bestehenden Regressionstests (Golden Master, M8-Isolation,
|
||||
M8-Rollenmatrix, HTTP-Smoke mit 36 Seiten) weiterhin grün.
|
||||
- Alle Testdaten (Stripe-Testobjekte kosten nichts und wurden im
|
||||
@@ -271,8 +273,9 @@ solange kein Pflicht-Upgrade anstand.
|
||||
Umgesetzte Dateien:
|
||||
|
||||
```text
|
||||
app/stripe.php (stripe_get_subscription, stripe_update_subscription_price, stripe_cancel_subscription)
|
||||
app/stripe.php (stripe_get_subscription, stripe_update_subscription_price, stripe_schedule_subscription_cancellation)
|
||||
app/billing.php (billing_plan_selectable_for)
|
||||
abo-bestellen.php (Bestellzusammenfassung und Rechtstextbestätigung)
|
||||
abo-upgrade.php (Fallunterscheidung Upgrade/Downgrade/Kündigung)
|
||||
mandant-einstellungen.php (vollständige Tarif-Vergleichstabelle mit Bestell-Button je Tarif)
|
||||
```
|
||||
@@ -286,12 +289,13 @@ mandant-einstellungen.php (vollständige Tarif-Vergleichstabelle mit Bestell-But
|
||||
- **`abo-upgrade.php`** unterscheidet jetzt drei Fälle statt nur
|
||||
„Checkout-Session erzeugen":
|
||||
1. Zielwert `free` mit bestehender bezahlter Subscription → echtes
|
||||
`stripe_cancel_subscription()`, lokal auf `free`/`canceled` gesetzt.
|
||||
`stripe_schedule_subscription_cancellation()` zum Ende der bereits
|
||||
bezahlten Periode; Tarif und Zugriff bleiben bis dahin erhalten.
|
||||
2. Zielwert ein anderer bezahlter Tarif **und** bereits eine
|
||||
aktive/`trialing`/`past_due`-Subscription vorhanden → Preis wird
|
||||
in-place über `stripe_update_subscription_price()` gewechselt
|
||||
(Stripe-Proration übernimmt die anteilige Verrechnung), kein neuer
|
||||
Checkout-Redirect nötig. Lokal sofort gespiegelt, der Webhook
|
||||
(ohne Zwischenbelastung oder Gutschrift in der laufenden Periode), kein
|
||||
neuer Checkout-Redirect nötig. Lokal sofort gespiegelt, der Webhook
|
||||
(`customer.subscription.updated`) bestätigt denselben Stand redundant.
|
||||
3. Kein aktives Abo bisher → wie vorher eine neue Stripe-Checkout-Session.
|
||||
- **`app/stripe.php`** neu: `stripe_get_subscription()` (liest Status,
|
||||
@@ -299,8 +303,9 @@ mandant-einstellungen.php (vollständige Tarif-Vergleichstabelle mit Bestell-But
|
||||
`stripe_update_subscription_price()` (Preis am bestehenden
|
||||
Subscription-Item austauschen, `cancel_at_period_end` explizit auf
|
||||
`false` – ein Tarifwechsel ist ein Signal, das Abo fortzuführen),
|
||||
`stripe_cancel_subscription()` (sofortige Kündigung, nicht zum
|
||||
Periodenende).
|
||||
`stripe_schedule_subscription_cancellation()` (ordentliche Kündigung zum
|
||||
Periodenende). `stripe_cancel_subscription()` bleibt dem wirksamen
|
||||
Verbraucherwiderruf vorbehalten.
|
||||
- **`app/billing.php`** neu: `billing_plan_selectable_for()` prüft, ob die
|
||||
aktive Teilnehmerzahl das Limit eines Zieltarifs einhält – Basis für das
|
||||
Deaktivieren nicht passender Tarife in der UI und für die
|
||||
@@ -315,8 +320,12 @@ mandant-einstellungen.php (vollständige Tarif-Vergleichstabelle mit Bestell-But
|
||||
- `enterprise` bleibt bewusst kein Selbstbedienungs-Ziel (kein
|
||||
Stripe-Preis vorhanden, Preis „auf Anfrage"); Auswahl verlinkt auf
|
||||
manuellen Kontakt statt eine falsche Buchung zu versuchen.
|
||||
- Bestätigungsdialog (JS `confirm()`) vor jedem Tarifwechsel, da ein
|
||||
Wechsel bei Stripe sofort wirksam wird und anteilig verrechnet wird.
|
||||
- Vor kostenpflichtiger Neubuchung oder Tarifwechsel zeigt
|
||||
`abo-bestellen.php` Leistung, Monatsgesamtpreis, Laufzeit, Kündigung und
|
||||
technische Voraussetzungen. Der Abschluss erfolgt über die eindeutig
|
||||
beschriftete Schaltfläche „zahlungspflichtig bestellen"; AGB,
|
||||
Widerrufsbelehrung und gewünschter sofortiger Leistungsbeginn werden
|
||||
versioniert protokolliert.
|
||||
|
||||
### Noch offen
|
||||
|
||||
|
||||
+44
-6
@@ -1,6 +1,6 @@
|
||||
# Deployment und Go-Live
|
||||
|
||||
Stand: 2026-08-06
|
||||
Stand: 2026-08-22
|
||||
|
||||
Dieses Dokument beschreibt, wie die Anwendung auf den Netcup-Webspace
|
||||
(Plesk) ausgerollt und in Betrieb genommen wird. Es ergänzt
|
||||
@@ -54,6 +54,8 @@ die `.htaccess` gesperrt.
|
||||
Zusätzlich sperrt die `.htaccess` `var|database|scripts|docs|app|lib`,
|
||||
die Vendor-Verzeichnisse `TCPDF|PHPMailer|DataTables`, Dotfile-Ordner
|
||||
sowie Konfigurationsdateien und `.sql`/`.md`/`.jsonc`/`.sh`/`.log`-Dateien.
|
||||
Im Ordner `legal/` sind ausschließlich datierte `.txt`-Fassungen von AGB,
|
||||
Datenschutz, AVV und Widerrufsbelehrung als dauerhafte Downloads erlaubt.
|
||||
Sie erzwingt außerdem HTTPS — ohne Redirect käme ein Nutzer per `http` an
|
||||
und bekäme eine Session-Cookie ohne `secure`-Flag.
|
||||
|
||||
@@ -135,7 +137,7 @@ den CSV-Import. Beide sind per `.htaccess` von außen gesperrt.
|
||||
|
||||
### 6. Migrationen einspielen
|
||||
|
||||
Es gibt 24 versionierte Migrationen in `database/migrations/`, angewendet
|
||||
Es gibt 30 versionierte Migrationen in `database/migrations/`, angewendet
|
||||
über `scripts/migrate.php`. Das Skript ist idempotent und wendet nur
|
||||
fehlende Migrationen an.
|
||||
|
||||
@@ -173,6 +175,8 @@ PayPal-Verbuchung und — kritisch — die Backups.
|
||||
| --- | --- | --- |
|
||||
| Zahlungserinnerungen | `/httpdocs/scripts/send-payment-reminders.php` | täglich, z. B. 07:00 |
|
||||
| PayPal-Mailabruf | `/httpdocs/scripts/fetch-paypal-payments.php` | alle 15–30 Minuten |
|
||||
| Technische Löschfristen | `/httpdocs/scripts/purge-expired-operational-data.php` | stündlich |
|
||||
| Beendete Verträge löschen | `/httpdocs/scripts/purge-ended-contracts.php` | täglich |
|
||||
| Datenbank-Backup | Shell-Befehl, siehe `docs/betrieb-backup-monitoring.md` | täglich nachts |
|
||||
|
||||
Zum Backup-Task: Das Zielverzeichnis muss **außerhalb** von `httpdocs`
|
||||
@@ -181,8 +185,10 @@ liegen (z. B. `/backups` auf Vhost-Ebene), sonst wären die Dumps
|
||||
Ort übertragen — ein Backup, das nur auf demselben Webspace liegt,
|
||||
schützt nicht gegen den Ausfall des Anbieters.
|
||||
|
||||
Vor der Scharfschaltung beide PHP-Tasks einmal manuell auslösen.
|
||||
`send-payment-reminders.php` unterstützt dafür `--dry-run`.
|
||||
Vor der Scharfschaltung die Aufgaben mit Lösch- oder Buchungswirkung zuerst
|
||||
manuell mit `--dry-run` auslösen. Dies unterstützen
|
||||
`send-payment-reminders.php`, `fetch-paypal-payments.php`,
|
||||
`purge-expired-operational-data.php` und `purge-ended-contracts.php`.
|
||||
|
||||
## Mailversand und DNS
|
||||
|
||||
@@ -227,6 +233,12 @@ Details zur Funktionsweise stehen in `docs/billing.md`. Für den Go-Live:
|
||||
echte Zahlungseingang ist dessen erster Test. Danach in Dolibarr
|
||||
kontrollieren, ob die Rechnung korrekt angelegt und validiert wurde.
|
||||
|
||||
Der Link „Zahlungsmethode verwalten“ startet technisch ausschließlich einen
|
||||
Stripe-Flow zur Aktualisierung der Zahlungsart. Im Stripe-Dashboard dürfen der
|
||||
öffentliche No-Code-Portal-Login und Tarifwechsel außerhalb der Anwendung nicht
|
||||
zusätzlich freigeschaltet werden; Bestellungen und Kündigungen sollen nur über
|
||||
die dokumentierten Abläufe der Kaffeeliste erfolgen.
|
||||
|
||||
## PayPal-Postfach
|
||||
|
||||
Für die automatische Verbuchung weitergeleiteter PayPal-Zahlungsmails
|
||||
@@ -299,6 +311,16 @@ frisches Backup ziehen.
|
||||
|
||||
Vor der Umstellung auf `app.kaffeeliste.de`:
|
||||
|
||||
Zuerst aus dem Projektverzeichnis ausführen:
|
||||
|
||||
```
|
||||
php scripts/check-production-readiness.php
|
||||
```
|
||||
|
||||
Der Check blockiert unter anderem bei fehlender B2C-Telefonnummer,
|
||||
unsicherer Basis-URL, fehlenden Zahlungs-/Abrechnungsschlüsseln, zu offenen
|
||||
Rechten der Secret-Dateien oder einer nicht angewendeten Legal-Migration.
|
||||
|
||||
- [ ] Kompletter Funktionsdurchlauf auf Staging erfolgreich
|
||||
(Registrierung, Mailversand, Mitglieder, Striche, Einzahlungen,
|
||||
CSV-Import, PDF-Export, Jahresauswertung, Stripe im Testmodus)
|
||||
@@ -308,8 +330,24 @@ Vor der Umstellung auf `app.kaffeeliste.de`:
|
||||
- [ ] Backup-Task läuft und ein Restore wurde einmal testweise
|
||||
eingespielt (`docs/betrieb-backup-monitoring.md`)
|
||||
- [ ] Stripe-Live-Keys und Produktiv-Webhook eingetragen
|
||||
- [ ] Impressum, AGB und Datenschutzerklärung inhaltlich freigegeben
|
||||
- [ ] Auftragsverarbeitungsvertrag (AVV) für Kunden vorbereitet
|
||||
- [ ] `LEGAL_PHONE`, `LEGAL_EMAIL` und `LEGAL_TICKET_URL` zeigen auf
|
||||
tatsächlich betreute Kontaktwege; Tickets werden werktags regelmäßig
|
||||
bearbeitet
|
||||
- [ ] Impressum, AGB, Datenschutz, AVV und Widerrufsbelehrung inhaltlich
|
||||
anwaltlich für B2B und B2C freigegeben
|
||||
- [ ] Kündigungs- und Widerrufsformular erzeugen beim Kunden und bei
|
||||
`LEGAL_EMAIL` eine E-Mail; die Textbestätigung lässt sich speichern
|
||||
- [ ] Offene Rechtserklärungen werden täglich im Back-Office geprüft;
|
||||
Rückzahlungen nach Widerruf werden manuell über Stripe fristgerecht
|
||||
bearbeitet (die Anwendung beendet das Abo, erstattet aber nicht automatisch)
|
||||
- [ ] Stripe Checkout läuft auf Deutsch, zeigt Endpreis und Monatslaufzeit;
|
||||
Test von Neubuchung, Tarifwechsel und Kündigung zum Periodenende
|
||||
- [ ] Webserver-Access-Logs werden spätestens nach sieben Tagen gelöscht oder
|
||||
anonymisiert; TLS/HSTS ist auf allen öffentlichen Domains aktiv
|
||||
- [ ] Täglicher Task `php scripts/purge-ended-contracts.php` ist eingerichtet;
|
||||
ein Vorlauf mit `--dry-run` wurde kontrolliert
|
||||
- [ ] Stündlicher Task `php scripts/purge-expired-operational-data.php` ist
|
||||
eingerichtet; ein Vorlauf mit `--dry-run` wurde kontrolliert
|
||||
- [ ] Frische Produktivdatenbank ohne Testdaten
|
||||
|
||||
## Bewusst offen
|
||||
|
||||
Reference in New Issue
Block a user