further rework to the documentation

This commit is contained in:
Codex Agent
2025-11-20 12:31:21 +01:00
parent 6afa44d947
commit 9afcaa7836
90 changed files with 1721 additions and 29 deletions

133
docs/ops/billing-ops.md Normal file
View File

@@ -0,0 +1,133 @@
---
title: Billing & Zahlungs-Operationen
sidebar_label: Billing-Runbook
---
Dieses Runbook beschreibt, wie mit Zahlungsproblemen, Paddle/RevenueCatWebhooks und PaketInkonsistenzen operativ umzugehen ist.
## 1. Komponentenüberblick
- **Paddle**
- Abwicklung von WebCheckout, Paketen und Subscriptions.
- Webhooks für Käufe, Verlängerungen, Stornos.
- **Fotospiel Backend**
- Modelle wie `Tenant`, `Packages`, `tenant_packages`, `event_packages`.
- Services/Jobs zur PaketZuweisung, LimitBerechnung und Nutzungstracking.
> Details zur Architektur findest du in den PRPKapiteln (Billing/Freemium) sowie in den TODODokumenten unter `docs/process/todo/paddle-migration.md` und `docs/process/todo/paddle-catalog-sync.md`.
## 2. Typische Problemszenarien
- **Webhook kommt nicht an / schlägt fehl**
- Symptom: Paddle zeigt Zahlung „completed“, TenantPaket im Backend bleibt unverändert.
- Checkliste:
- Logs der WebhookRoutes prüfen (Statuscodes, Exceptions).
- Endpoint: `POST /paddle/webhook``PaddleWebhookController::handle()`.
- Controller ruft `CheckoutWebhookService::handlePaddleEvent()` auf.
- WebhookReplay über das Paddle Dashboard auslösen (für einzelne Events).
- QueueStatus prüfen:
- Falls Webhooks über Queues verarbeitet werden, auf `default`/`billing`Queues achten (je nach Konfiguration).
- **Doppelte oder fehlende Abbuchungen**
- Abgleich von ZahlungsproviderDaten (Paddle/RevenueCat) mit internem Ledger.
- Bei doppelten Buchungen: Prozess definieren (Refund via Paddle, Anpassung im Ledger).
- Bei fehlenden Buchungen: ggf. manuelle Paketzuweisung nach erfolgter Zahlung.
- **Pakete/Limits passen nicht zur Realität**
- Tenant meldet: „Paket falsch“, „Galerie schon abgelaufen“ o.Ä.
- Prüfen:
- Aktives Paket (`tenant_packages`, `event_packages`).
- LimitZähler (`used_photos`, `used_events`) und aktuelle Nutzung.
- Letzte relevante Webhooks/Jobs (z.B. vor kurzem migriert?).
## 3. Operative Schritte bei Payment Incidents
1. **Event/Tenant identifizieren**
- IDs und relevante PaketInfos aus DB/Admin UI holen.
2. **Provider-Status prüfen**
- PaddleDashboard: ist die Zahlung dort korrekt verbucht? (Transaktions/AbonnementAnsicht).
3. **Backend-Status prüfen**
- Paketzuweisung und Limits in der DB (Readonly!) inspizieren:
- `checkout_sessions` wurde die Session korrekt auf `completed` gesetzt? (`provider = paddle`, `paddle_transaction_id` gefüllt?)
- `package_purchases` existiert ein Eintrag für Tenant/Package mit erwarteter ProviderReferenz?
- `tenant_packages` stimmt der `active`Status und `expires_at` mit dem erwarteten Abostatus überein?
4. **Entscheidung**
- Automatische Nachverarbeitung via WebhookReplay/JobRetry:
- PaddleEvents erneut senden lassen, ggf. `tests/api/_testing/checkout/sessions/{session}/simulate-paddle` (in TestUmgebungen) nutzen.
- Notfall: manuelle PaketAnpassung (nur mit klar dokumentierter Begründung):
- Paket in `tenant_packages` aktivieren/verlängern und `package_purchases` sauber nachziehen.
5. **Dokumentation**
- Vorgang im Ticket / `docs/process/changes/*` festhalten, falls wiederkehrend.
> TODO: Ergänze konkrete Tabellen-/Modellnamen und die relevanten Jobs/Artisan Commands, sobald Paddle/RevenueCat Migration finalisiert ist.
## 4. Zusammenarbeit mit Finance/Support
- Klar definieren, wer Rückerstattungen freigibt und durchführt.
- Playbook für Support:
- Welche Informationen sie sammeln sollen, bevor sie an Ops eskalieren (TenantID, EventID, PaymentProviderReferenz, Zeitstempel).
- Welche Standardantworten es gibt (z.B. „Zahlung in Prüfung, Paket kurzfristig manuell freigeschaltet“).
## 5. Hinweise zur Implementierung
- **Konfiguration**
- PaddleKeys, WebhookSecrets und FeatureFlags sollten ausschließlich in `.env`/ConfigDateien liegen und niemals im Code/Logs landen.
- Sandbox vs. LiveKeys klar trennen; Ops sollte wissen, welche Umgebung gerade aktiv ist.
- **Sicherheit**
- WebhookSignaturen und Timestamps prüfen; bei verdächtigen Mustern (z.B. ReplayAngriffe) SecurityRunbooks konsultieren.
- Keine sensiblen PaymentDetails in ApplikationsLogs ausgeben.
Diese Sektion ist bewusst generisch gehalten, damit sie auch nach Implementation der finalen BillingArchitektur noch passt. Details zu Tabellen/Jobs sollten ergänzt werden, sobald die PaddleMigration abgeschlossen ist.
## 6. Konkrete Paddle-Flows im System
### 6.1 Checkout-Erstellung
- Marketing-Checkout / API:
- `MarketingController` und `PackageController` nutzen `PaddleCheckoutService::createCheckout()` (`App\Services\Paddle\PaddleCheckoutService`).
- Der Service:
- Stellt sicher, dass ein `paddle_customer_id` für den Tenant existiert (`PaddleCustomerService::ensureCustomerId()`).
- Baut Metadaten (`tenant_id`, `package_id`, optional `checkout_session_id`) für spätere Zuordnung.
- Ruft `POST /checkout/links` im PaddleAPI auf und erhält eine `checkout_url`.
- Ops-Sicht:
- Wenn `paddle_price_id` bei einem Package fehlt, wird kein Checkout erzeugt MarketingUI zeigt entsprechende Fehlertexte (siehe `resources/lang/*/marketing.php`).
- Bei wiederkehrenden „checkout failed“Fehlern die Logs (`PaddleCheckoutService`, Controller) und PackageKonfiguration prüfen.
### 6.2 Webhook-Verarbeitung & Idempotenz
- Endpoint: `POST /paddle/webhook``PaddleWebhookController::handle()`.
- Service: `CheckoutWebhookService` (`App\Services\Checkout\CheckoutWebhookService`):
- Unterscheidet zwischen **TransaktionsEvents** (`transaction.*`) und **SubscriptionEvents** (`subscription.*`).
- Idempotenz:
- Nutzt ein CacheLock (`checkout:webhook:paddle:{transaction_id|session_id}`), um parallele Verarbeitung desselben Events zu verhindern.
- Schreibt Metadaten (`paddle_last_event`, `paddle_status`, `paddle_checkout_id`) in `checkout_sessions.provider_metadata`.
- Ergebnis:
- Bei `transaction.completed`:
- `CheckoutSession` wird als `processing` markiert.
- `CheckoutAssignmentService::finalise()` weist Paket/Tenant zu.
- Session wird auf `completed` gesetzt.
- Bei `transaction.failed` / `transaction.cancelled`:
- Session wird auf `failed` gesetzt, Coupons werden als fehlgeschlagen markiert.
### 6.3 Subscriptions & TenantPackages
- SubscriptionEvents (`subscription.*`) werden ebenfalls von `CheckoutWebhookService` behandelt:
- Tenant wird aus `metadata.tenant_id` oder `paddle_customer_id` ermittelt.
- Package wird über `metadata.package_id` oder `paddle_price_id` aufgelöst.
- `TenantPackage` wird erstellt/aktualisiert (`paddle_subscription_id`, `expires_at`, `active`).
- `Tenant.subscription_status` und `subscription_expires_at` werden gesteuert.
- Ops-Sicht:
- Bei abweichenden Abostatus (z.B. Paddle zeigt „active“, Tenant nicht):
- SubscriptionEvents im PaddleDashboard prüfen.
- Letzte `subscription.*`Events in den Logs, `TenantPackage` und `Tenant`Felder gegenprüfen.
### 6.4 Paket- & Coupon-Synchronisation
- Pakete:
- ArtisanCommand `paddle:sync-packages` (`App\Console\Commands\PaddleSyncPackages`) stößt für ausgewählte oder alle Pakete `SyncPackageToPaddle`/`PullPackageFromPaddle` Jobs an.
- SyncJobs nutzen `PaddleCatalogService`, um Produkte/Preise in Paddle zu erstellen/aktualisieren und `paddle_product_id`/`paddle_price_id` lokal zu pflegen.
- Coupons:
- `SyncCouponToPaddle`Job spiegelt interne CouponKonfiguration in Paddle Discounts (`PaddleDiscountService`).
- Ops-Sicht:
- Bei KatalogAbweichungen `paddle:sync-packages --dry-run` verwenden, um Snapshots zu prüfen, bevor tatsächliche Änderungen gesendet werden.
- Fehlgeschlagene Syncs in den Logs (`Paddle package sync failed`, `Paddle discount sync failed`) beobachten.
Diese Untersektion soll dir als Operator helfen zu verstehen, wie PaddleAktionen im System abgebildet sind und an welchen Stellen du im Fehlerfall ansetzen kannst.