Rechtstexte und B2C-Vertragsabläufe absichern

This commit is contained in:
2026-08-22 14:31:56 +02:00
parent d320a4fd7a
commit a31a235422
58 changed files with 2502 additions and 316 deletions
+24 -15
View File
@@ -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