Selbstbedienungs-Tarifverwaltung: Admin kann Abo im Backend waehlen und bestellen
- mandant-einstellungen.php: vollstaendige Tarif-Vergleichstabelle statt nur erzwungenem Upgrade-Hinweis. Jeder Tarif zeigt Limit, Preis und einen Aktions-Button (Buchen/Kuendigen/Anfragen); aktueller Tarif markiert, zu kleine Tarife fuer die aktuelle Teilnehmerzahl deaktiviert. - abo-upgrade.php: unterscheidet jetzt Upgrade, Downgrade zwischen bezahlten Stufen (Preis wird in-place gewechselt statt neuer Checkout-Session, Stripe prorated automatisch) und Wechsel auf 'free' (echte Kuendigung der bestehenden Subscription), statt nur eine Checkout-Session zu erzeugen. - app/stripe.php: neue Helper stripe_get_subscription(), stripe_update_subscription_price(), stripe_cancel_subscription(). - app/billing.php: billing_plan_selectable_for() prueft serverseitig, ob ein Zieltarif die aktuelle Teilnehmerzahl noch abdeckt (Downgrade-Schutz). - docs/billing.md: Phase 4 dokumentiert. Getestet: php -l fuer alle geaenderten Dateien und das gesamte Repo (keine Syntaxfehler), reine Funktionslogik-Tests ohne DB (billing_plans(), billing_required_plan_code(), stripe_flatten_params()) gruen. Kein Live-Test gegen echte Stripe-Testobjekte moeglich (keine PHP/DB-Laufzeitumgebung verfuegbar) - vor Go-Live im Stripe-Testmodus nachholen.
This commit is contained in:
@@ -259,3 +259,71 @@ stripe-webhook.php (invoice.paid ruft dolibarr_sync_invoice_paid())
|
||||
scharf zu bestätigen.
|
||||
- Zahlungserfassung in Dolibarr (Rechnung als bezahlt markieren) als
|
||||
möglicher Ausbau, sobald Bankkonto-/Zahlungsart-IDs bekannt sind.
|
||||
|
||||
## Phase 4: Selbstbedienungs-Tarifverwaltung im Mandanten-Backend (umgesetzt)
|
||||
|
||||
**Ausgangslage:** Bislang sah der Mandant nur einen erzwungenen
|
||||
Upgrade-Hinweis, sobald sein aktueller Tarif die Teilnehmerzahl nicht mehr
|
||||
deckte (`billingCheck['needs_upgrade']`); er konnte keinen anderen Tarif
|
||||
frei wählen, kein Downgrade vornehmen und keine echte Bestellung auslösen,
|
||||
solange kein Pflicht-Upgrade anstand.
|
||||
|
||||
Umgesetzte Dateien:
|
||||
|
||||
```text
|
||||
app/stripe.php (stripe_get_subscription, stripe_update_subscription_price, stripe_cancel_subscription)
|
||||
app/billing.php (billing_plan_selectable_for)
|
||||
abo-upgrade.php (Fallunterscheidung Upgrade/Downgrade/Kündigung)
|
||||
mandant-einstellungen.php (vollständige Tarif-Vergleichstabelle mit Bestell-Button je Tarif)
|
||||
```
|
||||
|
||||
- **Tarif-Vergleichstabelle** in „Abo & Tarif" zeigt jetzt alle Tarifstufen
|
||||
(nicht nur den benötigten) mit Limit, Preis und einem Aktions-Button je
|
||||
Zeile: aktueller Tarif ist markiert, Tarife die die aktuelle
|
||||
Teilnehmerzahl nicht abdecken sind deaktiviert
|
||||
(`billing_plan_selectable_for()`), Tarife ohne Stripe-Preis (`enterprise`)
|
||||
verlinken auf eine manuelle Anfrage per E-Mail.
|
||||
- **`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.
|
||||
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
|
||||
(`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,
|
||||
Subscription-Item-ID und aktuellen `lookup_key`),
|
||||
`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).
|
||||
- **`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
|
||||
Server-seitige Validierung in `abo-upgrade.php`.
|
||||
|
||||
### Bewusste Entscheidungen
|
||||
|
||||
- Downgrade auf einen bezahlten, aber zu kleinen Tarif wird serverseitig
|
||||
abgelehnt (`billing_plan_selectable_for()`), nicht nur clientseitig
|
||||
ausgegraut – verhindert, dass ein Mandant sich selbst unter sein
|
||||
eigenes Teilnehmerlimit bucht.
|
||||
- `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.
|
||||
|
||||
### Noch offen
|
||||
|
||||
- Kein automatisierter Live-Test gegen echte Stripe-Testobjekte für den
|
||||
In-Place-Preiswechsel (`stripe_update_subscription_price`) – die
|
||||
vorhandene Dev-Umgebung hat keinen PHP-Interpreter/DB-Zugriff für einen
|
||||
Live-Check; nur Syntax- und reine Funktionslogik-Tests ohne DB/Stripe-
|
||||
Zugriff wurden hier durchgeführt. Vor Go-Live einmal im Stripe-Testmodus
|
||||
gegen einen echten Test-Mandanten durchspielen (Upgrade, Downgrade
|
||||
zwischen zwei bezahlten Stufen, Downgrade auf `free`).
|
||||
|
||||
Reference in New Issue
Block a user