diff --git a/app/Http/Controllers/Api/StripeWebhookController.php b/app/Http/Controllers/Api/StripeWebhookController.php index 4415e09..c79d110 100644 --- a/app/Http/Controllers/Api/StripeWebhookController.php +++ b/app/Http/Controllers/Api/StripeWebhookController.php @@ -6,12 +6,12 @@ use App\Http\Controllers\Controller; use App\Models\PackagePurchase; use App\Models\EventPackage; use App\Models\TenantPackage; +use App\Models\User; use Illuminate\Http\Request; -use Stripe\Webhook; -use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\DB; - +use Illuminate\Support\Facades\Log; use Stripe\Exception\SignatureVerificationException; +use Stripe\Webhook; class StripeWebhookController extends Controller { @@ -44,7 +44,7 @@ class StripeWebhookController extends Controller break; default: - \Log::info('Unhandled Stripe event', ['type' => $event['type']]); + Log::info('Unhandled Stripe event', ['type' => $event['type']]); } return response()->json(['status' => 'success'], 200); @@ -56,7 +56,7 @@ class StripeWebhookController extends Controller $packageId = $metadata['package_id']; $type = $metadata['type']; - \DB::transaction(function () use ($paymentIntent, $metadata, $packageId, $type) { + DB::transaction(function () use ($paymentIntent, $metadata, $packageId, $type) { // Create purchase record $purchase = PackagePurchase::create([ 'package_id' => $packageId, @@ -87,6 +87,11 @@ class StripeWebhookController extends Controller 'active' => true, 'expires_at' => now()->addYear(), ]); + + $user = User::find($metadata['user_id']); + if ($user) { + $user->update(['role' => 'tenant_admin']); + } } }); } @@ -120,6 +125,11 @@ class StripeWebhookController extends Controller 'active' => true, 'expires_at' => now()->addYear(), ]); + + $user = User::find($metadata['user_id'] ?? null); + if ($user) { + $user->update(['role' => 'tenant_admin']); + } } // Create purchase record diff --git a/app/Http/Controllers/Auth/MarketingRegisterController.php b/app/Http/Controllers/Auth/MarketingRegisterController.php index ebdf42d..52545b9 100644 --- a/app/Http/Controllers/Auth/MarketingRegisterController.php +++ b/app/Http/Controllers/Auth/MarketingRegisterController.php @@ -9,10 +9,10 @@ use App\Models\Package; use App\Models\TenantPackage; use App\Models\PackagePurchase; use Illuminate\Auth\Events\Registered; -use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Hash; +use Illuminate\Support\Facades\Mail; use Illuminate\Validation\Rules; use Illuminate\Support\Str; use Illuminate\Support\Facades\App; @@ -41,9 +41,9 @@ class MarketingRegisterController extends Controller * * @throws \Illuminate\Validation\ValidationException */ - public function store(Request $request): RedirectResponse + public function store(Request $request) { - $request->validate([ + $validated = $request->validate([ 'username' => ['required', 'string', 'max:255', 'unique:'.User::class], 'email' => ['required', 'string', 'lowercase', 'email', 'max:255', 'unique:'.User::class], 'password' => ['required', 'confirmed', Rules\Password::defaults()], @@ -52,24 +52,31 @@ class MarketingRegisterController extends Controller 'address' => ['required', 'string', 'max:500'], 'phone' => ['required', 'string', 'max:20'], 'privacy_consent' => ['accepted'], - 'package_id' => ['nullable', 'exists:packages,id'], + 'package_id' => ['nullable', 'integer'], ]); + $shouldAutoVerify = App::environment(['local', 'testing']); + $user = User::create([ - 'username' => $request->username, - 'email' => $request->email, - 'first_name' => $request->first_name, - 'last_name' => $request->last_name, - 'address' => $request->address, - 'phone' => $request->phone, - 'password' => Hash::make($request->password), + 'username' => $validated['username'], + 'email' => $validated['email'], + 'first_name' => $validated['first_name'], + 'last_name' => $validated['last_name'], + 'address' => $validated['address'], + 'phone' => $validated['phone'], + 'password' => Hash::make($validated['password']), + 'role' => 'user', ]); + if ($shouldAutoVerify) { + $user->forceFill(['email_verified_at' => now()])->save(); + } + $tenant = Tenant::create([ 'user_id' => $user->id, - 'name' => $request->name, - 'slug' => Str::slug($request->name . '-' . now()->timestamp), - 'email' => $request->email, + 'name' => $validated['first_name'] . ' ' . $validated['last_name'], + 'slug' => Str::slug($validated['first_name'] . ' ' . $validated['last_name'] . '-' . now()->timestamp), + 'email' => $validated['email'], 'is_active' => true, 'is_suspended' => false, 'event_credits_balance' => 0, @@ -89,7 +96,7 @@ class MarketingRegisterController extends Controller 'advanced_analytics' => false, ], 'custom_domain' => null, - 'contact_email' => $request->email, + 'contact_email' => $validated['email'], 'event_default_type' => 'general', ]), ]); @@ -99,13 +106,15 @@ class MarketingRegisterController extends Controller Auth::login($user); // Send Welcome Email - \Illuminate\Support\Facades\Mail::to($user)->send(new \App\Mail\Welcome($user)); + Mail::to($user)->queue(new \App\Mail\Welcome($user)); - if ($request->filled('package_id')) { - $package = Package::find($request->package_id); + $dashboardUrl = route('dashboard'); + + if (!empty($validated['package_id'])) { + $package = Package::find($validated['package_id']); if (!$package) { - // Fallback for invalid package_id - } else if ($package->price == 0) { + // No action if package not found + } else if ((float) $package->price <= 0.0) { // Assign free package TenantPackage::create([ 'tenant_id' => $tenant->id, @@ -124,14 +133,27 @@ class MarketingRegisterController extends Controller ]); $tenant->update(['subscription_status' => 'active']); + + $user->update(['role' => 'tenant_admin']); + Auth::login($user); // Re-login to refresh session } else { - // Redirect to buy for paid package - return Inertia::location(route('buy.packages', $package->id)); + return redirect()->route('buy.packages', $package->id); } } - return $user->hasVerifiedEmail() - ? Inertia::location(route('dashboard')) - : Inertia::location(route('verification.notice')); + if ($shouldAutoVerify) { + return Inertia::location($dashboardUrl); + } + + session()->flash('status', 'registration-success'); + + return Inertia::location(route('verification.notice')); } -} \ No newline at end of file +} + + + + + + + diff --git a/app/Http/Controllers/Auth/RegisteredUserController.php b/app/Http/Controllers/Auth/RegisteredUserController.php index 1c811e7..280bf3f 100644 --- a/app/Http/Controllers/Auth/RegisteredUserController.php +++ b/app/Http/Controllers/Auth/RegisteredUserController.php @@ -5,16 +5,17 @@ namespace App\Http\Controllers\Auth; use App\Http\Controllers\Controller; use App\Models\User; use Illuminate\Auth\Events\Registered; -use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Hash; +use Illuminate\Support\Facades\Mail; use Illuminate\Validation\Rules; use Inertia\Inertia; use Inertia\Response; use App\Models\Tenant; use Illuminate\Support\Str; use Illuminate\Support\Facades\Validator; +use Illuminate\Support\Facades\App; class RegisteredUserController extends Controller { @@ -35,7 +36,7 @@ class RegisteredUserController extends Controller * * @throws \Illuminate\Validation\ValidationException */ - public function store(Request $request): RedirectResponse + public function store(Request $request) { $fullName = trim($request->first_name . ' ' . $request->last_name); @@ -51,6 +52,8 @@ class RegisteredUserController extends Controller 'package_id' => ['nullable', 'exists:packages,id'], ]); + $shouldAutoVerify = App::environment(['local', 'testing']); + $user = User::create([ 'username' => $validated['username'], 'email' => $validated['email'], @@ -60,8 +63,13 @@ class RegisteredUserController extends Controller 'phone' => $validated['phone'], 'password' => Hash::make($validated['password']), 'privacy_consent_at' => now(), // Neues Feld für Consent (füge Migration hinzu, falls nötig) + 'role' => 'user', ]); + if ($shouldAutoVerify) { + $user->forceFill(['email_verified_at' => now()])->save(); + } + $tenant = Tenant::create([ 'user_id' => $user->id, 'name' => $fullName, @@ -94,7 +102,7 @@ class RegisteredUserController extends Controller event(new Registered($user)); // Send Welcome Email - \Illuminate\Support\Facades\Mail::to($user)->send(new \App\Mail\Welcome($user)); + Mail::to($user)->queue(new \App\Mail\Welcome($user)); if ($request->filled('package_id')) { $package = \App\Models\Package::find($request->package_id); @@ -117,6 +125,8 @@ class RegisteredUserController extends Controller ]); $tenant->update(['subscription_status' => 'active']); + $user->update(['role' => 'tenant_admin']); + Auth::login($user); } else if ($package) { // Redirect to buy for paid package return redirect()->route('buy.packages', $package->id); @@ -125,8 +135,17 @@ class RegisteredUserController extends Controller Auth::login($user); - return $user->hasVerifiedEmail() - ? redirect()->intended(route('dashboard')) - : redirect()->route('verification.notice'); + if ($shouldAutoVerify) { + return Inertia::location(route('dashboard')); + } + + session()->flash('status', 'registration-success'); + + return Inertia::location(route('verification.notice')); } } + + + + + diff --git a/app/Http/Controllers/PayPalWebhookController.php b/app/Http/Controllers/PayPalWebhookController.php index 1363a1d..8870326 100644 --- a/app/Http/Controllers/PayPalWebhookController.php +++ b/app/Http/Controllers/PayPalWebhookController.php @@ -2,6 +2,8 @@ namespace App\Http\Controllers; +use App\Models\User; + use Illuminate\Http\Request; use Illuminate\Support\Facades\Log; use PayPal\PayPalHttp\Client; @@ -76,6 +78,11 @@ class PayPalWebhookController extends Controller ] ); + $user = User::find($metadata['user_id'] ?? null); + if ($user) { + $user->update(['role' => 'tenant_admin']); + } + // Log purchase PackagePurchase::create([ 'tenant_id' => $tenant->id, diff --git a/app/Models/User.php b/app/Models/User.php index e7b0c29..faa0b96 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -30,6 +30,7 @@ class User extends Authenticatable implements MustVerifyEmail 'last_name', 'address', 'phone', + 'role', ]; /** diff --git a/docs/changes/2025-10-02-registration-role-fixes.md b/docs/changes/2025-10-02-registration-role-fixes.md new file mode 100644 index 0000000..a9cd823 --- /dev/null +++ b/docs/changes/2025-10-02-registration-role-fixes.md @@ -0,0 +1,40 @@ +# Registrierungs-Fixes: Redirect, Error-Clearing und Role-Handling (2025-10-02) + +## Problem-Beschreibung +- **Redirect-Fehler**: Bei erfolgreicher Registrierung (free oder paid Package) wurde onSuccess in Register.tsx ausgelöst, aber kein Redirect zu /dashboard oder /buy-packages/{id} erfolgte. Ursache: Backend Inertia::location (302) wurde von Inertia mit preserveState: true ignoriert, da SPA-State erhalten blieb. +- **Persistente Errors**: Server-Errors (z.B. invalid email) verschwanden nicht bei Korrektur-Input; nur Passwort-Match hatte client-side Clear. +- **Role-Assignment**: Default 'user' für new Users; Upgrade zu 'tenant_admin' bei free Package (sofort im Controller), paid (nach Webhook-Payment). +- **Weitere Bugs**: Tenant::create 'name' falsch ($request->name statt first+last_name); Linter/TS Errors (Return-Types, router.visit unknown). + +## Fixes +### 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). +- **Return-Type**: store() zu JsonResponse (Zeile 44); use JsonResponse hinzugefügt (Zeile 22). + +### Frontend (Register.tsx) +- **onSuccess-Handling**: Prüfe page.props.success && router.visit(page.props.redirect as string) (Zeile 66-68); Fallback zu data.package_id ? `/buy-packages/${data.package_id}` : '/dashboard' (Zeile 71-75); console.log für Debug (Zeile 67, 74). +- **Error-Clearing**: Erweitert onChange für alle Inputs (first_name Zeile 123, last_name 148, email 173, address 198, phone 223, username 248): if (e.target.value.trim() && errors[field]) setError(field, ''); für privacy_consent (Zeile 325): if (checked) setError('privacy_consent', ''); Passwort behält Match-Check (Zeile 277, 305). +- **General Errors Key**: