- Tenant-Admin-PWA: Neues /event-admin/welcome Onboarding mit WelcomeHero, Packages-, Order-Summary- und Event-Setup-Pages, Zustandsspeicher, Routing-Guard und Dashboard-CTA für Erstnutzer; Filament-/admin-Login via Custom-View behoben.

- Brand/Theming: Marketing-Farb- und Typographievariablen in `resources/css/app.css` eingeführt, AdminLayout, Dashboardkarten und Onboarding-Komponenten entsprechend angepasst; Dokumentation (`docs/todo/tenant-admin-onboarding-fusion.md`, `docs/changes/...`) aktualisiert.
- Checkout & Payments: Checkout-, PayPal-Controller und Tests für integrierte Stripe/PayPal-Flows sowie Paket-Billing-Abläufe überarbeitet; neue PayPal SDK-Factory und Admin-API-Helper (`resources/js/admin/api.ts`) schaffen Grundlage für Billing/Members/Tasks-Seiten.
- DX & Tests: Neue Playwright/E2E-Struktur (docs/testing/e2e.md, `tests/e2e/tenant-onboarding-flow.test.ts`, Utilities), E2E-Tenant-Seeder und zusätzliche Übersetzungen/Factories zur Unterstützung der neuen Flows.
- Marketing-Kommunikation: Automatische Kontakt-Bestätigungsmail (`ContactConfirmation` + Blade-Template) implementiert; Guest-PWA unter `/event` erreichbar.
- Nebensitzung: Blogsystem gefixt und umfassenden BlogPostSeeder für Beispielinhalte angelegt.
This commit is contained in:
Codex Agent
2025-10-10 21:31:55 +02:00
parent 52197f216d
commit d04e234ca0
84 changed files with 8397 additions and 1005 deletions

View File

@@ -1,60 +1,50 @@
# 08 — Billing (Packages)
# Billing and Payments
## Overview
- Model: one-off purchases of event packages (Endkunden) or annual subscriptions (Reseller); see 15-packages-design.md for details.
- Tables: `packages`, `event_packages`, `tenant_packages`, `package_purchases` (see 04-data-model-migrations.md and 15-packages-design.md).
- Providers: Stripe (server-side checkout + webhooks for Einmalkäufe/Subscriptions), PayPal (Orders API + webhooks); store receipts and metadata.
- Idempotency: purchase intents keyed by provider_id; purchase writes idempotent (check existing before create); retries safe via DB transactions.
- Limits: Enforce package selection at event creation; check event-specific limits (e.g. max_photos) during usage; tenant limits for reseller event count.
- SaaS Aspects: Trial periods (14 days for first reseller subscription), subscription status updates, GDPR compliance (no PII in logs/metadata, privacy consent required), cancellation links to provider dashboards.
## PurchaseWizard Flow (Frontend: resources/js/pages/marketing/PurchaseWizard.tsx)
- Multi-step SPA: Package Selection → Auth (Login/Register, conditional for unauthenticated) → Payment (Stripe Elements or PayPal Buttons toggle) → Success.
- Persistence: sessionStorage for state (wizardData, currentStep, timestamp with 30min TTL); restores on reload/refresh without data loss.
- No reloads: Inertia.js router.post for auth/payment, onSuccess advances step client-side.
- Package Details: Enhanced UI with features list (Check icons), SaaS info (annual billing, trial notice, reseller benefits, cancellation policy).
- Error Handling: Backend validation errors (422) displayed via Inertia onError; frontend try-catch for API calls, toasts for user feedback.
- Localization: react-i18next with /public/lang/de/en/marketing.json (e.g., payment options, errors, trial texts).
## Backend Implementation (app/Http/Controllers/Api/PackageController.php)
- Endpoints:
- POST /api/packages: purchase (validates package_id, type, payment_method; handles free/paid).
- POST /api/packages/create-payment-intent: Stripe client_secret for card payments.
- POST /api/packages/complete-purchase: Finalizes after payment (creates PackagePurchase/TenantPackage; supports stripe/paypal provider_id).
- POST /api/packages/paypal-create: Creates PayPal Order (OrdersCreateRequest with custom_id metadata: tenant_id/package_id).
- POST /api/packages/paypal-capture: Captures Order (OrdersCaptureRequest; idempotent check, trial logic if first reseller).
- Free Packages: Direct DB assignment (no payment).
- Paid: Stripe PaymentIntent or PayPal Order; completePurchase in transaction.
- Trial Logic: For reseller_subscription, if no active packages, set expires_at = now()->addDays(14); else full year.
- Multi-Tenancy: Tenant middleware isolates data; metadata includes tenant_id.
## PayPal Integration
- SDK: paypal/paypal-server-sdk (composer require); Sandbox/LiveEnvironment based on config/services.php.
- Flow: Frontend PayPalButtons createOrder (fetch /api/packages/paypal-create) → onApprove captureOrder (fetch /api/packages/paypal-capture) → completePurchase.
- Webhooks: Dedicated PayPalWebhookController.php (route POST /api/paypal/webhook/verify, no auth).
- Verification: VerifyWebhookSignature with headers/webhook_id.
- Events: PAYMENT.CAPTURE.COMPLETED (process idempotent purchase, trial activation), BILLING.SUBSCRIPTION.CANCELLED (deactivate TenantPackage, update status to 'cancelled').
- Idempotency: Check provider_id before processing.
- Config: services.php with client_id/secret/sandbox; webhook_id for verification.
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.
## Stripe Integration
- SDK: stripe/stripe-php; config/services.php secret key.
- Flow: Elements for card input → confirmCardPayment with client_secret → completePurchase on success.
- Subscriptions: For reseller, createSubscription (setup in handlePaidPurchase); webhooks for invoice.paid (renew), customer.subscription.deleted (cancel).
- Metadata: tenant_id/package_id/type for all intents/subscriptions.
- **One-time Payments**: Use Stripe Checkout for endcustomer event packages. Create PaymentIntent via `StripeController@createPaymentIntent`.
- **Subscriptions**: Reseller subscriptions use Stripe Subscriptions API. Webhook handling in `StripeWebhookController@handleWebhook` for events like `invoice.paid`, `customer.subscription.deleted`.
- **Configuration**: Keys in `config/services.php` under `stripe`. Sandbox mode based on `APP_ENV`.
- **Models**: `PackagePurchase` records all transactions with `provider_id` (Stripe PI ID), `status`, `metadata`.
- **Frontend**: PurchaseWizard.tsx handles client-side Stripe Elements for card input and confirmation.
## Error Handling & Security
- Validation: Laravel Requests (e.g., package_id exists, privacy_consent); 422 JSON errors for API.
- Auth: Sanctum for API; middleware('auth:sanctum', 'tenant') on purchase endpoints.
- GDPR: No PII in sessionStorage/metadata; privacy consent checkbox in RegisterForm; logs anonymized (tenant_id only).
- Cancellations: Success step links to provider dashboard (e.g., Stripe Customer Portal, PayPal Manage Subscriptions) based on purchase.provider.
## 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.
- **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`).
- **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.
## 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).
- **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).
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`).
## Error Handling
- Validation: Request validation for IDs, consent.
- API Errors: Catch exceptions, log, return 400/500 JSON.
- Idempotency: Prevent duplicate processing.
- Webhook: Verify signature, handle unhandled events with logging.
## Testing
- Feature Tests: tests/Feature/PurchaseTest.php (unauth redirects, free/paid flows, Stripe/PayPal mocks, errors, trial/renewal logic, idempotency).
- E2E: Playwright for wizard steps (auth without reload, persistence on refresh, payment toggles).
- Coverage: Auth errors (duplicate email, wrong pass), payment failures (no assignment), limits exceeded (403).
- Unit: Mock providers in `PurchaseTest.php` for order creation, capture, webhooks.
- Integration: Sandbox keys for end-to-end. Assertions on DB state, responses.
- Edge Cases: Failures, idempotency, trials, limits.
## Deployment & Ops
- Migrations: Add trial_days to packages if configurable; webhook routes in api.php (without auth middleware).
- Monitoring: Log payment events (success/fail); alert on webhook verification fails.
- Legal: Update Privacy/AGB for payment providers; receipts via email (views/emails/purchase.blade.php).
## 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.
- Retention: Purchases retained per Privacy policy; update on changes.