Migrate billing from Paddle to Lemon Squeezy

This commit is contained in:
Codex Agent
2026-02-03 10:59:54 +01:00
parent 2f4ebfefd4
commit a0ef90e13a
228 changed files with 4369 additions and 4067 deletions

View File

@@ -13,34 +13,34 @@ Bevor du nachschaust:
- TenantID oder TenantSlug.
- Betroffenes Paket (Name oder Beschreibung, z.B. „ProPaket 79 €“).
- Zeitpunkt der Zahlung (Datum/Uhrzeit, ggf. Screenshot).
- Ggf. Auszug aus der PaddleBestätigung (ohne vollständige Kartendaten!).
- Ggf. Auszug aus der Lemon SqueezyBestätigung (ohne vollständige Kartendaten!).
Diese Infos erlauben dir, die korrekte Transaktion sowohl in Paddle als auch im Backend zu finden.
Diese Infos erlauben dir, die korrekte Transaktion sowohl in Lemon Squeezy als auch im Backend zu finden.
## 2. Paddle-Status prüfen
## 2. Lemon Squeezy-Status prüfen
1. Im PaddleDashboard:
- Suche nach EMail, TenantName oder dem vom Tenant genannten TransaktionsIdentifier.
- Stelle sicher, dass die Zahlung dort als „completed“/„paid“ markiert ist.
1. Im Lemon SqueezyDashboard:
- Suche nach EMail, TenantName oder der vom Tenant genannten OrderID.
- Stelle sicher, dass die Zahlung dort als „paid“/„completed“ markiert ist.
2. Notiere:
- PaddleTransactionID und ggf. CheckoutID.
- Lemon SqueezyOrderID und ggf. CheckoutID.
- Status (paid/processing/failed/cancelled).
Wenn Paddle die Zahlung nicht als erfolgreich zeigt, ist dies primär ein Finance/CustomerTopic ggf. mit Customer Support klären, ob eine neue Zahlung oder Klärung mit dem Kunden notwendig ist.
Wenn Lemon Squeezy die Zahlung nicht als erfolgreich zeigt, ist dies primär ein Finance/CustomerTopic ggf. mit Customer Support klären, ob eine neue Zahlung oder Klärung mit dem Kunden notwendig ist.
## 3. Backend-Status prüfen
Mit bestätigter Zahlung in Paddle:
Mit bestätigter Zahlung in Lemon Squeezy:
1. `checkout_sessions`:
- Suche nach Sessions des Tenants (`tenant_id`) mit dem betroffenen `package_id`:
- Achte auf `status` (erwartet `completed`) und `provider = paddle`.
- Prüfe `provider_metadata` auf `paddle_last_event`, `paddle_status`, `paddle_checkout_id`.
- Wenn du die Session über PaddleMetadaten finden möchtest:
- `paddle_checkout_id` aus dem Webhook/ProviderMetadata oder `transaction_id` verwenden.
- Achte auf `status` (erwartet `completed`) und `provider = lemonsqueezy`.
- Prüfe `provider_metadata` auf `lemonsqueezy_last_event`, `lemonsqueezy_status`, `lemonsqueezy_checkout_id`, `lemonsqueezy_order_id`.
- Wenn du die Session über Lemon SqueezyMetadaten finden möchtest:
- `lemonsqueezy_checkout_id` oder `lemonsqueezy_order_id` verwenden.
2. `package_purchases`:
- Prüfe, ob ein Eintrag für `(tenant_id, package_id)` mit passender ProviderReferenz existiert:
- z.B. `provider = 'paddle'`, `provider_id` = TransactionID.
- z.B. `provider = 'lemonsqueezy'`, `provider_id` = OrderID.
3. `tenant_packages`:
- Prüfe, ob es einen aktiven Eintrag für `(tenant_id, package_id)` gibt:
- `active = 1`, `expires_at` in der Zukunft.
@@ -51,34 +51,34 @@ Wenn `checkout_sessions` noch nicht auf `completed` steht oder `tenant_packages`
1. Logs prüfen:
- `storage/logs/laravel.log` und ggf. `billing`Channel.
- Suche nach Einträgen von `PaddleWebhookController` / `CheckoutWebhookService` rund um den Zahlungszeitpunkt.
- Suche nach Einträgen von `LemonSqueezyWebhookController` / `CheckoutWebhookService` rund um den Zahlungszeitpunkt.
2. Typische Ursachen:
- Webhook nicht zugestellt (Netzwerk/SSL).
- Webhook konnte die Session nicht auflösen (`[CheckoutWebhook] Paddle session not resolved`).
- IdempotenzLock (`Paddle lock busy`) hat dazu geführt, dass Event nur teilweise verarbeitet wurde.
- Webhook konnte die Session nicht auflösen (`[CheckoutWebhook] Lemon Squeezy session not resolved`).
- IdempotenzLock (`Lemon Squeezy lock busy`) hat dazu geführt, dass Event nur teilweise verarbeitet wurde.
## 5. Korrektur-Schritte
### 5.1 Automatischer Replay (empfohlen)
1. Im PaddleDashboard:
- Den betreffenden `transaction.*`Event finden.
1. Im Lemon SqueezyDashboard:
- Den betreffenden `order_*`Event finden.
- WebhookReplay auslösen.
2. In den Logs beobachten:
- Ob `CheckoutWebhookService::handlePaddleEvent()` diesmal die Session findet und `CheckoutAssignmentService::finalise()` ausführt.
- Ob `CheckoutWebhookService::handleLemonSqueezyEvent()` diesmal die Session findet und `CheckoutAssignmentService::finalise()` ausführt.
3. Nochmal `checkout_sessions` und `tenant_packages` prüfen:
- Session sollte auf `completed` stehen, Paket aktiv sein.
### 5.2 Manuelle Korrektur (Notfall)
Nur anwenden, wenn klare Freigabe vorliegt und Paddle die Zahlung eindeutig als erfolgreich listet.
Nur anwenden, wenn klare Freigabe vorliegt und Lemon Squeezy die Zahlung eindeutig als erfolgreich listet.
1. `tenant_packages` aktualisieren:
- Entweder neuen Eintrag anlegen oder bestehenden für `(tenant_id, package_id)` so setzen, dass:
- `active = 1`,
- `purchased_at` und `expires_at` zu PaddleDaten passen.
- `active = 1`,
- `purchased_at` und `expires_at` zu Lemon SqueezyDaten passen.
2. `package_purchases` ergänzen:
- Sicherstellen, dass die Zahlung als Zeile mit `provider = 'paddle'`, `provider_id = TransactionID` und passender `price` existiert (für spätere Audits).
- Sicherstellen, dass die Zahlung als Zeile mit `provider = 'lemonsqueezy'`, `provider_id = OrderID` und passender `price` existiert (für spätere Audits).
3. Konsistenz prüfen:
- Admin UI für Tenant öffnen und prüfen, ob Limits/Paketstatus jetzt korrekt angezeigt werden.
4. Dokumentation:
@@ -88,7 +88,7 @@ Nur anwenden, wenn klare Freigabe vorliegt und Paddle die Zahlung eindeutig als
- Sobald der BackendStatus korrigiert ist:
- Kurz bestätigen, dass das Paket aktiv ist und welche Auswirkungen das hat (z.B. neue Limits, verlängerte Galerie).
- Falls Paddle die Zahlung nicht als erfolgreich führt:
- Falls Lemon Squeezy die Zahlung nicht als erfolgreich führt:
- Ehrlich kommunizieren, dass laut Zahlungsprovider noch keine endgültige Zahlung vorliegt und welche Optionen es gibt (z.B. neue Zahlung, Klärung mit Bank/Kreditkarte).
Dieses Howto sollte dem Support/OnCall helfen, den gängigsten BillingFehlerfall strukturiert abzuarbeiten. Für tiefere Ursachenanalysen siehe `docs/ops/billing-ops.md`.