Files
fotospiel-app/docs/prp/08-billing.md

5.1 KiB

08 — Billing (Packages)

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.

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.

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.

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).

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).