Billing Phase 2: Stripe Checkout, Kundenportal und Webhook-Verarbeitung
app/stripe.php: minimaler REST-Client fuer die Stripe-API auf Basis von PHP-Streams statt eines vendorten SDKs - diese PHP-Installation hat keine curl-Extension, Streams sind zudem portabler und brauchen kein composer.json. - Fuer jeden bezahlten Tarif per API ein Stripe-Produkt mit monatlichem Preis angelegt, referenziert ueber einen stabilen lookup_key statt hartcodierter Price-ID; Erstellung ist idempotent. - abo-upgrade.php: erstellt eine Stripe-Checkout-Session fuer den gewaehlten Tarif, tenant_id/plan_code als Metadaten auf Session UND Subscription (damit spaetere Subscription-Events zuordenbar bleiben). - abo-portal.php: oeffnet das Stripe Customer Portal fuer bestehende Kunden (Zahlungsmittel/Kuendigung, ohne eigene UI dafuer). - stripe-webhook.php: verifiziert die Stripe-Signature per HMAC-SHA256 mit Zeitstempel-Toleranz, verarbeitet checkout.session.completed, customer.subscription.updated/.deleted, invoice.paid/.payment_failed und haelt tenant_billing aktuell. Bewusst kein CSRF-/Login-Check (Stripe ruft unauthentifiziert auf), stattdessen ausschliesslich Signaturpruefung als Echtheitsnachweis. - mandant-einstellungen.php: echter "Jetzt upgraden"-Button (Stripe Checkout) sowie "Zahlungsmethode verwalten/Abo kuendigen" (Customer Portal), sobald ein Stripe-Kunde existiert. Live getestet (Stripe-Testmodus, kein echtes Geld): voller Checkout-Flow bis zum echten Redirect auf checkout.stripe.com, Webhook-Verarbeitung durch selbst erzeugte, korrekt signierte Test-Events (da diese Dev-Umgebung keine oeffentlich erreichbare URL fuer echte Stripe- Zustellung hat) inklusive Ablehnung falscher Signaturen, Customer Portal mit echtem per API angelegtem Test-Kunden. Alle Regressionstests weiterhin gruen (36 Seiten HTTP-Smoke, Golden Master, M8-Isolation/ Rollenmatrix). Noch offen: echten Webhook-Endpunkt auf die Produktions-Domain eintragen, sobald diese feststeht; Wechsel auf Live-Keys; Dolibarr-Sync (Phase 3). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+78
-11
@@ -66,19 +66,86 @@ als „basic" erkannt) geprüft; UI-Anzeige in Mandant-Einstellungen und
|
||||
Back-Office live verifiziert. Alle bestehenden M8-Isolationstests und der
|
||||
Rollen-Matrix-Test bleiben unverändert grün.
|
||||
|
||||
## Phase 2: Stripe-Anbindung (noch offen)
|
||||
## Phase 2: Stripe-Anbindung (umgesetzt)
|
||||
|
||||
Wartet auf Stripe-Test-API-Keys vom Kunden. Geplanter Umfang:
|
||||
Umgesetzte Dateien:
|
||||
|
||||
- Stripe Checkout zum Abschließen/Wechseln eines bezahlten Tarifs.
|
||||
- Webhook-Endpunkt für `checkout.session.completed`,
|
||||
`customer.subscription.updated`/`.deleted`, `invoice.paid`/`.payment_failed`,
|
||||
der `tenant_billing` aktuell hält.
|
||||
- Stripe Customer Portal für Zahlungsmittel-Verwaltung/Kündigung durch
|
||||
den Kunden selbst.
|
||||
- Automatischer Stufenwechsel: Sobald `billing_check_tenant()` einen
|
||||
höheren Bedarf erkennt, Wechsel der Stripe-Subscription auslösen
|
||||
(mit Proration) statt nur anzuzeigen.
|
||||
```text
|
||||
app/stripe.php
|
||||
app/billing.php (Stripe-Preis-Auflösung ergänzt)
|
||||
abo-upgrade.php
|
||||
abo-portal.php
|
||||
stripe-webhook.php
|
||||
mandant-einstellungen.php (Upgrade-/Portal-Buttons)
|
||||
```
|
||||
|
||||
- `app/stripe.php`: minimaler REST-Client für die Stripe-API auf Basis von
|
||||
PHP-Streams (`file_get_contents` + `stream_context_create`), **kein**
|
||||
vendortes SDK und **kein** composer.json nötig – diese PHP-Installation
|
||||
hat ohnehin keine curl-Extension, Streams sind portabler.
|
||||
- Für jeden bezahlten Tarif (`basic`/`plus`/`pro`) wurde per API ein
|
||||
Stripe-Produkt mit monatlichem Preis angelegt, referenziert über einen
|
||||
stabilen `lookup_key` (`kaffeeliste_basic`/`_plus`/`_pro`) statt einer
|
||||
hartcodierten Price-ID – `stripe_find_or_create_price()` ist idempotent,
|
||||
ein erneuter Aufruf legt nichts doppelt an.
|
||||
- `abo-upgrade.php`: erstellt eine Stripe-Checkout-Session (Modus
|
||||
`subscription`) für den vom Kunden gewählten Tarif und leitet dorthin
|
||||
weiter. `tenant_id`/`plan_code` werden sowohl auf der Checkout-Session
|
||||
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.
|
||||
- `stripe-webhook.php`: verifiziert die `Stripe-Signature` nach dem von
|
||||
Stripe dokumentierten HMAC-SHA256-Schema (inklusive Zeitstempel-Toleranz
|
||||
gegen Replay), verarbeitet `checkout.session.completed`,
|
||||
`customer.subscription.updated`/`.deleted`,
|
||||
`invoice.paid`/`.payment_failed` und hält `tenant_billing` aktuell.
|
||||
Bewusst kein `app_require_csrf()`/`saas_require_login()` (Stripe ruft
|
||||
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).
|
||||
|
||||
**Noch nicht umgesetzt:** automatischer Stufenwechsel bei wachsender
|
||||
Teilnehmerzahl (aktuell muss der Kunde selbst erneut auf „Upgraden"
|
||||
klicken, wenn `billing_check_tenant()` einen höheren Bedarf anzeigt).
|
||||
|
||||
### Live getestet (Testmodus, kein echtes Geld)
|
||||
|
||||
- Checkout-Session-Erstellung direkt über die API sowie über den vollen
|
||||
Seiten-Flow (Login → Mandant-Einstellungen zeigt Upgrade-Button bei 13
|
||||
Teilnehmern → Klick → echter 302-Redirect zu einer
|
||||
`checkout.stripe.com`-URL).
|
||||
- Webhook-Verarbeitung durch selbst erzeugte, korrekt signierte
|
||||
Test-Ereignisse (da diese Dev-Umgebung keine öffentlich erreichbare URL
|
||||
für echte Stripe-Zustellung hat): `checkout.session.completed` setzt
|
||||
`tenant_billing` korrekt (Tarif, Status, Stripe-IDs) und wird im
|
||||
Mandanten-Audit-Log protokolliert; `customer.subscription.deleted`
|
||||
setzt den Mandanten korrekt auf `free`/`canceled` zurück.
|
||||
- Signaturprüfung: eine Anfrage mit falscher Signatur wird korrekt mit
|
||||
`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`.
|
||||
- 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
|
||||
Stripe-Testmodus belassen; lokale DB-Testdaten wurden entfernt)
|
||||
aufgeräumt.
|
||||
|
||||
### Für den Produktivbetrieb noch zu tun
|
||||
|
||||
- Echten Webhook-Endpunkt im Stripe-Dashboard (oder per API) auf die
|
||||
produktive Domain (`https://app.kaffeeliste.de/stripe-webhook.php`)
|
||||
eintragen und das dortige Signing-Secret als `STRIPE_WEBHOOK_SECRET`
|
||||
hinterlegen – das aktuelle Secret ist nur ein lokal erzeugter Platzhalter
|
||||
für die Simulation in dieser Dev-Umgebung.
|
||||
- Von Test- auf Live-API-Keys wechseln, sobald ein echter Stripe-Account
|
||||
mit Bankverbindung eingerichtet ist.
|
||||
|
||||
## Phase 3: Dolibarr-Anbindung (noch offen)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user