switched to paddle inline checkout, removed paypal and most of stripe. added product sync between app and paddle.
This commit is contained in:
@@ -21,10 +21,10 @@ Das bestehende Modell ist Credits-basiert (Freemium mit 1 Free-Credit, One-off-K
|
||||
- **API:** Endpunkte `/api/v1/tenant/credits/balance`, `/credits/ledger`, `/credits/purchase`, `/credits/sync`, `/purchases/intent`.
|
||||
- **Frontend (Admin PWA):** Dashboard-Cards für Balance, Kauf-Integration (RevenueCat).
|
||||
- **Guest PWA:** Keine direkten Checks (Backend-handhabt).
|
||||
- **Billing:** Stripe (Checkout/Webhooks), RevenueCat (IAP), PayPalWebhookController (teilweise).
|
||||
- **Billing:** Stripe (Checkout/Webhooks), RevenueCat (IAP), PaddleWebhookController (teilweise).
|
||||
- **Tests:** `RevenueCatWebhookTest`, Credit-Unit-Tests.
|
||||
- **Docs:** PRP 08-billing.md (Credits-MVP), 14-freemium-business-model.md (IAP-Struktur), API-Specs (credits-Endpunkte).
|
||||
- **Lücken im Aktuellen:** Keine Package-Limits (nur Balance), Subscriptions nicht live, PayPal untergenutzt.
|
||||
- **Lücken im Aktuellen:** Keine Package-Limits (nur Balance), Subscriptions nicht live, Paddle untergenutzt.
|
||||
|
||||
**Auswirkungen:** Vollständige Ersetzung, um Flexibilität (Limits/Features pro Package) zu ermöglichen.
|
||||
|
||||
@@ -99,7 +99,7 @@ Packages ersetzen Credits: Vordefinierte Bündel mit Limits/Features. Kauf bei E
|
||||
$table->foreignId('tenant_id')->nullable()->constrained();
|
||||
$table->foreignId('event_id')->nullable()->constrained();
|
||||
$table->foreignId('package_id')->constrained();
|
||||
$table->string('provider_id'); // Stripe/PayPal ID
|
||||
$table->string('provider_id'); // Paddle ID
|
||||
$table->decimal('price', 8, 2);
|
||||
$table->enum('type', ['endcustomer_event', 'reseller_subscription']);
|
||||
$table->json('metadata'); // {event_id, ip_address}
|
||||
@@ -129,13 +129,13 @@ Packages ersetzen Credits: Vordefinierte Bündel mit Limits/Features. Kauf bei E
|
||||
|
||||
- **TenantPackageResource (SuperAdmin/TenantAdmin):**
|
||||
- Form: Select('tenant_id'), Select('package_id'), DateTimePicker('purchased_at'), DateTimePicker('expires_at'), TextInput('used_events', readOnly), Toggle('active').
|
||||
- Table: TextColumn('tenant.name'), BadgeColumn('package.name'), DateColumn('expires_at', color: expired → danger), ProgressColumn('used_events' / max_events), Actions (Renew: set expires_at +1 year, Cancel: active=false + Stripe/PayPal cancel).
|
||||
- Table: TextColumn('tenant.name'), BadgeColumn('package.name'), DateColumn('expires_at', color: expired → danger), ProgressColumn('used_events' / max_events), Actions (Renew: set expires_at +1 year, Cancel: active=false + Paddle cancel).
|
||||
- Relations: BelongsTo Tenant/Package, HasMany Events (RelationManager mit Event-List).
|
||||
- Bulk-Actions: Renew Selected.
|
||||
|
||||
- **PurchaseResource (SuperAdmin/TenantAdmin):**
|
||||
- Form: Select('tenant_id/event_id'), Select('package_id'), TextInput('provider_id'), MoneyInput('price'), Select('type'), JSONEditor('metadata'), Toggle('refunded').
|
||||
- Table: BadgeColumn('type'), LinkColumn('tenant' or 'event'), TextColumn('package.name/price'), DateColumn('purchased_at'), BadgeColumn('status' – paid/refunded), Actions (View, Refund: Call Stripe/PayPal API, decrement counters, log).
|
||||
- Table: BadgeColumn('type'), LinkColumn('tenant' or 'event'), TextColumn('package.name/price'), DateColumn('purchased_at'), BadgeColumn('status' – paid/refunded), Actions (View, Refund: Call Paddle API, decrement counters, log).
|
||||
- Filters: SelectFilter('type'), DateRangeFilter('purchased_at'), TenantFilter.
|
||||
- Widgets: StatsOverview (Total Revenue, Monthly Purchases, Top Package), ChartWidget (Revenue over Time via Laravel Charts).
|
||||
- Export: CSV (für Buchhaltung: tenant, package, price, date).
|
||||
@@ -145,14 +145,14 @@ Packages ersetzen Credits: Vordefinierte Bündel mit Limits/Features. Kauf bei E
|
||||
## 5. Marketing- und Legal-Anpassungen (Todo 4)
|
||||
- **Webfrontend (Blade, resources/views/marketing/):**
|
||||
- **packages.blade.php (neu, Route /packages):** Hero ("Entdecken Sie unsere Packages"), Tabs (Endkunden/Reseller), Tabelle/Accordion mit Details (Preis, Limits als Icons, Features-Bullets, i18n-Übersetzungen). CTA: "Kaufen" → /checkout/{id}. Dynamisch: @foreach(Package::where('type', 'endcustomer')->get() as $package).
|
||||
- **checkout.blade.php (neu, Route /checkout/{package_id}):** Summary-Box (Package-Details), Form (Name, E-Mail, Adresse für Reseller), Zahlungsoptionen (Radio: Stripe/PayPal), Stripe-Element/PayPal-Button. Submit: POST /purchases/intent → Redirect. Tailwind: Secure-Design mit Badges.
|
||||
- **checkout.blade.php (neu, Route /checkout/{package_id}):** Summary-Box (Package-Details), Form (Name, E-Mail, Adresse für Reseller), Zahlungsoptionen (Radio: Paddle), Stripe-Element/Paddle-Button. Submit: POST /purchases/intent → Redirect. Tailwind: Secure-Design mit Badges.
|
||||
- **success.blade.php:** "Vielen Dank! Package {name} gekauft." Details (Limits, Event-Link), Upsell ("Upgrade zu Reseller?"), Rechnung-Download (PDF via Dompdf), Onboarding-Tour-Link.
|
||||
- **marketing.blade.php:** Teaser-Section mit Package-Icons/Preisen, Link zu /packages.
|
||||
- **occasions.blade.php/blog*.blade.php:** Kontextuelle Erwähnungen (z.B. "Ideal für Partys: Starter-Paket"), Blog-Post "Neues Package-Modell" mit FAQ.
|
||||
|
||||
- **Legal (resources/views/legal/):**
|
||||
- **datenschutz.blade.php:** Abschnitt "Zahlungen" (Stripe/PayPal: Keine Karten-Speicherung, GDPR: Löschung nach 10 Jahren; Consent für E-Mails). "Package-Daten (Limits) sind anonymisiert."
|
||||
- **impressum.blade.php:** "Monetarisierung: Packages via Stripe/PayPal; USt-ID: ...; Support: support@fotospiel.de".
|
||||
- **datenschutz.blade.php:** Abschnitt "Zahlungen" (Paddle: Keine Karten-Speicherung, GDPR: Löschung nach 10 Jahren; Consent für E-Mails). "Package-Daten (Limits) sind anonymisiert."
|
||||
- **impressum.blade.php:** "Monetarisierung: Packages via Paddle; USt-ID: ...; Support: support@fotospiel.de".
|
||||
- **Allgemein:** Datum "Aktualisiert: 2025-09-26 – Package-Modell"; Links zu Provider-Datenschutz.
|
||||
|
||||
**i18n:** Translations in lang/de/en (z.B. 'package.starter' → 'Starter-Paket').
|
||||
@@ -160,7 +160,7 @@ Packages ersetzen Credits: Vordefinierte Bündel mit Limits/Features. Kauf bei E
|
||||
## 6. Backend-Logik & API (Todo 6/7)
|
||||
- **Controllers:**
|
||||
- `PackagesController` (index: Liste mit Cache, show: Details, store: Intent für Kauf).
|
||||
- `PurchasesController` (intent: Erstelle Stripe-Session oder PayPal-Order basierend auf method; store: Nach Webhook).
|
||||
- `PurchasesController` (intent: Erstelle Stripe-Session oder Paddle-Order basierend auf method; store: Nach Webhook).
|
||||
- **Middleware:** `PackageMiddleware` (für Events: Check event_packages.used_photos < max_photos; für Tenant: used_events < max_events_per_year).
|
||||
- **Models:** `Package` (Relationships: hasMany EventPackage/TenantPackage), `EventPackage` (incrementUsedPhotos-Method), `TenantPackage` (isActive-Scope, Observer für Expiry: E-Mail + active=false).
|
||||
- **API-Endpunkte (routes/api.php, tenant-group):**
|
||||
@@ -175,7 +175,7 @@ Packages ersetzen Credits: Vordefinierte Bündel mit Limits/Features. Kauf bei E
|
||||
|
||||
## 7. Frontend-Anpassungen (Todo 8/9)
|
||||
- **Admin PWA (resources/js/admin/):**
|
||||
- EventFormPage.tsx: Select('package_id') mit Details-Modal (Limits/Preis), Button 'Kaufen' → Stripe/PayPal-Integration (stripe.elements oder PayPal-Button).
|
||||
- EventFormPage.tsx: Select('package_id') mit Details-Modal (Limits/Preis), Button 'Kaufen' → Paddle-Integration (stripe.elements oder Paddle-Button).
|
||||
- Dashboard: Card 'Aktuelles Package' (Limits, Expiry, Upgrade-Button).
|
||||
- SettingsPage.tsx: Reseller-Übersicht (used_events/Progress, Renew-Button).
|
||||
- Hooks: usePackageLimits (fetch /packages, check used_photos).
|
||||
@@ -185,21 +185,21 @@ Packages ersetzen Credits: Vordefinierte Bündel mit Limits/Features. Kauf bei E
|
||||
- Features: Watermark-Overlay if watermark_allowed; Branding-Logo if branding_allowed.
|
||||
- Router: Guard für Limits (z.B. /upload → Check API).
|
||||
|
||||
**Tech:** React Query für API-Calls, Stripe.js/PayPal-SDK in Components, i18n mit react-i18next.
|
||||
**Tech:** React Query für API-Calls, Stripe.js/Paddle-SDK in Components, i18n mit react-i18next.
|
||||
|
||||
## 8. Billing-Integration (Todo 10)
|
||||
- **Provider:** Stripe (Primär: Einmalkäufe/Subscriptions) + PayPal (Alternative: PHP SDK für Orders/Subscriptions).
|
||||
- **Flow:** Auswahl → Intent (Controller: if 'stripe' → Stripe::checkout()->sessions->create([...]); if 'paypal' → PayPal::orders()->create([...]) ) → Redirect → Webhook (verifiziert, insert package_purchases, assign Package, E-Mail).
|
||||
- **Webhooks:** StripeWebhookController (neue Events: checkout.session.completed → ProcessPurchase), PayPalWebhookController (erweitert: PAYMENT.CAPTURE.COMPLETED → ProcessPurchase).
|
||||
- **Provider:** Stripe (Primär: Einmalkäufe/Subscriptions) + Paddle (Alternative: PHP SDK für Orders/Subscriptions).
|
||||
- **Flow:** Auswahl → Intent (Controller: if 'stripe' → Stripe::checkout()->sessions->create([...]); if 'paypal' → Paddle::orders()->create([...]) ) → Redirect → Webhook (verifiziert, insert package_purchases, assign Package, E-Mail).
|
||||
- **Webhooks:** StripeWebhookController (neue Events: checkout.session.completed → ProcessPurchase), PaddleWebhookController (erweitert: PAYMENT.CAPTURE.COMPLETED → ProcessPurchase).
|
||||
- **SDKs:** composer require stripe/stripe-php ^10.0, paypal/rest-api-sdk-php ^1.14; NPM: @stripe/stripe-js, @paypal/react-paypal-js.
|
||||
- **Free:** Kein Provider – direkt assign via API.
|
||||
- **Refunds:** Action in PurchaseResource: Call Stripe::refunds->create oder PayPal::refunds, decrement Counters.
|
||||
- **Refunds:** Action in PurchaseResource: Call Stripe::refunds->create oder Paddle::refunds, decrement Counters.
|
||||
- **Env:** STRIPE_KEY/SECRET, PAYPAL_CLIENT_ID/SECRET, SANDBOX-Flags.
|
||||
|
||||
## 9. Tests (Todo 11)
|
||||
- **Unit/Feature:** Pest/PHPUnit: Test PackageSeeder, Migration (assert Tables exist), Controllers (mock Stripe/PayPal SDKs mit Stripe::mock(), test Intent/Webhook), Models (Package::find(1)->limits, TenantPackage::isActive), Middleware (assert denies if limit exceeded).
|
||||
- **E2E (Playwright):** Test Kauf-Flow (navigate /packages, select Starter, choose PayPal, complete sandbox, assert success.blade.php), Limits (upload photo, assert counter +1, deny at max).
|
||||
- **Anpassungen:** RevenueCatWebhookTest → Stripe/PayPalWebhookTest; Add PackageValidationTest (e.g. EventCreate without Package → 422).
|
||||
- **Unit/Feature:** Pest/PHPUnit: Test PackageSeeder, Migration (assert Tables exist), Controllers (mock Paddle SDKs mit Stripe::mock(), test Intent/Webhook), Models (Package::find(1)->limits, TenantPackage::isActive), Middleware (assert denies if limit exceeded).
|
||||
- **E2E (Playwright):** Test Kauf-Flow (navigate /packages, select Starter, choose Paddle, complete sandbox, assert success.blade.php), Limits (upload photo, assert counter +1, deny at max).
|
||||
- **Anpassungen:** RevenueCatWebhookTest → PaddleWebhookTest; Add PackageValidationTest (e.g. EventCreate without Package → 422).
|
||||
- **Coverage:** 80% für Billing/DB; Mock Providers für Isolation.
|
||||
|
||||
## 10. Deployment & Rollout (Todo 12)
|
||||
@@ -222,8 +222,8 @@ Packages ersetzen Credits: Vordefinierte Bündel mit Limits/Features. Kauf bei E
|
||||
- **Support:** E-Mail-Templates (PurchaseMailable), FAQ in /support/packages, Onboarding-Tour post-Kauf.
|
||||
- **Performance:** Caching (Packages-Liste), Indexing (purchased_at), Queues für Webhooks (ProcessPurchaseJob).
|
||||
- **Edge-Cases:** Upgrade (prorate Preis, transfer Limits), Expiry (Observer + E-Mail), Offline-PWA (queued Käufe sync).
|
||||
- **Dependencies:** Stripe/PayPal SDKs, Dompdf (Rechnungen), Laravel Cashier (optional für Stripe).
|
||||
- **Kosten:** Env für Sandbox/Prod-Keys; Test mit Stripe/PayPal Test-Accounts.
|
||||
- **Dependencies:** Paddle SDKs, Dompdf (Rechnungen), Laravel Cashier (optional für Stripe).
|
||||
- **Kosten:** Env für Sandbox/Prod-Keys; Test mit Paddle Test-Accounts.
|
||||
|
||||
## 12. Todo-List (Status: Alle Planung completed)
|
||||
- [x] Analyse.
|
||||
|
||||
Reference in New Issue
Block a user