- Wired the checkout wizard for Google “comfort login”: added Socialite controller + dependency, new Google env

hooks in config/services.php/.env.example, and updated wizard steps/controllers to store session payloads,
attach packages, and surface localized success/error states.
- Retooled payment handling for both Stripe and PayPal, adding richer status management in CheckoutController/
PayPalController, fallback flows in the wizard’s PaymentStep.tsx, and fresh feature tests for intent
creation, webhooks, and the wizard CTA.
- Introduced a consent-aware Matomo analytics stack: new consent context, cookie-banner UI, useAnalytics/
useCtaExperiment hooks, and MatomoTracker component, then instrumented marketing pages (Home, Packages,
Checkout) with localized copy and experiment tracking.
- Polished package presentation across marketing UIs by centralizing formatting in PresentsPackages, surfacing
localized description tables/placeholders, tuning badges/layouts, and syncing guest/marketing translations.
- Expanded docs & reference material (docs/prp/*, TODOs, public gallery overview) and added a Playwright smoke
test for the hero CTA while reconciling outstanding checklist items.
This commit is contained in:
Codex Agent
2025-10-19 11:41:03 +02:00
parent ae9b9160ac
commit a949c8d3af
113 changed files with 5169 additions and 712 deletions

View File

@@ -25,6 +25,16 @@ Guest Polling (no WebSockets in v1)
- GET `/events/{token}/photos?since=<ISO8601|cursor>` — incremental gallery refresh.
- Response: `{ data: Photo[], next_cursor?: string, latest_photo_at: ISO8601 }`.
- Use `If-None-Match` or `If-Modified-Since` to return `304 Not Modified` when unchanged.
- Legacy slug-based guest endpoints have been removed; tokens are mandatory for public access.
Webhooks
- Payment provider events, media pipeline status, and deletion callbacks. All signed with shared secret per provider.
- RevenueCat webhook: `POST /api/v1/webhooks/revenuecat` signed via `X-Signature` (HMAC SHA1/256). Dispatches `ProcessRevenueCatWebhook` to credit tenants and sync subscription expiry.
Public Gallery
- `GET /gallery/{token}`: returns event snapshot + branding colors; responds with `410` once the package gallery window expires.
- `GET /gallery/{token}/photos?cursor=&limit=`: cursor-based pagination of approved photos. Response shape `{ data: Photo[], next_cursor: string|null }`.
- `GET /gallery/{token}/photos/{photo}/download`: streams or redirects to an approved original. Returns `404` if the asset is gone.
Tenant Admin Downloads
- `GET /tenant/events/{event}/photos/archive`: authenticated ZIP export of all approved photos for an event. Returns `404` when none exist.

View File

@@ -30,6 +30,7 @@ Core Features
- Masonry grid, lazy-load, pull-to-refresh; open photo lightbox with swipe.
- Like (heart) with optimistic UI; share system sheet (URL to CDN variant).
- Filters: emotion, featured, mine (local-only tag for items uploaded from this device).
- Public share: host can hand out `https://app.domain/g/{token}`; guests see a themed, read-only gallery with per-photo downloads.
- Safety & abuse controls
- Rate limits per device and IP; content-length checks; mime/type sniffing.
- Upload moderation state: pending → approved/hidden; show local status.
@@ -39,6 +40,7 @@ Core Features
Screens
- Splash/Loading: event lookup + token validation; friendly skeleton.
- Slug-based deep links are no longer accepted; guests must enter or scan a join token QR.
- Terms & PIN: legal links, optional PIN input; remember choice per event.
- Gallery: grid of approved photos; toolbar with filter, upload, settings.
- Upload Picker: camera/library, selection preview, emotion/task tagging.

View File

@@ -9,6 +9,15 @@ The Fotospiel platform supports multiple payment providers for package purchases
- **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.
- **Webhook Matrix**:
| Event | Purpose | Internal handler |
| --- | --- | --- |
| `payment_intent.succeeded` | Completes single-event package purchase | `StripeWebhookController@handlePaymentIntentSucceeded` |
| `payment_intent.payment_failed` | Logs failure, triggers recovery emails | `handlePaymentIntentFailed` |
| `invoice.paid` | Confirms subscription renewal, extends package | `handleInvoicePaid` |
| `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.
@@ -17,6 +26,14 @@ The Fotospiel platform supports multiple payment providers for package purchases
- **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`).
- **Webhook Matrix**:
| Event | Purpose | Internal handler |
| --- | --- | --- |
| `PAYMENT.CAPTURE.COMPLETED` | Confirms one-time order, activates tenant package | `handleCapture` |
| `BILLING.SUBSCRIPTION.ACTIVATED`, `BILLING.SUBSCRIPTION.UPDATED` | Syncs reseller subscription status/expiry | `handleSubscription` |
| `BILLING.SUBSCRIPTION.CANCELLED`, `BILLING.SUBSCRIPTION.EXPIRED` | Marks package inactive and downgrades tenant | `handleSubscription` |
| `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.

View File

@@ -6,3 +6,20 @@
- Logging: structured, no PII; add request/trace IDs; redact secrets.
- GDPR: retention settings per tenant; deletion workflows; legal pages managed via CMS-like resource.
- Rate limits: per-tenant, per-user, per-device; protect upload and admin mutations.
## 2025 Hardening Priorities
- **Identity & OAuth***Owner: Backend Platform*
Track JWT key rotation via `oauth:rotate-keys`, roll out dual-key support (old/new KID overlap), surface refresh-token revocation tooling, and extend IP/device binding rules for long-lived sessions.
- **Guest Join Tokens***Owner: Guest Platform*
Hash stored join tokens, add anomaly metrics (usage spikes, stale tokens), and tighten gallery/photo rate limits with visibility in storage dashboards.
- **Public API Resilience***Owner: Core API*
Ensure gallery/download endpoints serve signed URLs, expand abuse throttles (token + IP), and document incident response runbooks in ops guides.
- **Media Pipeline & Storage***Owner: Media Services*
Introduce antivirus + EXIF scrubbing workers, stream uploads to disk to avoid buffering, and enforce checksum verification during hot→archive transfers with configurable alerts from `StorageHealthService`.
- **Payments & Webhooks***Owner: Billing*
Align legacy Stripe hooks with checkout sessions, add idempotency locks/signature expiry checks, and plug failed capture notifications into the credit ledger audit trail.
- **Frontend & CSP***Owner: Marketing Frontend*
Replace unsafe-inline allowances (Stripe/Matomo) with nonce or hashed CSP rules, gate analytics injection behind consent, and localise cookie-banner copy that discloses data sharing.
Progress updates belong in `docs/changes/` and roadmap status in `docs/implementation-roadmap.md`.

View File

@@ -0,0 +1,29 @@
# 11 — Public Guest Gallery
Purpose
- Provide a shareable, mobile-friendly gallery for guests who only need to view and download approved photos.
- Respect existing join-token security and automatically disable access once a packages gallery duration expires.
Access Model
- URL pattern: `https://<app-domain>/g/{joinToken}`. Tokens are the same join tokens tenants already issue; revoking or expiring a token immediately locks the gallery.
- Tokens expire when the associated event packages `gallery_expires_at` passes; guests receive an explanatory message (HTTP `410`).
- Only *approved* photos appear; pending/rejected items remain hidden.
Client Experience
- Responsive grid with lazy-loaded thumbnails (IntersectionObserver) and infinite scroll/pagination.
- Event branding colours (primary, secondary, background) are applied via CSS custom properties fetched from the API.
- Fullscreen lightbox shows creation timestamp + guest label when available and exposes a single-photo download button (streams the original asset).
API Touchpoints (see 03 — API Contract for details)
- `GET /api/v1/gallery/{token}` → event snapshot + branding.
- `GET /api/v1/gallery/{token}/photos` → cursor-based pagination of approved photos.
- `GET /api/v1/gallery/{token}/photos/{photo}/download` → single-photo download/redirect.
Tenant Admin Support
- Filament action “Download all photos” (Event resource) queues a server-side ZIP export via `GET /tenant/events/{event}/photos/archive` for authenticated tenants.
- Only approved photos are included; failed assets are skipped with logging.
Future Enhancements
- Background job + notification for large ZIP exports (current implementation streams synchronously).
- Optional passcode/PIN layered on top of join tokens for sensitive events.
- Aggregate analytics (views/downloads per photo) presented in Tenant Admin dashboards.

View File

@@ -12,6 +12,8 @@
- **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.
- **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`).
## Payment State Machine
State constants live on `CheckoutSession` (`status` column, enum):
@@ -129,6 +131,5 @@ Stripe/PayPal routes remain under `routes/web.php` but call into new service cla
## Open Questions / Follow-Ups
- Map package records to Stripe price ids and PayPal plan ids (store on `packages` table or config?).
- Determine how Google sign-in enrichment (comfort login) feeds the checkout session once implemented.
- Confirm legal copy updates for new checkout experience before GA.
- Align email templates (welcome, receipt) with new assignment service outputs.

View File

@@ -40,6 +40,7 @@ Ziel: Vollständige Migration zu Inertia.js für SPA-ähnliche Konsistenz, mit e
- **Auth-Integration**: usePage().props.auth für CTAs (z.B. Login-Button im Header).
- **Responsive Design**: Tailwind: block md:hidden für Mobile-Carousel in Packages.
- **Fonts & Assets**: Vite-Plugin für Fonts/SVGs; preload in Layout.
- **Analytics (Matomo)**: Aktivierung via `.env` (`MATOMO_ENABLED=true`, `MATOMO_URL`, `MATOMO_SITE_ID`). `AppServiceProvider` teilt die Konfiguration als `analytics.matomo`; `MarketingLayout` rendert `MatomoTracker`, der das Snippet aus `/docs/piwik-trackingcode.txt` nur bei erteilter Analyse-Zustimmung lädt, `disableCookies` setzt und bei jedem Inertia-Navigationsevent `trackPageView` sendet. Ein lokalisierter Consent-Banner (DE/EN) übernimmt die DSGVO-konforme Einwilligung und ist über den Footer erneut erreichbar.
- **Tests**: E2E mit Playwright (z.B. navigate to /packages, check header/footer presence).
### 5. Diagramm: Layout-Struktur
@@ -66,4 +67,4 @@ MarketingLayout.tsx
- Styling-Konflikte: CSS-Isolation mit Tailwind-Prefix.
- Performance: Code-Splitting für große Pages.
Dieser Plan basiert auf bestehender Struktur (docs/prp/ als Referenz). Nach Umsetzung: Update PRP (docs/prp/01-architecture.md).
Dieser Plan basiert auf bestehender Struktur (docs/prp/ als Referenz). Nach Umsetzung: Update PRP (docs/prp/01-architecture.md).

View File

@@ -0,0 +1,28 @@
# Public Entry Points
This overview lists every user-facing URL surface, grouped by persona, and notes authentication/expiry rules.
## Marketing Site
- `/` — marketing landing page.
- `/packages` — package overview.
- `/checkout/{package}` — checkout wizard (requires logged-in tenant or email login within flow).
- `/blog`, `/contact`, `/impressum`, `/datenschutz`, `/agb` — legal and marketing content.
## Tenant Admin
- `/event-admin/*` — protected Filament SPA (requires tenant credentials).
- `/tenant/events/{event}/photos/archive` — authenticated ZIP export for approved photos (tenant ownership enforced).
## Guest PWA (event-bound)
- `/event` — landing for new guests (code entry / QR).
- `/e/{token}` — full guest experience (home, tasks, gallery, upload) gated by join token; token expiry revokes access.
- `/g/{token}` — read-only public gallery (new). Shows approved photos themed by event branding; downloads allowed while token valid and gallery duration active.
- `/setup/{token}` — onboarding/profile setup for guests.
## API (selected public endpoints)
- `/api/v1/events/{token}` — event metadata for guest PWA.
- `/api/v1/events/{token}/photos` — guest gallery polling (legacy PWA).
- `/api/v1/gallery/{token}` — public gallery metadata (new).
- `/api/v1/gallery/{token}/photos` — public gallery pagination (new).
- `/api/v1/gallery/{token}/photos/{photo}/download` — single photo download (new).
All other `/api/v1/*` routes require authenticated tenant or super-admin access as documented in `docs/prp/03-api.md`.

View File

@@ -1,15 +1,22 @@
# Detaillierte PRP für Tenant Admin App (Capacitor + Framework7)
## Status
- **Aktiv**: Erste Version (2025-09-13)
- **Version**: 1.0.0
- **Autor**: Sonoma (AI Architect)
- **Supersedes**: docs/prp/06-tenant-admin-pwa.md (erweitert und detailliert)
- **Aktualisiert**: 2025-10-17 (Onboarding Fusion & QR Revamp)
- **Version**: 1.2.0
- **Autor**: Core Platform Team (Codex)
- **Supersedes**: docs/prp/06-tenant-admin-pwa.md (legacy Framework7 reference)
## Überblick
Diese detaillierte Product Requirement Plan (PRP) beschreibt die Spezifikationen für die Tenant Admin App. Die App ist eine store-ready mobile Anwendung, die mit Capacitor für iOS und Trusted Web Activity (TWA) für Android gepackt wird. Die UI basiert auf Framework7 für ein natives Mobile-Erlebnis. Die App ermöglicht Tenant-Admins (z.B. Event-Organisatoren) die vollständige Verwaltung ihrer Events, Galerien, Mitglieder, Einstellungen und Käufe über eine API-first Backend-Integration.
Die App ersetzt das frühere Filament-basierte Tenant-Panel und fokussiert auf Mobile-First-UX mit Offline-Fähigkeiten, Push-Notifications und sicherer Authentifizierung. Sie respektiert das Multi-Tenancy-Modell und GDPR-Anforderungen.
Die App ersetzt das frühere Filament-basierte Tenant-Panel und fokussiert auf eine Mobile-First-UX mit Offline-Fähigkeiten, Push-Notifications und sicherer Authentifizierung. Sie respektiert das Multi-Tenancy-Modell und GDPR-Anforderungen. Seit Oktober 2025 wird das UI in React 19 + Vite + Tailwind/shadcn/ui umgesetzt; die alte Framework7-Schicht bleibt nur als historische Referenz erhalten.
## 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.
- **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`.
## Kernziele
- **Deliverables**: Voll funktionsfähige App mit CRUD-Operationen für Tenant-Ressourcen (Events, Photos, Tasks, etc.).
@@ -40,4 +47,4 @@ Diese PRP erweitert die knappe Beschreibung in 06-tenant-admin-pwa.md um:
- Detaillierte Settings und Capacitor-Integration.
- Mobile-spezifische Features wie Push-Notifications und Offline-Sync.
Für Feedback oder Änderungen: Siehe TODO.md oder Issues.
Für Feedback oder Änderungen: Siehe TODO.md oder Issues.

View File

@@ -22,6 +22,7 @@ Diese Dokumentation beschreibt alle API-Endpunkte, die die Tenant Admin App mit
- **Redirect URI**: Standardmaessig `${origin}/event-admin/auth/callback` (per Vite-Env anpassbar)
- **Headers**: `Authorization: Bearer {access_token}`
- **Response**: `{ id, email, tenant_id, role, name }`
- **Hinweis**: `client_id` entspricht `VITE_OAUTH_CLIENT_ID`; Seeder `OAuthClientSeeder` synchronisiert Frontend und Backend.
## Dashboard
@@ -260,6 +261,7 @@ curl -H "Authorization: Bearer {token}" \
- **VITE_API_URL**: Backend-API-URL (Pflicht)
- **VITE_OAUTH_CLIENT_ID**: OAuth-Client-ID (Pflicht, muss mit `config/services.php` übereinstimmen der Seeder legt damit den Client in `oauth_clients` an)
- **VITE_REVENUECAT_PUBLIC_KEY**: Optional fuer In-App-Kaeufe (RevenueCat)
- **REVENUECAT_WEBHOOK_SECRET / REVENUECAT_PRODUCT_MAPPINGS / REVENUECAT_APP_USER_PREFIX / REVENUECAT_WEBHOOK_QUEUE**: Backend-Konfiguration für RevenueCat-Webhooks, siehe `config/services.php`.
### Build & Deploy
1. **Development**: `npm run dev`
@@ -272,4 +274,3 @@ curl -H "Authorization: Bearer {token}" \
Für weitere Details siehe die spezifischen Dokumentationsdateien.

View File

@@ -23,12 +23,14 @@ Die Admin-App muss folgende Kernfunktionen bereitstellen:
### Onboarding Journey
- 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.
- Dashboard weist per CTA auf offenes Onboarding hin, bis ein erstes Event erstellt wurde.
### Event Lifecycle
- Erstellung prüft Paketverfügbarkeit; generiert Join-Token.
- Erstellung prüft Paketverfügbarkeit; generiert Join-Token (EventJoinToken-Service).
- QR-Layouts und Token-Rotation erfolgen über `/event-admin/welcome` bzw. das Filament-Panel; slug-basierte QR-Links wurden deaktiviert.
- Bearbeiten erlaubt Statuswechsel, Aufgaben, Emotions, Join-Token-Verwaltung.
- Veröffentlichen schaltet Guest-PWA frei; Archivieren respektiert Retention-Policy.