pull('checkout_google_status'); $googleError = session()->pull('checkout_google_error'); $googleProfile = session()->pull('checkout_google_profile'); $packageOptions = Package::orderBy('price')->get() ->map(fn (Package $pkg) => $this->presentPackage($pkg)) ->values() ->all(); return Inertia::render('marketing/CheckoutWizardPage', [ 'package' => $this->presentPackage($package), 'packageOptions' => $packageOptions, 'privacyHtml' => view('legal.datenschutz-partial')->render(), 'auth' => [ 'user' => Auth::user(), ], 'googleAuth' => [ 'status' => $googleStatus, 'error' => $googleError, 'profile' => $googleProfile, ], 'paddle' => [ 'environment' => config('paddle.environment'), 'client_token' => config('paddle.client_token'), ], ]); } public function register(Request $request) { $validator = Validator::make($request->all(), [ 'email' => 'required|email|unique:users,email', 'username' => 'required|string|max:255|unique:users,username', 'password' => ['required', 'confirmed', Password::defaults()], 'first_name' => 'required|string|max:255', 'last_name' => 'required|string|max:255', 'address' => 'required|string|max:500', 'phone' => 'required|string|max:255', 'package_id' => 'required|exists:packages,id', 'terms' => 'required|accepted', 'privacy_consent' => 'required|accepted', 'locale' => 'nullable|string|max:10', ]); if ($validator->fails()) { return response()->json([ 'errors' => $validator->errors(), ], 422); } $package = Package::findOrFail($request->package_id); $validated = $validator->validated(); $user = DB::transaction(function () use ($request, $package, $validated) { // User erstellen $user = User::create([ 'email' => $request->email, 'username' => $validated['username'], 'first_name' => $validated['first_name'], 'last_name' => $validated['last_name'], 'name' => trim($validated['first_name'].' '.$validated['last_name']), 'address' => $validated['address'], 'phone' => $validated['phone'], 'preferred_locale' => $validated['locale'] ?? null, 'password' => Hash::make($request->password), 'pending_purchase' => true, ]); // Tenant erstellen $tenant = Tenant::create([ 'user_id' => $user->id, '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, 'subscription_tier' => 'free', 'subscription_expires_at' => null, 'settings' => json_encode([ 'branding' => [ 'logo_url' => null, 'primary_color' => '#3B82F6', 'secondary_color' => '#1F2937', 'font_family' => 'Inter, sans-serif', ], 'features' => [ 'photo_likes_enabled' => false, 'event_checklist' => false, 'custom_domain' => false, 'advanced_analytics' => false, ], 'custom_domain' => null, 'contact_email' => $validated['email'], 'event_default_type' => 'general', ]), ]); $user->forceFill(['tenant_id' => $tenant->id])->save(); // Package zuweisen $tenant->packages()->attach($package->id, [ 'price' => $package->price, 'purchased_at' => now(), 'expires_at' => $this->packageIsFree($package) ? now()->addYear() : now()->addYear(), 'active' => $this->packageIsFree($package), // Kostenlose Pakete sofort aktivieren ]); // E-Mail-Verifizierung senden $user->sendEmailVerificationNotification(); // Willkommens-E-Mail senden Mail::to($user) ->locale($user->preferred_locale ?? app()->getLocale()) ->queue(new Welcome($user)); return $user; }); Auth::login($user); $request->session()->put('checkout.pending_package_id', $package->id); $redirectUrl = route('checkout.show', ['package' => $package->id]); $request->session()->put('checkout.verify_redirect', $redirectUrl); $request->session()->put('url.intended', $redirectUrl); return response()->json([ 'success' => true, 'message' => 'Registrierung erfolgreich. Bitte überprüfen Sie Ihre E-Mail zur Verifizierung.', 'redirect' => $redirectUrl, 'user' => [ 'id' => $user->id, 'email' => $user->email, 'name' => $user->name ?? trim($user->first_name.' '.$user->last_name), 'pending_purchase' => $user->pending_purchase ?? true, 'email_verified_at' => $user->email_verified_at, ], 'pending_purchase' => $user->pending_purchase ?? true, ]); } public function login(Request $request) { $validator = Validator::make($request->all(), [ 'identifier' => 'required|string', 'password' => 'required|string', 'remember' => 'boolean', 'locale' => 'nullable|string', ]); if ($validator->fails()) { return response()->json(['errors' => $validator->errors()], 422); } $packageId = $request->session()->get('selected_package_id'); // Custom Auth für Identifier (E-Mail oder Username) $identifier = $request->identifier; $user = User::where('email', $identifier) ->orWhere('username', $identifier) ->first(); if (! $user || ! Hash::check($request->password, $user->password)) { return response()->json([ 'errors' => ['identifier' => ['Ungültige Anmeldedaten.']], ], 422); } Auth::login($user, $request->boolean('remember')); $request->session()->regenerate(); // Checkout-spezifische Logik DB::transaction(function () use ($request, $user, $packageId) { if ($packageId && ! $user->pending_purchase) { $user->update(['pending_purchase' => true]); $request->session()->put('pending_package_id', $packageId); } }); return response()->json([ 'user' => [ 'id' => $user->id, 'email' => $user->email, 'name' => $user->name ?? null, 'pending_purchase' => $user->pending_purchase ?? false, ], 'message' => 'Login erfolgreich', ]); } public function trackAbandonedCheckout(Request $request) { $validated = $request->validate([ 'package_id' => 'required|exists:packages,id', 'email' => 'nullable|email', 'step' => 'nullable|string|in:package,auth,payment,confirmation', 'checkout_state' => 'nullable|array', ]); $user = Auth::user(); if (! $user && ! empty($validated['email'])) { $user = User::where('email', $validated['email'])->first(); } if (! $user) { return response()->json(['status' => 'skipped'], 202); } $package = Package::find($validated['package_id']); if (! $package) { return response()->json(['status' => 'missing_package'], 404); } $stepMap = [ 'package' => 1, 'auth' => 2, 'payment' => 3, 'confirmation' => 4, ]; $lastStep = $stepMap[$validated['step'] ?? 'package'] ?? 1; $checkout = AbandonedCheckout::firstOrNew([ 'user_id' => $user->id, 'package_id' => $package->id, ]); $checkout->email = $user->email; $checkout->checkout_state = $validated['checkout_state'] ?? [ 'step' => $validated['step'] ?? 'package', ]; $checkout->last_step = $lastStep; $checkout->abandoned_at = now(); if (! $checkout->exists || $checkout->converted) { $checkout->reminder_stage = 'none'; $checkout->reminded_at = null; } if (! $checkout->expires_at || $checkout->expires_at->isPast()) { $checkout->expires_at = now()->addDays(30); } $checkout->converted = false; $checkout->save(); return response()->json(['status' => 'tracked']); } private function packageIsFree(Package $package): bool { if (isset($package->is_free)) { return (bool) $package->is_free; } $price = (float) $package->price; return $price <= 0; } }