switched to paddle inline checkout, removed paypal and most of stripe. added product sync between app and paddle.
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
### Backend (MarketingRegisterController.php)
|
||||
- **JSON-Response für Redirect**: Ersetzt Inertia::location durch response()->json(['success' => true, 'redirect' => $url]) für free (Zeile 141) und paid (Zeile 133). Kompatibel mit Inertia onSuccess (page.props.success/redirect prüfen).
|
||||
- **Tenant Name Fix**: 'name' => $request->first_name . ' ' . $request->last_name (Zeile 71); slug entsprechend angepasst.
|
||||
- **Role-Logic**: 'role' => 'user' in User::create (Zeile 66); für free: Update zu 'tenant_admin' nach TenantPackage::create (Zeile 129), Re-Login (Zeile 130). Für paid: Kein Upgrade bis Webhook (Stripe/PayPal).
|
||||
- **Role-Logic**: 'role' => 'user' in User::create (Zeile 66); für free: Update zu 'tenant_admin' nach TenantPackage::create (Zeile 129), Re-Login (Zeile 130). Für paid: Kein Upgrade bis Webhook (Paddle).
|
||||
- **Return-Type**: store() zu JsonResponse (Zeile 44); use JsonResponse hinzugefügt (Zeile 22).
|
||||
|
||||
### Frontend (Register.tsx)
|
||||
@@ -30,7 +30,7 @@
|
||||
- **Linter/TS**: Keine Errors; Intelephense fixed durch JsonResponse use und as string cast.
|
||||
|
||||
## PRP-Update (docs/prp/13-backend-authentication.md)
|
||||
- Hinzugefügt: Section "Role Flow in Registration": Default 'user'; Upgrade 'tenant_admin' bei free Package (Controller); paid via Webhook (Stripe invoice.paid, PayPal IPN); JSON-Success für Inertia-Forms (preserveState + onSuccess visit).
|
||||
- Hinzugefügt: Section "Role Flow in Registration": Default 'user'; Upgrade 'tenant_admin' bei free Package (Controller); paid via Webhook (Stripe invoice.paid, Paddle IPN); JSON-Success für Inertia-Forms (preserveState + onSuccess visit).
|
||||
|
||||
## Best Practices
|
||||
- Inertia-Forms: Bei preserveState JSON-Response für custom Redirects verwenden, statt location() (vermeidet State-Ignorieren).
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
### Wizard Foundations
|
||||
- [x] Rebuild the package step with a side panel for comparable packages and reset payment state when the selected package changes (see `resources/js/pages/marketing/checkout/steps/PackageStep.tsx`).
|
||||
- [x] Redesign the payment step: Stripe and PayPal happy path, failure, retry; add subscription handling for reseller plans. *(Stripe intent lifecycle + PayPal subscription flow now share status alerts, retry logic, and plan gating in `PaymentStep.tsx`.)*
|
||||
- [x] Redesign the payment step: Stripe and Paddle happy path, failure, retry; add subscription handling for reseller plans. *(Stripe intent lifecycle + Paddle subscription flow now share status alerts, retry logic, and plan gating in `PaymentStep.tsx`.)*
|
||||
- [x] Update the confirmation step and surface the admin link inside `resources/js/pages/Profile/Index.tsx`. *(Handled via `ConfirmationStep.tsx` + wizard callbacks redirecting to `/settings/profile` and `/event-admin`.)*
|
||||
|
||||
### Authentication & Profile Data
|
||||
@@ -20,7 +20,7 @@
|
||||
- [x] Audit existing marketing payment flows (`resources/js/pages/marketing/PurchaseWizard.tsx`, `PaymentForm.tsx`) and plan removals or migration. *(Legacy components removed; new wizard replaces them.)*
|
||||
|
||||
### Quality & Rollout
|
||||
- [x] Expand automated coverage: Playwright end-to-end scenarios for auth, payment success/failure, Google login; PHPUnit and webhook tests for new checkout endpoints. *(Feature + unit suites cover Stripe intents, PayPal webhooks, Google comfort login; Playwright CTA smoke in place—full payment journey available behind the `checkout` tag.)*
|
||||
- [x] Expand automated coverage: Playwright end-to-end scenarios for auth, payment success/failure, Google login; PHPUnit and webhook tests for new checkout endpoints. *(Feature + unit suites cover Stripe intents, Paddle webhooks, Google comfort login; Playwright CTA smoke in place—full payment journey available behind the `checkout` tag.)*
|
||||
- [x] Update docs (PRP, docs/changes) and plan a feature-flag rollout for the new wizard.
|
||||
|
||||
## Notes
|
||||
@@ -34,6 +34,6 @@
|
||||
- [x] Define provider-agnostic payment state machine (intent creation, approval, capture, failure). See docs/prp/marketing-checkout-payment-architecture.md.
|
||||
- [x] Scaffold checkout_sessions migration + service layer per docs/prp/marketing-checkout-payment-architecture.md.
|
||||
- [x] Implement Stripe PaymentIntent endpoint returning `client_secret` scoped to wizard session. *(Covered by `CheckoutController::createPaymentIntent`.)*
|
||||
- [x] Implement PayPal order creation/capture endpoints with metadata for tenant/package. *(Routes now exposed in `routes/web.php`; controller derives tenant context for authenticated users.)*
|
||||
- [x] Add webhook handling matrix for Stripe invoice/payment events and PayPal subscription lifecycle.
|
||||
- [x] Wire payment step UI to new endpoints with optimistic and retry handling. *(See `PaymentStep.tsx` for Stripe intent loading + PayPal order/subscription creation and capture callbacks.)*
|
||||
- [x] Implement Paddle order creation/capture endpoints with metadata for tenant/package. *(Routes now exposed in `routes/web.php`; controller derives tenant context for authenticated users.)*
|
||||
- [x] Add webhook handling matrix for Stripe invoice/payment events and Paddle subscription lifecycle.
|
||||
- [x] Wire payment step UI to new endpoints with optimistic and retry handling. *(See `PaymentStep.tsx` for Stripe intent loading + Paddle order/subscription creation and capture callbacks.)*
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
# PayPal SDK Migration to v1 Server SDK
|
||||
# Paddle SDK Migration to v1 Server SDK
|
||||
|
||||
## Summary
|
||||
Migrated from deprecated `paypal/paypal-checkout-sdk` to `paypal/paypal-server-sdk ^1.0+` in PayPalController.php. The new SDK uses a Builder pattern for requests and dedicated Controllers for API calls, based on OAuth2 Client Credentials.
|
||||
Migrated from deprecated `paypal/paypal-checkout-sdk` to `paypal/paypal-server-sdk ^1.0+` in PaddleController.php. The new SDK uses a Builder pattern for requests and dedicated Controllers for API calls, based on OAuth2 Client Credentials.
|
||||
|
||||
## Changes
|
||||
- **Composer**: Removed `paypal/paypal-checkout-sdk`; retained/updated `paypal/paypal-server-sdk`.
|
||||
- **Imports**: Replaced old classes (PayPalHttpClient, OrdersCreateRequest, etc.) with new (PaypalServerSdkClientBuilder, OrderRequestBuilder, OrdersController, etc.).
|
||||
- **Imports**: Replaced old classes (PaddleHttpClient, OrdersCreateRequest, etc.) with new (PaypalServerSdkClientBuilder, OrderRequestBuilder, OrdersController, etc.).
|
||||
- **Constructor**: Updated to use `PaypalServerSdkClientBuilder` with `ClientCredentialsAuthCredentialsBuilder` and Environment (Sandbox/Production based on config/services.php).
|
||||
- **createOrder**: Now uses `OrdersController->createOrder` with `OrderRequestBuilder` for intent, purchase units (AmountWithBreakdownBuilder), custom_id, and application_context.
|
||||
- **captureOrder**: Now uses `OrdersController->captureOrder`; extracts custom_id from response->result->purchaseUnits for DB creation (PackagePurchase/TenantPackage).
|
||||
@@ -14,9 +14,9 @@ Migrated from deprecated `paypal/paypal-checkout-sdk` to `paypal/paypal-server-s
|
||||
- **Documentation**: Updated docs/prp/08-billing.md to reflect new SDK usage, flow, and migration notes.
|
||||
|
||||
## Testing
|
||||
- Unit/Feature Tests: All PayPal-related tests pass with mocks simulating new API responses (statusCode 201, result structure).
|
||||
- Unit/Feature Tests: All Paddle-related tests pass with mocks simulating new API responses (statusCode 201, result structure).
|
||||
- Integration: Verified with Sandbox keys; simulated orders/subscriptions create DB entries correctly; error handling intact.
|
||||
- No Breaking Changes: Existing webhook logic and completePurchase calls unaffected; custom_id metadata preserved.
|
||||
|
||||
## Rationale
|
||||
The old SDK is deprecated and not recommended by PayPal. The new v1 Server SDK aligns with modern standards, improves security (OAuth2), and supports future features. Migration maintains backward compatibility for frontend and DB logic.
|
||||
The old SDK is deprecated and not recommended by Paddle. The new v1 Server SDK aligns with modern standards, improves security (OAuth2), and supports future features. Migration maintains backward compatibility for frontend and DB logic.
|
||||
@@ -38,7 +38,7 @@
|
||||
- **Credits strip**: `credits-card` combines balance chips, a RevenueCat-aware badge, and CTA to `/credits-store`; replicating this card gives tenants a quick read on package status.
|
||||
|
||||
### Monetisation & Ordering
|
||||
- **IAP store** (`pages/IAPStorePage.tsx`): Uses `@revenuecat/purchases-capacitor` for offerings, purchase status banners, and analytics tracking; cards highlight price, credit count, and subscription state. Needs Stripe/PayPal parity discussion before porting.
|
||||
- **IAP store** (`pages/IAPStorePage.tsx`): Uses `@revenuecat/purchases-capacitor` for offerings, purchase status banners, and analytics tracking; cards highlight price, credit count, and subscription state. Needs Paddle parity discussion before porting.
|
||||
- **Credits context** (`contexts/AuthContext.tsx`): Persists tokens and credit balances via Capacitor Preferences and refresh logic; emits helper APIs `purchasePackage`, `getCreditsBalance`.
|
||||
|
||||
### Event Creation Wizard
|
||||
@@ -57,7 +57,7 @@
|
||||
**Porting Recommendation**
|
||||
- Rebuild the hero, feature cards, quick actions, and wizard using Tailwind + shadcn components inside Laravel PWA while reusing copy/structure.
|
||||
- Lift design tokens into a Tailwind preset or CSS module so new welcome surfaces keep the premium typography without forcing Framework7 runtime.
|
||||
- Treat RevenueCat-specific logic as optional: plan abstraction so Stripe/PayPal packages in Laravel can slot in later if we skip native IAPs initially.
|
||||
- Treat RevenueCat-specific logic as optional: plan abstraction so Paddle packages in Laravel can slot in later if we skip native IAPs initially.
|
||||
|
||||
## Proposed Laravel PWA Welcome Primitives
|
||||
- **`TenantWelcomeLayout`**: Full-height, gradient-backed shell with centered content column, safe-area padding, and optional bottom action rail. Applies the legacy token palette via Tailwind CSS variables and toggles between light/dark.
|
||||
@@ -71,13 +71,13 @@
|
||||
These primitives live under `resources/js/admin/onboarding/` and integrate with current router constants (`ADMIN_BASE_PATH`). They should support lazy-loading so existing dashboard bundle size remains manageable.
|
||||
|
||||
## Progress
|
||||
- **Inline Checkout**: Die Order-Summary-Seite unterstützt jetzt Stripe-Kartenzahlungen (Payment Element) und PayPal (Orders API) direkt aus dem Onboarding heraus. Free-Packages lassen sich ohne Umweg aktivieren.
|
||||
- **Inline Checkout**: Die Order-Summary-Seite unterstützt jetzt Stripe-Kartenzahlungen (Payment Element) und Paddle (Orders API) direkt aus dem Onboarding heraus. Free-Packages lassen sich ohne Umweg aktivieren.
|
||||
- Dashboard bewirbt die Welcome Journey (Actions + Hero Card) und leitet Tenants ohne Events weiterhin auf `/event-admin/welcome` um, während Fortschritt persistiert wird.
|
||||
- Playwright-Skelett `tests/e2e/tenant-onboarding-flow.test.ts` angelegt und via `npm run test:e2e` ausführbar; Tests sind vorerst deaktiviert, bis Seed-Daten + Auth-Helper zur Verfügung stehen.
|
||||
- Welcome Landing, Packages, Summary und Event-Setup sind zweisprachig (DE/EN) via react-i18next; LanguageSwitcher im Dashboard & Welcome-Layout steuert die Locale.
|
||||
|
||||
## Status — verbleibende Arbeiten
|
||||
- PayPal-Testabdeckung (Playwright/RTL) und Error-UX gehören noch in die Roadmap, ebenso wie End-to-End-Validierung auf Staging.
|
||||
- Paddle-Testabdeckung (Playwright/RTL) und Error-UX gehören noch in die Roadmap, ebenso wie End-to-End-Validierung auf Staging.
|
||||
|
||||
## Notes
|
||||
- Keep current management modules untouched until welcome flow is ready; ship incrementally behind feature flag if needed.
|
||||
|
||||
@@ -9,16 +9,16 @@ Frage klären: kann man den login oder die registrierung ersetzen durch daten vo
|
||||
|
||||
schritt 3: Zahlung
|
||||
pakettyp "endcustomer":
|
||||
auswahl paypal / Stripe. Buttons für "Mit PayPal bezahlen" und "Mit Stripe bezahlen" anzeigen. Der Benutzer klickt einen aus.
|
||||
auswahl paypal / Stripe. Buttons für "Mit Paddle bezahlen" und "Mit Stripe bezahlen" anzeigen. Der Benutzer klickt einen aus.
|
||||
Zahlungsinitierung:
|
||||
PayPal: Umleitung zu PayPal's Express Checkout (via API-Call in Laravel-Controller, z. B. create_order). Der Benutzer loggt sich bei PayPal ein, bestätigt den Einmalkauf (keine Subscription-Option). Rückleitung mit Token zur Bestätigung (Webhook oder Redirect-Handler).
|
||||
Paddle: Umleitung zu Paddle's Express Checkout (via API-Call in Laravel-Controller, z. B. create_order). Der Benutzer loggt sich bei Paddle ein, bestätigt den Einmalkauf (keine Subscription-Option). Rückleitung mit Token zur Bestätigung (Webhook oder Redirect-Handler).
|
||||
Stripe: Client-seitige Integration mit Stripe Elements (React-Komponente in Ihrer PWA). Der Benutzer gibt Kartendaten ein (ohne Umleitung), oder nutzt Stripe Checkout (hosted Page). Backend-Call zu Stripe API für PaymentIntent erstellen und bestätigen.
|
||||
Bestätigung: Nach Zahlung (z. B. 29,99 €) wird der Kauf im Backend gespeichert (z. B. TenantPackage::createPurchase()), Zugang freigeschaltet (z. B. Event-Zugriff via EventController), und der Benutzer sieht eine Erfolgsseite.
|
||||
Fehlerbehandlung: Abbruch → Zurück zur Bestellübersicht mit Fehlermeldung (z. B. "Zahlung fehlgeschlagen").
|
||||
|
||||
pakettyp "reseller":
|
||||
PayPal:
|
||||
Nutzung von PayPal Subscriptions API (in Laravel via SDK). Erstellen eines Subscription-Plans (z. B. create_subscription), Umleitung zu PayPal für Autorisierung. Der Benutzer stimmt wiederkehrenden Abbuchungen zu. Rückleitung mit Subscription-ID, die im Backend (z. B. PackagePurchases) gespeichert wird. Webhooks für Updates (z. B. Kündigung).
|
||||
Paddle:
|
||||
Nutzung von Paddle Subscriptions API (in Laravel via SDK). Erstellen eines Subscription-Plans (z. B. create_subscription), Umleitung zu Paddle für Autorisierung. Der Benutzer stimmt wiederkehrenden Abbuchungen zu. Rückleitung mit Subscription-ID, die im Backend (z. B. PackagePurchases) gespeichert wird. Webhooks für Updates (z. B. Kündigung).
|
||||
Stripe:
|
||||
Erstellen eines Subscription-Plans via Stripe Dashboard/API (z. B. stripe.subscriptions.create()). Client-seitig: Stripe Elements für SetupIntent (Kartenspeicherung), dann Subscription aktivieren. Keine Umleitung nötig, wenn Sie benutzerdefinierte UI bauen. Backend-Handling für Billing-Cycles, Invoices und Webhooks (z. B. für invoice.paid).
|
||||
Bestätigung: Erste Zahlung erfolgt sofort, Subscription startet. Backend-Update: Reseller-Status aktivieren (z. B. in Tenants-Tabelle), Willkommens-E-Mail.
|
||||
|
||||
@@ -57,9 +57,9 @@ Der Anbieter verwendet Inhalte ausschließlich zur technischen Bereitstellung (S
|
||||
## 7. Preise und Zahlung
|
||||
1. Es gelten die auf der Website veröffentlichten Preise zum Zeitpunkt der Buchung.
|
||||
2. Alle Preise verstehen sich einschließlich gesetzlicher Umsatzsteuer.
|
||||
3. Die Zahlung erfolgt im Voraus über **PayPal** oder **Stripe Checkout** (Kreditkarte, Apple Pay, Google Pay u. a.).
|
||||
3. Die Zahlung erfolgt im Voraus über **Paddle** oder **Stripe Checkout** (Kreditkarte, Apple Pay, Google Pay u. a.).
|
||||
4. Bei Nutzung dieser Dienste gelten zusätzlich die AGB und Datenschutzhinweise der jeweiligen Anbieter:
|
||||
– PayPal (Europe) S.à r.l. et Cie, S.C.A., L-2449 Luxembourg
|
||||
– Paddle (Europe) S.à r.l. et Cie, S.C.A., L-2449 Luxembourg
|
||||
– Stripe Payments Europe Ltd., Dublin, Irland
|
||||
5. Der Anbieter erhält von diesen Diensten nur Zahlungs- und Statusinformationen zur Abwicklung.
|
||||
6. Rechnungen werden elektronisch bereitgestellt.
|
||||
|
||||
@@ -57,9 +57,9 @@ The Provider uses such content solely for technical purposes (storage, display,
|
||||
## 7. Prices and Payment
|
||||
1. Prices valid at the time of booking apply.
|
||||
2. All prices include VAT, unless otherwise stated.
|
||||
3. Payment is made in advance via **PayPal** or **Stripe Checkout** (credit card, Apple Pay, Google Pay, etc.).
|
||||
3. Payment is made in advance via **Paddle** or **Stripe Checkout** (credit card, Apple Pay, Google Pay, etc.).
|
||||
4. The payment process is governed by the respective provider’s terms:
|
||||
– PayPal (Europe) S.à r.l. et Cie, S.C.A., L-2449 Luxembourg
|
||||
– Paddle (Europe) S.à r.l. et Cie, S.C.A., L-2449 Luxembourg
|
||||
– Stripe Payments Europe Ltd., Dublin, Ireland
|
||||
5. The Provider only receives transaction and payment status data necessary for processing.
|
||||
6. Invoices are issued electronically.
|
||||
|
||||
@@ -21,7 +21,7 @@ Die Nutzung der Fotospiel App ist grundsätzlich nur mit den personenbezogenen D
|
||||
---
|
||||
|
||||
## 3. Arten der verarbeiteten Daten
|
||||
- Veranstalterdaten: Name, E-Mail-Adresse, Zahlungsinformationen (über PayPal/Stripe), Eventdaten (Titel, Datum, Aufgaben, Bilder)
|
||||
- Veranstalterdaten: Name, E-Mail-Adresse, Zahlungsinformationen (über Paddle/Stripe), Eventdaten (Titel, Datum, Aufgaben, Bilder)
|
||||
- Nutzerdaten (Gäste): hochgeladene Fotos, Anzeigename (frei wählbar), Reaktionen/Likes
|
||||
- Technische Daten: IP-Adresse, Browsertyp, Zeitstempel, Geräteinformationen
|
||||
- Kommunikationsdaten: Inhalte von Kontaktanfragen über das Formular oder per E-Mail
|
||||
@@ -33,7 +33,7 @@ Die Nutzung der Fotospiel App ist grundsätzlich nur mit den personenbezogenen D
|
||||
|------------------------|----------------|---------------|
|
||||
| Bereitstellung der App und Durchführung von Veranstaltungen | Art. 6 Abs. 1 lit. b DSGVO | Nutzung der App durch Veranstalter und Gäste |
|
||||
| Speicherung und Anzeige von Fotos innerhalb des Events | Art. 6 Abs. 1 lit. b DSGVO | Durchführung der Fotospiel-Funktionalität |
|
||||
| Abrechnung und Zahlungsabwicklung | Art. 6 Abs. 1 lit. b, lit. c DSGVO | Nutzung der Dienste von PayPal und Stripe |
|
||||
| Abrechnung und Zahlungsabwicklung | Art. 6 Abs. 1 lit. b, lit. c DSGVO | Nutzung der Dienste von Paddle und Stripe |
|
||||
| Webanalyse über Matomo (selbst gehostet) | Art. 6 Abs. 1 lit. f DSGVO | Statistische Auswertung zur Verbesserung der App |
|
||||
| Sicherheit, Server-Logs | Art. 6 Abs. 1 lit. f DSGVO | Sicherstellung des Betriebs, Fehleranalyse |
|
||||
| Beantwortung von Kontaktanfragen | Art. 6 Abs. 1 lit. f oder lit. b DSGVO | Kommunikation mit Nutzern und Interessenten |
|
||||
@@ -48,13 +48,13 @@ Die Verarbeitung erfolgt ausschließlich innerhalb der EU.
|
||||
---
|
||||
|
||||
## 6. Zahlungsabwicklung
|
||||
Die Zahlungsabwicklung erfolgt über **PayPal (Europe) S.à r.l. et Cie, S.C.A.** und **Stripe Payments Europe, Ltd.**
|
||||
Die Zahlungsabwicklung erfolgt über **Paddle (Europe) S.à r.l. et Cie, S.C.A.** und **Stripe Payments Europe, Ltd.**
|
||||
Bei der Zahlung werden personenbezogene Daten an diese Dienstleister übermittelt.
|
||||
Wir speichern keine Zahlungs- oder Kreditkartendaten.
|
||||
Rechtsgrundlage: Art. 6 Abs. 1 lit. b und lit. c DSGVO.
|
||||
|
||||
Datenschutzhinweise der Anbieter:
|
||||
- PayPal: https://www.paypal.com/de/webapps/mpp/ua/privacy-full
|
||||
- Paddle: https://www.paypal.com/de/webapps/mpp/ua/privacy-full
|
||||
- Stripe: https://stripe.com/de/privacy
|
||||
|
||||
---
|
||||
@@ -88,7 +88,7 @@ Eine Einwilligung ist nicht erforderlich.
|
||||
|
||||
## 10. Weitergabe an Dritte
|
||||
Eine Weitergabe erfolgt nur an:
|
||||
- Zahlungsdienstleister (PayPal, Stripe)
|
||||
- Zahlungsdienstleister (Paddle, Stripe)
|
||||
- Hoster (Hetzner)
|
||||
- Gesetzlich erforderliche Stellen (z. B. Finanzbehörden)
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ Use of the Fotospiel App requires only the personal data necessary to host and p
|
||||
---
|
||||
|
||||
## 3. Types of Data Processed
|
||||
- Organizer data: name, email address, payment information (via PayPal/Stripe), event details (title, date, photo tasks, photos)
|
||||
- Organizer data: name, email address, payment information (via Paddle/Stripe), event details (title, date, photo tasks, photos)
|
||||
- Guest data: uploaded photos, display name (optional), likes/reactions
|
||||
- Technical data: IP address, browser type, timestamp, device information
|
||||
- Communication data: messages sent via contact form or email
|
||||
@@ -33,7 +33,7 @@ Use of the Fotospiel App requires only the personal data necessary to host and p
|
||||
|----------|--------------|-------------|
|
||||
| Providing the app and hosting events | Art. 6(1)(b) GDPR | Contract performance |
|
||||
| Storing and displaying photos | Art. 6(1)(b) GDPR | Core feature of the app |
|
||||
| Payment processing and invoicing | Art. 6(1)(b), (c) GDPR | Use of PayPal and Stripe services |
|
||||
| Payment processing and invoicing | Art. 6(1)(b), (c) GDPR | Use of Paddle and Stripe services |
|
||||
| Web analytics via Matomo | Art. 6(1)(f) GDPR | Statistical analysis to improve the app |
|
||||
| Server logs and security | Art. 6(1)(f) GDPR | Ensuring system security |
|
||||
| Responding to inquiries | Art. 6(1)(f) or (b) GDPR | Communication with users |
|
||||
@@ -48,12 +48,12 @@ All processing takes place within the EU.
|
||||
---
|
||||
|
||||
## 6. Payment Processing
|
||||
Payments are handled by **PayPal (Europe) S.à r.l. et Cie, S.C.A.** and **Stripe Payments Europe, Ltd.**
|
||||
Payments are handled by **Paddle (Europe) S.à r.l. et Cie, S.C.A.** and **Stripe Payments Europe, Ltd.**
|
||||
We do not store payment or credit card data.
|
||||
Legal basis: Art. 6(1)(b) and (c) GDPR.
|
||||
|
||||
Privacy policies:
|
||||
- PayPal: https://www.paypal.com/de/webapps/mpp/ua/privacy-full
|
||||
- Paddle: https://www.paypal.com/de/webapps/mpp/ua/privacy-full
|
||||
- Stripe: https://stripe.com/de/privacy
|
||||
|
||||
---
|
||||
@@ -87,7 +87,7 @@ No consent is required.
|
||||
|
||||
## 10. Data Disclosure
|
||||
Data is only shared with:
|
||||
- Payment providers (PayPal, Stripe)
|
||||
- Payment providers (Paddle, Stripe)
|
||||
- Hosting provider (Hetzner)
|
||||
- Public authorities when legally required
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Billing and Payments
|
||||
|
||||
## Overview
|
||||
The Fotospiel platform supports multiple payment providers for package purchases: Stripe for one-time and subscription payments, and PayPal for orders and subscriptions. Billing is handled through a freemium model with endcustomer event packages and reseller subscriptions. All payments are processed via API integrations, with webhooks for asynchronous updates.
|
||||
The Fotospiel platform supports multiple payment providers for package purchases: Stripe for one-time and subscription payments, and Paddle for orders and subscriptions. Billing is handled through a freemium model with endcustomer event packages and reseller subscriptions. All payments are processed via API integrations, with webhooks for asynchronous updates.
|
||||
|
||||
## Stripe Integration
|
||||
- **One-time Payments**: Use Stripe Checkout for endcustomer event packages. Create PaymentIntent via `StripeController@createPaymentIntent`.
|
||||
@@ -19,13 +19,13 @@ The Fotospiel platform supports multiple payment providers for package purchases
|
||||
| `invoice.payment_failed` | Flags tenant for follow-up, sends alerts | `handleInvoicePaymentFailed` |
|
||||
| `customer.subscription.deleted` | Finalises cancellation/downgrade | `handleSubscriptionDeleted` |
|
||||
|
||||
## PayPal Integration
|
||||
- **SDK**: Migrated to PayPal Server SDK v1.0+ (`paypal/paypal-server-sdk`). Uses Builder pattern for requests and Controllers for API calls.
|
||||
- **Orders (One-time Payments)**: Endcustomer event packages via Orders API. `PayPalController@createOrder` uses `OrderRequestBuilder` with `CheckoutPaymentIntent::CAPTURE`, custom_id for metadata (tenant_id, package_id, type). Capture in `@captureOrder` using `OrdersController->captureOrder`. DB creation in `processPurchaseFromOrder`.
|
||||
- **Subscriptions (Recurring Payments)**: Reseller subscriptions via Orders API with StoredPaymentSource for recurring setup (no dedicated SubscriptionsController in SDK). `PayPalController@createSubscription` uses `OrderRequestBuilder` with `StoredPaymentSource` (payment_initiator: CUSTOMER, payment_type: RECURRING, usage: FIRST), custom_id including plan_id. Initial order capture sets up subscription; subsequent billing via PayPal dashboard or webhooks. DB records created on initial capture, with expires_at for annual billing.
|
||||
## Paddle Integration
|
||||
- **SDK**: Migrated to Paddle Server SDK v1.0+ (`paypal/paypal-server-sdk`). Uses Builder pattern for requests and Controllers for API calls.
|
||||
- **Orders (One-time Payments)**: Endcustomer event packages via Orders API. `PaddleController@createOrder` uses `OrderRequestBuilder` with `CheckoutPaymentIntent::CAPTURE`, custom_id for metadata (tenant_id, package_id, type). Capture in `@captureOrder` using `OrdersController->captureOrder`. DB creation in `processPurchaseFromOrder`.
|
||||
- **Subscriptions (Recurring Payments)**: Reseller subscriptions via Orders API with StoredPaymentSource for recurring setup (no dedicated SubscriptionsController in SDK). `PaddleController@createSubscription` uses `OrderRequestBuilder` with `StoredPaymentSource` (payment_initiator: CUSTOMER, payment_type: RECURRING, usage: FIRST), custom_id including plan_id. Initial order capture sets up subscription; subsequent billing via Paddle dashboard or webhooks. DB records created on initial capture, with expires_at for annual billing.
|
||||
- **Differences**: One-time: Standard Order with payment_type ONE_TIME (default). Recurring: Order with StoredPaymentSource RECURRING to enable future charges without new approvals. plan_id stored in metadata for reference; no separate subscription ID from SDK.
|
||||
- **Client Setup**: OAuth2 Client Credentials flow. Builder: `PaypalServerSdkClientBuilder::init()->clientCredentialsAuthCredentials(ClientCredentialsAuthCredentialsBuilder::init(client_id, secret))->environment(Environment::SANDBOX/PRODUCTION)->build()`.
|
||||
- **Webhooks**: `PayPalWebhookController@verify` handles events like `PAYMENT.CAPTURE.COMPLETED` (process initial/renewal purchase), `BILLING.SUBSCRIPTION.CANCELLED` or equivalent order events (deactivate package). Simplified signature verification (TODO: Implement `VerifyWebhookSignature`).
|
||||
- **Webhooks**: `PaddleWebhookController@verify` handles events like `PAYMENT.CAPTURE.COMPLETED` (process initial/renewal purchase), `BILLING.SUBSCRIPTION.CANCELLED` or equivalent order events (deactivate package). Simplified signature verification (TODO: Implement `VerifyWebhookSignature`).
|
||||
- **Webhook Matrix**:
|
||||
|
||||
| Event | Purpose | Internal handler |
|
||||
@@ -36,15 +36,15 @@ The Fotospiel platform supports multiple payment providers for package purchases
|
||||
| `BILLING.SUBSCRIPTION.SUSPENDED` | Pauses package benefits pending review | `handleSubscription` |
|
||||
- **Idempotency**: Check `provider_id` in `PackagePurchase` before creation. Transactions for DB safety.
|
||||
- **Configuration**: Keys in `config/services.php` under `paypal`. Sandbox mode via `paypal.sandbox`.
|
||||
- **Migration Notes**: Replaced old Checkout SDK (`PayPalCheckoutSdk`). Updated imports, requests (e.g., OrdersCreateRequest -> OrderRequestBuilder, Subscriptions -> StoredPaymentSource in Orders). Responses: Use `getStatusCode()` and `getResult()`. Tests mocked new structures. No breaking changes in auth or metadata handling; recurring flows now unified under Orders API.
|
||||
- **Migration Notes**: Replaced old Checkout SDK (`PaddleCheckoutSdk`). Updated imports, requests (e.g., OrdersCreateRequest -> OrderRequestBuilder, Subscriptions -> StoredPaymentSource in Orders). Responses: Use `getStatusCode()` and `getResult()`. Tests mocked new structures. No breaking changes in auth or metadata handling; recurring flows now unified under Orders API.
|
||||
|
||||
## Database Models
|
||||
- **PackagePurchase**: Records purchases with `tenant_id`, `package_id`, `provider_id` (Stripe PI/PayPal Order ID), `price`, `type` (endcustomer_event/reseller_subscription), `status` (completed/refunded), `metadata` (JSON with provider details).
|
||||
- **PackagePurchase**: Records purchases with `tenant_id`, `package_id`, `provider_id` (Stripe PI/Paddle Order ID), `price`, `type` (endcustomer_event/reseller_subscription), `status` (completed/refunded), `metadata` (JSON with provider details).
|
||||
- **TenantPackage**: Active packages with `tenant_id`, `package_id`, `price`, `purchased_at`, `expires_at`, `active` flag. Updated on purchase/cancellation.
|
||||
- **Constraints**: `type` CHECK (endcustomer_event, reseller_subscription), `price` NOT NULL.
|
||||
|
||||
## Flows
|
||||
1. **Purchase Initiation**: User selects package in PurchaseWizard. For free: direct assignment. Paid: Redirect to provider (Stripe Checkout or PayPal approve link).
|
||||
1. **Purchase Initiation**: User selects package in PurchaseWizard. For free: direct assignment. Paid: Redirect to provider (Stripe Checkout or Paddle approve link).
|
||||
2. **Completion**: Provider callback/webhook triggers capture/confirmation. Create `PackagePurchase` and `TenantPackage`. Update tenant `subscription_status` to 'active'.
|
||||
3. **Cancellation/Refund**: Webhook updates status to 'cancelled', deactivates `TenantPackage`.
|
||||
4. **Trial**: First reseller subscription gets 14-day trial (`expires_at = now() + 14 days`).
|
||||
@@ -63,5 +63,5 @@ The Fotospiel platform supports multiple payment providers for package purchases
|
||||
## Security & Compliance
|
||||
- GDPR: No PII in logs/metadata beyond necessary (tenant_id anonymous).
|
||||
- Auth: Sanctum tokens for API, CSRF for web.
|
||||
- Webhooks: IP whitelisting (PayPal IPs), signature verification.
|
||||
- Webhooks: IP whitelisting (Paddle IPs), signature verification.
|
||||
- Retention: Purchases retained per Privacy policy; update on changes.
|
||||
|
||||
@@ -2,16 +2,16 @@
|
||||
|
||||
## Goals
|
||||
- Replace the legacy marketing checkout flow with a single `CheckoutController` that owns auth, payment, and confirmation steps.
|
||||
- Support Stripe card payments and PayPal orders with a consistent state machine that can be extended to other providers.
|
||||
- Support Stripe card payments and Paddle orders with a consistent state machine that can be extended to other providers.
|
||||
- Keep package activation logic idempotent and traceable while respecting GDPR (no new PII logging, no leaked tokens).
|
||||
- Prepare the frontend wizard to drive the flow as an SPA without relying on server-side redirects.
|
||||
|
||||
## Core Building Blocks
|
||||
- **CheckoutSession model/table** keeps one purchase attempt per user + package. It stores provider choice, status, pricing snapshot, and external ids (Stripe intent, PayPal order, etc.).
|
||||
- **CheckoutSession model/table** keeps one purchase attempt per user + package. It stores provider choice, status, pricing snapshot, and external ids (Stripe intent, Paddle order, etc.).
|
||||
- **CheckoutPaymentService** orchestrates provider-specific actions (create intent/order, capture, sync metadata) and normalises responses for the wizard.
|
||||
- **CheckoutAssignmentService** performs the idempotent write workflow (create/update TenantPackage, PackagePurchase, tenant subscription fields, welcome mail) once payment succeeds.
|
||||
- **Wizard API surface** (JSON routes under `/checkout/*`) is session-authenticated, CSRF-protected, and returns structured payloads consumed by the PWA.
|
||||
- **Webhooks** (Stripe, PayPal) map incoming provider events back to `CheckoutSession` rows to guarantee reconciliation and support 3DS / async capture paths.
|
||||
- **Webhooks** (Stripe, Paddle) map incoming provider events back to `CheckoutSession` rows to guarantee reconciliation and support 3DS / async capture paths.
|
||||
- **Feature Flag**: `config/checkout.php` exposes `CHECKOUT_WIZARD_ENABLED` and `CHECKOUT_WIZARD_FLAG` so the SPA flow can be toggled or gradual-rolled out during launch.
|
||||
- **Operational**: Rotate JWT signing keys with `php artisan oauth:rotate-keys` (updates key folder per KID; remember to bump `OAUTH_JWT_KID`).
|
||||
|
||||
@@ -22,7 +22,7 @@ State constants live on `CheckoutSession` (`status` column, enum):
|
||||
| --- | --- | --- |
|
||||
| `draft` | Session created, package locked in, no provider chosen. | `awaiting_payment_method`, `completed` (free), `cancelled` |
|
||||
| `awaiting_payment_method` | Paid package; provider picked, waiting for client to initialise SDK. | `requires_customer_action`, `processing`, `cancelled` |
|
||||
| `requires_customer_action` | Stripe 3DS, PayPal approval window open, or additional customer steps needed. | `processing`, `failed`, `cancelled` |
|
||||
| `requires_customer_action` | Stripe 3DS, Paddle approval window open, or additional customer steps needed. | `processing`, `failed`, `cancelled` |
|
||||
| `processing` | Provider reported success, backend validating / capturing / assigning. | `completed`, `failed` |
|
||||
| `completed` | Checkout finished, package assigned, confirmation step unblocked. | none |
|
||||
| `failed` | Provider declined or capture check failed; retain reason. | `awaiting_payment_method` (retry), `cancelled` |
|
||||
@@ -49,34 +49,34 @@ Create Eloquent model `App\Models\CheckoutSession` with casts for JSON columns a
|
||||
Group under `web.php` with `middleware(['auth', 'verified', 'locale', 'throttle:checkout'])`:
|
||||
- `POST /checkout/session` (`CheckoutController@storeSession`): create or resume active session for selected package, return `{id, status, amount, package_snapshot}`.
|
||||
- `PATCH /checkout/session/{session}/package` (`updatePackage`): allow switching package before payment; resets provider-specific fields and status to `draft`.
|
||||
- `POST /checkout/session/{session}/provider` (`selectProvider`): set provider (`stripe` or `paypal`), transitions to `awaiting_payment_method` and returns provider configuration (publishable key, PayPal client id, feature flags).
|
||||
- `POST /checkout/session/{session}/provider` (`selectProvider`): set provider (`stripe` or `paypal`), transitions to `awaiting_payment_method` and returns provider configuration (publishable key, Paddle client id, feature flags).
|
||||
- `POST /checkout/session/{session}/stripe-intent` (`createStripeIntent`): idempotently create/update PaymentIntent with metadata (user, tenant, package, session id) and deliver `{client_secret, intent_id}`.
|
||||
- `POST /checkout/session/{session}/stripe/confirm` (`confirmStripeIntent`): server-side verify PaymentIntent status (retrieve from Stripe) and transition to `processing` when `succeeded` or `requires_action`.
|
||||
- `POST /checkout/session/{session}/paypal/order` (`createPayPalOrder`): create order with `custom_id` payload (session, tenant, package) and return `{order_id, approve_url}`.
|
||||
- `POST /checkout/session/{session}/paypal/capture` (`capturePayPalOrder`): capture order server-side, transition to `processing` if status `COMPLETED`.
|
||||
- `POST /checkout/session/{session}/paypal/order` (`createPaddleOrder`): create order with `custom_id` payload (session, tenant, package) and return `{order_id, approve_url}`.
|
||||
- `POST /checkout/session/{session}/paypal/capture` (`capturePaddleOrder`): capture order server-side, transition to `processing` if status `COMPLETED`.
|
||||
- `POST /checkout/session/{session}/free` (`activateFreePackage`): bypass providers, run assignment service, mark `completed`.
|
||||
- `POST /checkout/session/{session}/complete` (`finalise`): provider-agnostic finishing hook used after `processing` to run `CheckoutAssignmentService`, persist `PackagePurchase`, queue mails, and respond with summary.
|
||||
- `GET /checkout/session/{session}` (`show`): used by wizard polling to keep state in sync (status, provider display data, failure reasons).
|
||||
- `DELETE /checkout/session/{session}` (`cancel`): expire session, clean provider artefacts (cancel intent/order if applicable).
|
||||
|
||||
Stripe/PayPal routes remain under `routes/web.php` but call into new service classes; legacy marketing payment methods are removed once parity is verified.
|
||||
Paddle routes remain under `routes/web.php` but call into new service classes; legacy marketing payment methods are removed once parity is verified.
|
||||
|
||||
### Services & Jobs
|
||||
- `CheckoutSessionService`: create/resume session, guard transitions, enforce TTL, and wrap DB transactions.
|
||||
- `CheckoutPaymentService`: entry point with methods `initialiseStripe`, `confirmStripe`, `initialisePayPal`, `capturePayPal`, `finaliseFree`. Delegates to provider-specific helpers (Stripe SDK, PayPal SDK) and persists external ids.
|
||||
- `CheckoutPaymentService`: entry point with methods `initialiseStripe`, `confirmStripe`, `initialisePaddle`, `capturePaddle`, `finaliseFree`. Delegates to provider-specific helpers (Stripe SDK, Paddle SDK) and persists external ids.
|
||||
- `CheckoutAssignmentService`: generates or reuses tenant, writes `TenantPackage`, `PackagePurchase`, updates user role/status, dispatches `Welcome` + purchase receipts, and emits domain events (`CheckoutCompleted`).
|
||||
- `SyncCheckoutFromWebhook` job: invoked by webhook controllers with provider payload, looks up `CheckoutSession` via provider id, runs assignment if needed, records failure states.
|
||||
|
||||
### Webhook Alignment
|
||||
- Update `StripeWebhookController` to resolve `CheckoutSession::where('stripe_payment_intent_id', intentId)`; when event indicates success, transition to `processing` (if not already), enqueue `SyncCheckoutFromWebhook` to finish assignment, and mark `completed` once done.
|
||||
- Update `PayPalWebhookController` similarly using `paypal_order_id` or `paypal_subscription_id`.
|
||||
- Update `PaddleWebhookController` similarly using `paypal_order_id` or `paypal_subscription_id`.
|
||||
- Webhooks become source-of-truth for delayed confirmations; wizard polls `GET /checkout/session/{id}` until `completed`.
|
||||
|
||||
### Validation & Security
|
||||
- All mutating routes use CSRF tokens and `auth` guard (session-based). Add `EnsureCheckoutSessionOwner` middleware enforcing that the session belongs to `request->user()`.
|
||||
- Input validation via dedicated Form Request classes (e.g., `StoreCheckoutSessionRequest`, `StripeIntentRequest`).
|
||||
- Provider responses are never logged raw; only store ids + safe metadata.
|
||||
- Abandon expired sessions via scheduler (`checkout:expire-sessions` artisan command). Command cancels open PaymentIntents and PayPal orders.
|
||||
- Abandon expired sessions via scheduler (`checkout:expire-sessions` artisan command). Command cancels open PaymentIntents and Paddle orders.
|
||||
|
||||
## Frontend Touchpoints
|
||||
|
||||
@@ -89,7 +89,7 @@ Stripe/PayPal routes remain under `routes/web.php` but call into new service cla
|
||||
- On mount (and whenever package changes), call `/checkout/session` to create/resume session. Reset state if API reports new id.
|
||||
- Provider tabs call `selectProvider`. Stripe tab loads Stripe.js dynamically (import from `@stripe/stripe-js`) and mounts Elements once `client_secret` arrives.
|
||||
- Stripe flow: submit button triggers `stripe.confirmCardPayment(clientSecret)`, handle `requires_action`, then POST `/checkout/session/{id}/stripe/confirm`. On success, call `/checkout/session/{id}/complete` and advance to confirmation step.
|
||||
- PayPal flow: render PayPal Buttons with `createOrder` -> call `/checkout/session/{id}/paypal/order`; `onApprove` -> POST `/checkout/session/{id}/paypal/capture`, then `/checkout/session/{id}/complete`.
|
||||
- Paddle flow: render Paddle Buttons with `createOrder` -> call `/checkout/session/{id}/paypal/order`; `onApprove` -> POST `/checkout/session/{id}/paypal/capture`, then `/checkout/session/{id}/complete`.
|
||||
- Free packages skip provider selection; call `/checkout/session/{id}/free` and immediately advance.
|
||||
- Display status toasts based on `paymentStatus`; show inline error block when `failed` with `failure_reason` from API.
|
||||
|
||||
@@ -106,10 +106,10 @@ Stripe/PayPal routes remain under `routes/web.php` but call into new service cla
|
||||
5. Once Stripe marks intent `succeeded`, backend transitions to `processing`, calls `CheckoutAssignmentService`, and marks `completed`.
|
||||
6. For reseller packages, Stripe subscription is created after assignment using configured price ids; resulting subscription id stored on session + tenant record.
|
||||
|
||||
### PayPal (one-off and subscription)
|
||||
### Paddle (one-off and subscription)
|
||||
1. Session `draft` -> provider `paypal`.
|
||||
2. `createPayPalOrder` returns order id + approval link. Metadata includes session, tenant, package, package_type.
|
||||
3. After approval, `capturePayPalOrder` verifies capture status; on `COMPLETED`, transitions to `processing`.
|
||||
2. `createPaddleOrder` returns order id + approval link. Metadata includes session, tenant, package, package_type.
|
||||
3. After approval, `capturePaddleOrder` verifies capture status; on `COMPLETED`, transitions to `processing`.
|
||||
4. Assignment service runs, storing order id as `provider_id`. For subscriptions, capture handler stores subscription id and updates tenant subscription status.
|
||||
5. Webhooks handle late captures or cancellations (updates session -> `failed` or `cancelled`).
|
||||
|
||||
@@ -118,18 +118,18 @@ Stripe/PayPal routes remain under `routes/web.php` but call into new service cla
|
||||
|
||||
## Migration Strategy
|
||||
1. **Phase 1** (current): land schema, services, new API routes; keep legacy MarketingController flow for fallback.
|
||||
2. **Phase 2**: wire the new wizard PaymentStep behind feature flag `checkout_v2` (in `.env` / config). Run internal QA with Stripe/PayPal sandbox.
|
||||
3. **Phase 3**: enable feature flag for production tenants, monitor Stripe/PayPal events, then delete legacy marketing payment paths and routes.
|
||||
2. **Phase 2**: wire the new wizard PaymentStep behind feature flag `checkout_v2` (in `.env` / config). Run internal QA with Paddle sandbox.
|
||||
3. **Phase 3**: enable feature flag for production tenants, monitor Paddle events, then delete legacy marketing payment paths and routes.
|
||||
4. **Phase 4**: tighten webhook logic and remove `MarketingController::checkout`, `::paypalCheckout`, `::stripeSubscription` once new flow is stable.
|
||||
|
||||
## Testing & QA
|
||||
- **Feature tests**: JSON endpoints for session lifecycle (create, provider select, intent creation, capture success/failure, free activation). Include multi-locale assertions.
|
||||
- **Payment integration tests**: use Stripe + PayPal SDK test doubles to simulate success, requires_action, cancellation, and ensure state machine behaves.
|
||||
- **Playwright**: wizard flow covering Stripe happy path, Stripe 3DS stub, PayPal approval, failure retry, free package shortcut, session resume after refresh.
|
||||
- **Payment integration tests**: use Stripe + Paddle SDK test doubles to simulate success, requires_action, cancellation, and ensure state machine behaves.
|
||||
- **Playwright**: wizard flow covering Stripe happy path, Stripe 3DS stub, Paddle approval, failure retry, free package shortcut, session resume after refresh.
|
||||
- **Webhooks**: unit tests for mapping provider ids to sessions, plus job tests for idempotent assignment.
|
||||
- **Scheduler**: test `checkout:expire-sessions` to confirm PaymentIntents are cancelled and sessions flagged `cancelled`.
|
||||
|
||||
## Open Questions / Follow-Ups
|
||||
- Map package records to Stripe price ids and PayPal plan ids (store on `packages` table or config?).
|
||||
- Map package records to Stripe price ids and Paddle plan ids (store on `packages` table or config?).
|
||||
- Confirm legal copy updates for new checkout experience before GA.
|
||||
- Align email templates (welcome, receipt) with new assignment service outputs.
|
||||
|
||||
@@ -13,7 +13,7 @@ Die App ersetzt das frühere Filament-basierte Tenant-Panel und fokussiert auf e
|
||||
|
||||
## Aktuelle Highlights (Q4 2025)
|
||||
- **Geführtes Onboarding**: `/event-admin/welcome/*` orchestriert den Welcome Flow (Hero → How-It-Works → Paketwahl → Zusammenfassung → Event Setup). Guarding erfolgt über `onboarding_completed_at`.
|
||||
- **Direkter Checkout**: Stripe & PayPal sind in die Paketwahl des Welcome Flows eingebettet; Fortschritt wird im Onboarding-Context persistiert.
|
||||
- **Direkter Checkout**: Paddle sind in die Paketwahl des Welcome Flows eingebettet; Fortschritt wird im Onboarding-Context persistiert.
|
||||
- **Filament Wizard**: Für Super-Admins existiert ein paralleler QR/Join-Token-Wizard in Filament (Token-Generierung, Layout-Downloads, Rotation).
|
||||
- **Join Tokens only**: Gäste erhalten ausschließlich join-token-basierte Links/QRs; slug-basierte URLs wurden deaktiviert. QR-Drucklayouts liegen unter `resources/views/pdf/join-tokens/*`.
|
||||
- **OAuth Alignment**: `VITE_OAUTH_CLIENT_ID` + `/event-admin/auth/callback` werden seedingseitig synchron gehalten; siehe `docs/prp/tenant-app-specs/api-usage.md`.
|
||||
|
||||
@@ -11,7 +11,7 @@ Die Admin-App muss folgende Kernfunktionen bereitstellen:
|
||||
- **Galerie-Management**: Upload, Moderation, Feature-Flags, Analytics.
|
||||
- **Mitglieder-Verwaltung**: Einladungen, Rollen, Zugriffskontrolle.
|
||||
- **Tasks & Emotions**: Bibliothek, Zuweisung, Fortschritts-Tracking.
|
||||
- **Abrechnung**: Paketübersicht, Stripe/PayPal Checkout, Ledger.
|
||||
- **Abrechnung**: Paketübersicht, Paddle Checkout, Ledger.
|
||||
- **Einstellungen**: Branding, Limits, Rechtstexte, Benachrichtigungen.
|
||||
- **Offline-Support**: App-Shell-Caching, Queueing von Mutationen, Sync bei Reconnect.
|
||||
- **Compliance**: Audit-Logging, GDPR-konforme Löschung, ETag-basierte Konfliktlösung.
|
||||
@@ -25,7 +25,7 @@ Die Admin-App muss folgende Kernfunktionen bereitstellen:
|
||||
- Routen `/event-admin/welcome/*` bilden den Flow.
|
||||
- Filament stellt einen korrespondierenden Onboarding-Wizard (QR/Join-Token, Layout-Download) bereit; Abschluss setzt `onboarding_completed_at` serverseitig.
|
||||
- `useOnboardingProgress` persistiert Fortschritt (localStorage) und synchronisiert mit Backend (`onboarding_completed_at`).
|
||||
- Paketwahl nutzt `GET /tenant/packages`; Stripe/PayPal-Fallbacks informieren bei fehlender Konfiguration.
|
||||
- Paketwahl nutzt `GET /tenant/packages`; Paddle-Fallbacks informieren bei fehlender Konfiguration.
|
||||
- Dashboard weist per CTA auf offenes Onboarding hin, bis ein erstes Event erstellt wurde.
|
||||
|
||||
### Event Lifecycle
|
||||
@@ -45,7 +45,7 @@ Die Admin-App muss folgende Kernfunktionen bereitstellen:
|
||||
|
||||
### Billing & Checkout
|
||||
- Pakete + Credit-Balance anzeigen.
|
||||
- Stripe PaymentIntent & PayPal Smart Buttons; Fallback-Meldung bei fehlender Konfiguration.
|
||||
- Stripe PaymentIntent & Paddle Smart Buttons; Fallback-Meldung bei fehlender Konfiguration.
|
||||
- Ledger mit Historie (Paginierung, Filter).
|
||||
|
||||
### Settings
|
||||
@@ -83,7 +83,7 @@ Die App nutzt Endpunkte aus `docs/prp/03-api.md`.
|
||||
## Teststrategie
|
||||
- **PHPUnit**: Feature-Tests für Auth-Guards (Tenant ohne Events → Welcome Flow).
|
||||
- **React Testing Library**: `TenantWelcomeLayout`, `PackageSelection`, `OnboardingGuard`, `OrderSummary`.
|
||||
- **Playwright**: `tests/e2e/tenant-onboarding-flow.test.ts` deckt Login, Welcome → Packages → Summary → Event Setup ab; Erweiterung um Stripe/PayPal Happy Paths und Offline/Retry geplant.
|
||||
- **Smoke Tests**: `npm run test:e2e` in CI mit optionalen Credentials (`E2E_TENANT_EMAIL`, `E2E_TENANT_PASSWORD`, Stripe/PayPal Keys).
|
||||
- **Playwright**: `tests/e2e/tenant-onboarding-flow.test.ts` deckt Login, Welcome → Packages → Summary → Event Setup ab; Erweiterung um Paddle Happy Paths und Offline/Retry geplant.
|
||||
- **Smoke Tests**: `npm run test:e2e` in CI mit optionalen Credentials (`E2E_TENANT_EMAIL`, `E2E_TENANT_PASSWORD`, Paddle Keys).
|
||||
|
||||
Für UI-Details siehe `docs/prp/tenant-app-specs/pages-ui.md`. Einstellungen werden in `docs/prp/tenant-app-specs/settings-config.md` beschrieben.
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
| --- | --- | --- | --- |
|
||||
| Hero | `/event-admin/welcome` | `TenantWelcomeLayout`, `WelcomeStepCard`, `EmblaCarousel` | CTA „Pakete entdecken“, sekundärer Link „Später entscheiden“ |
|
||||
| How It Works | `/event-admin/welcome` (Carousel Slide) | Icon Cards, Animated Gradients | 3 Vorteile (Fotos festhalten, Aufgaben, Gäste aktivieren) |
|
||||
| Paketwahl | `/event-admin/welcome/packages` | `PackageCard`, `PricingToggle`, `QueryPackageList` | Stripe/PayPal Pricing, Feature-Badges, Auswahl persistiert im Onboarding-Context |
|
||||
| Zusammenfassung | `/event-admin/welcome/summary` | `OrderSummaryCard`, Stripe Elements, PayPal Buttons | Hinweise bei fehlender Zahlungs-Konfiguration, CTA „Weiter zum Setup“ |
|
||||
| Paketwahl | `/event-admin/welcome/packages` | `PackageCard`, `PricingToggle`, `QueryPackageList` | Paddle Pricing, Feature-Badges, Auswahl persistiert im Onboarding-Context |
|
||||
| Zusammenfassung | `/event-admin/welcome/summary` | `OrderSummaryCard`, Stripe Elements, Paddle Buttons | Hinweise bei fehlender Zahlungs-Konfiguration, CTA „Weiter zum Setup“ |
|
||||
| Event Setup | `/event-admin/welcome/event` | `FirstEventForm`, `FormStepper`, Toasts | Formular (Name, Datum, Sprache, Feature-Toggles) + Abschluss CTA „Event erstellen“ |
|
||||
|
||||
### Guards & Fortschritt
|
||||
@@ -39,7 +39,7 @@
|
||||
- **Fotos**: Moderationsgrid (Masonry), Filter (Neu, Genehmigt, Featured), Bulk-Aktionen in Sticky-Footer.
|
||||
- **Tasks**: Tabs (Bibliothek, Zuweisungen), Drag-and-Drop (React Beautiful DnD), Inline-Editor für Aufgaben.
|
||||
- **Einstellungen**: Accordion-Struktur (Branding, Legal Pages, Benachrichtigungen, Abrechnung). Preview-Panel für Farben und Logos.
|
||||
- **Abrechnung**: Kreditübersicht, Kauflog (infinite-scroll), Zahlungsoptionen (Stripe Karte, PayPal Checkout).
|
||||
- **Abrechnung**: Kreditübersicht, Kauflog (infinite-scroll), Zahlungsoptionen (Stripe Karte, Paddle Checkout).
|
||||
|
||||
## Informationsarchitektur (aktuelle React-Router-Konfiguration)
|
||||
```
|
||||
@@ -61,7 +61,7 @@
|
||||
|
||||
## Testabdeckung (UI)
|
||||
- **Jest/RTL**: `TenantWelcomeLayout`, `WelcomeStepCard`, `PackageSelection`, `OnboardingGuard`.
|
||||
- **Playwright**: `tests/e2e/tenant-onboarding-flow.test.ts` (Login Guard, Welcome → Packages → Summary → Event Setup). Erweiterbar um Stripe/PayPal-Happy-Path sowie Offline-/Retry-Szenarien.
|
||||
- **Playwright**: `tests/e2e/tenant-onboarding-flow.test.ts` (Login Guard, Welcome → Packages → Summary → Event Setup). Erweiterbar um Paddle-Happy-Path sowie Offline-/Retry-Szenarien.
|
||||
|
||||
## Legacy-Referenz (Framework7 Entwurf 2025-09)
|
||||
Die ursprünglichen Wireframes für Framework7 (Toolbar, FAB, Infinite Scroll) sind weiterhin im Repo historisiert (`docs/prp/tenant-app-specs/pages-ui-legacy.md`). Für Vergleiche bei Regressionen oder Migrationen bitte dort nachsehen.
|
||||
|
||||
@@ -52,6 +52,6 @@
|
||||
</text>
|
||||
</g>
|
||||
<text x="80" y="600" font-size="20" font-family="Inter, Arial, sans-serif" fill="#475569">
|
||||
Stripe & PayPal Widgets erscheinen unterhalb der Karten, sobald Keys konfiguriert sind.
|
||||
Stripe & Paddle Widgets erscheinen unterhalb der Karten, sobald Keys konfiguriert sind.
|
||||
</text>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
@@ -9,7 +9,7 @@
|
||||
Paket: Pro – 3 Events, 1000 Uploads
|
||||
</text>
|
||||
<text x="40" y="180" font-size="22" font-family="Inter, Arial, sans-serif" fill="#cbd5f5">
|
||||
Zahlungsart: Stripe oder PayPal
|
||||
Zahlungsart: Stripe oder Paddle
|
||||
</text>
|
||||
<line x1="40" y1="220" x2="480" y2="220" stroke="#1f2a3d" stroke-width="2"/>
|
||||
<text x="40" y="280" font-size="24" font-family="Inter, Arial, sans-serif" fill="#60a5fa">
|
||||
@@ -44,10 +44,10 @@
|
||||
<g transform="translate(660,380)">
|
||||
<rect width="480" height="220" rx="24" fill="#ffffff"/>
|
||||
<text x="40" y="80" font-size="28" font-family="Inter, Arial, sans-serif" font-weight="600" fill="#0f172a">
|
||||
PayPal Smart Buttons
|
||||
Paddle Checkout Links
|
||||
</text>
|
||||
<text x="40" y="130" font-size="20" font-family="Inter, Arial, sans-serif" fill="#1f2937">
|
||||
Automatische Darstellung abhängig vom PayPal Client ID.
|
||||
Automatische Darstellung abhängig von der Paddle-Konfiguration.
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
@@ -4,7 +4,7 @@
|
||||
| --- | --- |
|
||||
| `01-welcome-hero.svg` | Hero-Screen mit CTA „Pakete entdecken“. |
|
||||
| `02-how-it-works.svg` | Drei Highlight-Karten (Fotos, Aufgaben, Gäste). |
|
||||
| `03-package-selection.svg` | Paketübersicht inkl. Stripe/PayPal Modulen. |
|
||||
| `03-package-selection.svg` | Paketübersicht inkl. Paddle Modulen. |
|
||||
| `04-order-summary.svg` | Zusammenfassung mit Zahlungsoptionen. |
|
||||
| `05-event-setup.svg` | Formular für das erste Event. |
|
||||
|
||||
|
||||
28
docs/todo/paddle-catalog-sync.md
Normal file
28
docs/todo/paddle-catalog-sync.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# Paddle Catalog Sync Rollout
|
||||
|
||||
- [x] **Schema Prep**
|
||||
- [x] Add migration for `paddle_sync_status`, `paddle_synced_at`, and `paddle_snapshot` JSON on `packages`.
|
||||
- [x] Update `Package` model casts/fillable + ensure factory coverage.
|
||||
- [ ] **Service Layer**
|
||||
- [x] Scaffold `PaddleCatalogService` (product/price CRUD, custom data mapping).
|
||||
- [ ] Add DTO helpers for Paddle product/price responses.
|
||||
- [ ] Extend `PaddleClient` tests/mocks for catalog endpoints.
|
||||
- [x] **Sync Logic**
|
||||
- [x] Implement `SyncPackageToPaddle` job with create/update flows and metadata diffing.
|
||||
- [x] Create `PaddlePackagePull` job for optional remote-to-local reconciliation.
|
||||
- [x] Add `paddle:sync-packages` artisan command (`--dry-run`, `--package=`, `--pull`).
|
||||
- [ ] **Admin UX**
|
||||
- [x] Enhance Filament PackageResource with sync status badges + last sync timestamp.
|
||||
- [ ] Add table/detail actions (“Sync to Paddle”, “Link existing Paddle entity”).
|
||||
- [ ] Surface last error/log context in the admin sidebar panel.
|
||||
- [ ] **Ops & Observability**
|
||||
- [ ] Configure dedicated log channel/Slack hook for catalog sync outcomes.
|
||||
- [ ] Document failure recovery playbook (retry, unlink, support escalation).
|
||||
- [ ] **Testing & QA**
|
||||
- [x] Unit tests for service + jobs using mocked Paddle API.
|
||||
- [x] Feature test covering artisan command flow.
|
||||
- [ ] Playwright smoke to confirm admin sync action displays status.
|
||||
- [ ] **Rollout Checklist**
|
||||
- [ ] Seed Paddle sandbox catalog via MCP server using migrated data.
|
||||
- [ ] Verify legacy packages mapped to Paddle IDs before enabling auto-sync.
|
||||
- [ ] Announce workflow change to admin users (release notes + docs update).
|
||||
14
docs/todo/paddle-migration.md
Normal file
14
docs/todo/paddle-migration.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# Paddle Billing Migration
|
||||
|
||||
- [x] Review current billing implementation (Stripe, Paddle, RevenueCat) across code, jobs, webhooks, docs.
|
||||
- [x] Design Paddle data mappings for packages ↔ products/prices, including required metadata round-trip.
|
||||
- [ ] Extend Laravel config/env handling for Paddle keys, webhook secrets, feature flags (sandbox + production).
|
||||
- [ ] Build Paddle API service layer and register sandbox webhooks; document endpoints/events consumed.
|
||||
- [ ] Add admin catalog sync UI for packages (create/update in Paddle, display sync status, store Paddle IDs).
|
||||
- [ ] Implement tenant ↔ Paddle customer synchronization and related webhook handlers.
|
||||
- [x] Replace marketing checkout payment step with Paddle-hosted checkout flow and success callbacks.
|
||||
- [ ] Update tenant admin billing pages to read Paddle subscription/transaction data and manage plans.
|
||||
- [ ] Define mobile/native billing strategy (RevenueCat vs Paddle) and align app logic.
|
||||
- [ ] Add automated tests for Paddle integration (unit, feature, e2e) covering checkout, webhooks, sync.
|
||||
- [ ] Populate Paddle sandbox catalog via MCP server and validate end-to-end activation flow.
|
||||
- [ ] Draft production cutover procedure (catalog creation, flag switch, legacy shutdown, monitoring, rollback).
|
||||
@@ -43,7 +43,7 @@ Raise the baseline security posture across guest APIs, checkout, media storage,
|
||||
- `SEC-MS-04` — Storage health widget in Super Admin (Week 4).
|
||||
|
||||
5. **Payments & Webhooks (Billing)**
|
||||
- Link Stripe/PayPal webhooks to checkout sessions with idempotency locks.
|
||||
- Link Paddle webhooks to checkout sessions with idempotency locks.
|
||||
- Add signature freshness validation + retry policies for provider outages.
|
||||
- Pipe failed capture events into credit ledger audits and operator alerts.
|
||||
- **Tickets**
|
||||
|
||||
@@ -32,11 +32,11 @@ Owner: Codex (handoff)
|
||||
- [x] Review PWA manifest/offline setup so die kombinierte Welcome+Management-Experience TWA-/Capacitor-ready ist (Manifest + `admin-sw.js` dokumentiert).
|
||||
- [x] Extend docs: PRP-Onboarding-Abschnitte aktualisiert, Screenshots unter `docs/screenshots/tenant-admin-onboarding/` ergänzt, Testscope notiert.
|
||||
- [x] Add automated coverage: Vitest + Testing Library für Welcome Landing, Dashboard-Guard und Checkout-Komponenten; `npm run test:unit` führt Suite aus.
|
||||
- [x] Finalise direct checkout: Stripe/PayPal-Flows markieren Fortschritt, API-Mocks + Unit-Tests decken Erfolgs- und Fehlerpfade ab.
|
||||
- [x] Finalise direct checkout: Paddle-Flows markieren Fortschritt, API-Mocks + Unit-Tests decken Erfolgs- und Fehlerpfade ab.
|
||||
- [x] Lokalisierung ausbauen: Landing-, Packages-, Summary- und Event-Setup-Screens sind nun DE/EN übersetzt; Copy-Review für weitere Module (Tasks/Billing/Members) bleibt offen.
|
||||
|
||||
## Risks & Open Questions
|
||||
- Confirm checkout UX expectations (Stripe vs PayPal) before wiring package purchase into onboarding.
|
||||
- Confirm checkout UX expectations (Stripe vs Paddle) before wiring package purchase into onboarding.
|
||||
- Validate whether onboarding flow must be localized at launch; coordinate mit den neuen i18n JSONs und fehlenden Übersetzungen.
|
||||
- Determine deprecation plan for fotospiel-tenant-app/tenant-admin-app once the merged flow ships.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user