# Packages-Design für Fotospiel ## Überblick Dieses Dokument definiert das neue package-basierte Business Model, das das bestehende Credits-System ersetzt. Packages sind vordefinierte Bündel mit Limits und Features, die als Einmalkäufe pro Event (für Endkunden) oder jährliche Subscriptions (für Reseller/Agenturen) verkauft werden. Das Modell priorisiert Einfachheit: Bei Event-Erstellung wählt der User ein Package, das Limits für diesen Event setzt. Für Reseller limitiert das Tenant-Package die Anzahl Events pro Jahr und globale Features. Ziele: - Ersetze Credits vollständig (keine Balance mehr, sondern Event-spezifische Limits). - Unterstütze Freemium: Free-Paket für Einstieg. - Skalierbar: Endkunden pro Event, Reseller jährlich. - Integration: Stripe für Zahlungen (Einmalkäufe/Subscriptions), Ledger für Transaktionen. ## Endkunden-Pakete (pro Event, Einmalkauf) Diese Pakete werden bei Event-Erstellung ausgewählt und gekauft. Sie definieren Limits für den spezifischen Event (z.B. max_photos, gallery_duration). Preise basierend auf User-Vorschlag. | Paket | Preis | max_photos | max_guests | gallery_days | max_tasks | watermark | branding | Extra Features | |-----------|-------|------------|------------|--------------|-----------|-----------|----------|----------------| | Free/Test | 0 € | 30 | 10 | 3 | 1 | Standard | Nein | - | | Starter | 19 € | 300 | 50 | 14 | 5 | Standard | Nein | - | | Standard | 39 € | 1000 | 150 | 30 | 10 | Custom | Ja | Logo | | Premium | 79 € | 3000 | 500 | 180 | 20 | Kein | Ja | Live-Slideshow, Analytics | ## Reseller/Agentur-Pakete (jährlich, Subscription) Diese Pakete werden auf Tenant-Ebene gekauft und limitieren Events pro Jahr, mit erweiterten Features (z.B. White-Label). Preise als Richtwerte. | Paket | Preis/Jahr | max_events/year | Per-Event Limits | Branding | Extra | |------------|------------|-----------------|------------------|----------|-------| | Reseller S | 149 € | 5 | Standard | Eingeschränkt | - | | Reseller M | 299 € | 15 | Standard | Eigene Logos | 3 Monate Galerie | | Reseller L | 599 € | 40 | Premium | White-Label | - | | Enterprise | ab 999 € | Unlimited | Premium | Voll | Custom Domain, Support | ## Datenbank-Schema ### Globale Packages-Tabelle (für alle Pakete, geteilt) ```php Schema::create('packages', function (Blueprint $table) { $table->id(); $table->string('name'); // z.B. 'Starter', 'Reseller M' $table->string('type'); // 'endcustomer' oder 'reseller' $table->decimal('price', 8, 2); // Preis in EUR $table->integer('max_photos')->nullable(); // Null für Reseller (vererbt an Events) $table->integer('max_guests')->nullable(); $table->integer('gallery_days')->nullable(); $table->integer('max_tasks')->nullable(); $table->boolean('watermark_allowed')->default(true); $table->boolean('branding_allowed')->default(false); $table->integer('max_events_per_year')->nullable(); // Für Reseller $table->timestamp('expires_after')->nullable(); // Für Subscriptions $table->json('features')->nullable(); // z.B. ['live_slideshow', 'analytics'] $table->timestamps(); }); ``` Seeder: Füge die obigen Pakete ein. ### Event-Packages (Zuordnung Event zu Endkunden-Paket) ```php Schema::create('event_packages', function (Blueprint $table) { $table->id(); $table->foreignId('event_id')->constrained()->cascadeOnDelete(); $table->foreignId('package_id')->constrained()->cascadeOnDelete(); $table->decimal('purchased_price', 8, 2); $table->timestamp('purchased_at'); $table->integer('used_photos')->default(0); // Counter für Limits $table->timestamps(); }); ``` - Bei Event-Create: Package auswählen/kaufen, Eintrag erstellen. - Checks: z.B. if ($event->package->used_photos >= $event->package->max_photos) abort(403); ### Tenant-Packages (für Reseller) ```php Schema::create('tenant_packages', function (Blueprint $table) { $table->id(); $table->foreignId('tenant_id')->constrained()->cascadeOnDelete(); $table->foreignId('package_id')->constrained()->cascadeOnDelete(); $table->decimal('price', 8, 2); $table->timestamp('purchased_at'); $table->timestamp('expires_at'); // z.B. +1 Jahr $table->integer('used_events')->default(0); // Counter für max_events_per_year $table->boolean('active')->default(true); $table->timestamps(); }); ``` - Bei Tenant-Registrierung: Free-Reseller oder Upgrade. - Check: if ($tenant->activePackage->used_events >= $tenant->activePackage->max_events_per_year) block new Event. ### Ledger und Purchases (ersetzt Credits-Ledger) ```php Schema::create('package_purchases', function (Blueprint $table) { $table->id(); $table->foreignId('tenant_id')->constrained()->nullable(); // Null für Event-spezifisch $table->foreignId('event_id')->constrained()->nullable(); // Für Endkunden $table->foreignId('package_id')->constrained(); $table->string('stripe_id'); // Für Webhook/Idempotenz $table->decimal('price', 8, 2); $table->string('type'); // 'endcustomer_event', 'reseller_subscription' $table->json('metadata'); // z.B. {'event_id': 123} $table->timestamps(); }); ``` - Kein separater Ledger nötig; Purchases tracken alles. ## Integration und Logik - **Event-Create Flow**: User wählt Package → Stripe-Checkout (Einmalkauf) → Webhook bestätigt → Event mit package_id erstellen. - **Reseller-Upgrade**: Im Admin-Dashboard Package auswählen → Subscription erstellen → expires_at setzen. - **Limits-Checks**: Middleware prüft Event-Package-Limits (Photos/Uploads), Tenant-Package für Event-Anzahl. - **Fallback**: Bestehende Tenants migrieren zu Free-Paket (z.B. if old_credits > 100 → Standard). - **UI/Features**: Wasserzeichen: if (!$package->watermark_allowed) hide; Branding: Custom Logo-Upload if allowed. - **Billing**: Stripe Products für jedes Package; Webhooks updaten Status (z.B. Subscription cancel → active=false). ## Migration von Altem System - Entferne: event_credits_balance aus tenants, event_purchases, event_credits_ledger. - Migriere: Für Events mit Credits → Zuordnen zu Free-Paket; Tenant-Balance → Initial Reseller S. - Skript: Artisan Command `php artisan migrate:packages` für Daten-Transfer. Dieses Design ist final und bereit für Implementierung. Updates via PR.