nicer package layout, also in checkout step 1, fixed missing registration language strings, registration error handling, email verification redirect, email verification error handling and messaging,
This commit is contained in:
@@ -9,6 +9,7 @@ use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||
use Illuminate\Http\Client\ConnectionException as HttpClientConnectionException;
|
||||
use Illuminate\Queue\InvalidQueueException;
|
||||
use Illuminate\Queue\MaxAttemptsExceededException;
|
||||
use Illuminate\Routing\Exceptions\InvalidSignatureException;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use League\Flysystem\FilesystemException;
|
||||
use PDOException;
|
||||
@@ -70,6 +71,16 @@ class Handler extends ExceptionHandler
|
||||
}
|
||||
}
|
||||
|
||||
if ($e instanceof InvalidSignatureException && ! $request->expectsJson()) {
|
||||
$request->session()->flash('verification', [
|
||||
'status' => 'error',
|
||||
'title' => __('auth.verification.expired_title'),
|
||||
'message' => __('auth.verification.expired_message'),
|
||||
]);
|
||||
|
||||
return redirect()->route('verification.notice');
|
||||
}
|
||||
|
||||
if (! $request->expectsJson() && ! $request->inertia()) {
|
||||
if ($hintKey = $this->resolveServerErrorHint($e)) {
|
||||
$request->attributes->set('serverErrorHint', __($hintKey, [], app()->getLocale()));
|
||||
|
||||
@@ -15,8 +15,18 @@ class EmailVerificationPromptController extends Controller
|
||||
*/
|
||||
public function __invoke(Request $request): Response|RedirectResponse
|
||||
{
|
||||
return $request->user()->hasVerifiedEmail()
|
||||
? redirect()->intended(route('dashboard', absolute: false))
|
||||
: Inertia::render('auth/verify-email', ['status' => $request->session()->get('status')]);
|
||||
if ($request->user()->hasVerifiedEmail()) {
|
||||
$redirectToCheckout = $request->session()->pull('checkout.verify_redirect');
|
||||
|
||||
if ($redirectToCheckout) {
|
||||
$separator = str_contains($redirectToCheckout, '?') ? '&' : '?';
|
||||
|
||||
return redirect()->to($redirectToCheckout.$separator.'verified=1');
|
||||
}
|
||||
|
||||
return redirect()->intended(route('dashboard', absolute: false));
|
||||
}
|
||||
|
||||
return Inertia::render('auth/verify-email', ['status' => $request->session()->get('status')]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,12 +13,61 @@ class VerifyEmailController extends Controller
|
||||
*/
|
||||
public function __invoke(EmailVerificationRequest $request): RedirectResponse
|
||||
{
|
||||
if ($request->user()->hasVerifiedEmail()) {
|
||||
return redirect()->intended(route('dashboard', absolute: false).'?verified=1');
|
||||
if (! $request->user()->hasVerifiedEmail()) {
|
||||
$request->fulfill();
|
||||
}
|
||||
|
||||
$request->fulfill();
|
||||
return $this->redirectAfterVerification($request);
|
||||
}
|
||||
|
||||
return redirect()->intended(route('dashboard', absolute: false).'?verified=1');
|
||||
protected function redirectAfterVerification(EmailVerificationRequest $request): RedirectResponse
|
||||
{
|
||||
$redirectToCheckout = $request->session()->pull('checkout.verify_redirect');
|
||||
|
||||
if (! $redirectToCheckout && $request->user()->pending_purchase) {
|
||||
$packageId = $request->session()->pull('checkout.pending_package_id');
|
||||
|
||||
if (! $packageId) {
|
||||
$packageId = optional($request->user()->tenant)
|
||||
?->packages()
|
||||
->latest('tenant_packages.created_at')
|
||||
->value('packages.id');
|
||||
}
|
||||
|
||||
if ($packageId) {
|
||||
$redirectToCheckout = route('checkout.show', ['package' => $packageId]);
|
||||
}
|
||||
}
|
||||
|
||||
$this->flashVerificationSuccess($request, (bool) $redirectToCheckout);
|
||||
|
||||
if ($redirectToCheckout) {
|
||||
$request->session()->forget('checkout.pending_package_id');
|
||||
$separator = str_contains($redirectToCheckout, '?') ? '&' : '?';
|
||||
|
||||
return redirect()->to($redirectToCheckout.$separator.'verified=1');
|
||||
}
|
||||
|
||||
$fallbackLogin = route('marketing.login');
|
||||
$separator = str_contains($fallbackLogin, '?') ? '&' : '?';
|
||||
|
||||
return redirect()->intended($fallbackLogin.$separator.'verified=1');
|
||||
}
|
||||
|
||||
private function flashVerificationSuccess(EmailVerificationRequest $request, bool $forCheckout): void
|
||||
{
|
||||
$message = $forCheckout
|
||||
? __('auth.verification.checkout_success_message')
|
||||
: __('auth.verification.success_message');
|
||||
|
||||
$request->session()->flash('verification', [
|
||||
'status' => 'success',
|
||||
'title' => __('auth.verification.success_title'),
|
||||
'message' => $message,
|
||||
]);
|
||||
|
||||
if (! $forCheckout) {
|
||||
$request->session()->flash('status', __('auth.verification.success_message'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ class CheckoutController extends Controller
|
||||
|
||||
$package = Package::findOrFail($request->package_id);
|
||||
$validated = $validator->validated();
|
||||
DB::transaction(function () use ($request, $package, $validated) {
|
||||
$user = DB::transaction(function () use ($request, $package, $validated) {
|
||||
|
||||
// User erstellen
|
||||
$user = User::create([
|
||||
@@ -138,10 +138,28 @@ class CheckoutController extends Controller
|
||||
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,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -48,13 +48,13 @@ class Kernel extends HttpKernel
|
||||
];
|
||||
|
||||
/**
|
||||
* The application's route middleware.
|
||||
* The application's middleware aliases.
|
||||
*
|
||||
* These middleware may be assigned to groups or used individually.
|
||||
*
|
||||
* @var array<string, class-string|string>
|
||||
*/
|
||||
protected $routeMiddleware = [
|
||||
protected $middlewareAliases = [
|
||||
'auth' => \App\Http\Middleware\Authenticate::class,
|
||||
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
|
||||
'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class,
|
||||
|
||||
@@ -69,6 +69,9 @@ class HandleInertiaRequests extends Middleware
|
||||
'profile' => __('profile'),
|
||||
'dashboard' => __('dashboard'),
|
||||
],
|
||||
'flash' => [
|
||||
'verification' => fn () => $request->session()->get('verification'),
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
29
app/Http/Middleware/NormalizeSignedUrlParameters.php
Normal file
29
app/Http/Middleware/NormalizeSignedUrlParameters.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class NormalizeSignedUrlParameters
|
||||
{
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
$queryString = $request->server->get('QUERY_STRING');
|
||||
|
||||
if (is_string($queryString) && str_contains($queryString, '&')) {
|
||||
$normalized = str_replace('&', '&', $queryString);
|
||||
|
||||
if ($normalized !== $queryString) {
|
||||
$request->server->set('QUERY_STRING', $normalized);
|
||||
parse_str($normalized, $params);
|
||||
|
||||
if (is_array($params) && ! empty($params)) {
|
||||
$request->query->replace(array_merge($request->query->all(), $params));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
33
app/Http/Middleware/ValidateSignature.php
Normal file
33
app/Http/Middleware/ValidateSignature.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Routing\Exceptions\InvalidSignatureException;
|
||||
use Illuminate\Routing\Middleware\ValidateSignature as BaseValidateSignature;
|
||||
|
||||
class ValidateSignature extends BaseValidateSignature
|
||||
{
|
||||
public function handle($request, Closure $next, ...$args)
|
||||
{
|
||||
try {
|
||||
return parent::handle($request, $next, ...$args);
|
||||
} catch (InvalidSignatureException $exception) {
|
||||
if ($request->expectsJson()) {
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
if ($request->routeIs('verification.verify')) {
|
||||
$request->session()->flash('verification', [
|
||||
'status' => 'error',
|
||||
'title' => __('auth.verification.expired_title'),
|
||||
'message' => __('auth.verification.expired_message'),
|
||||
]);
|
||||
|
||||
return redirect()->route('verification.notice');
|
||||
}
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,8 +7,10 @@ use App\Models\Tenant;
|
||||
use App\Models\User;
|
||||
use App\Policies\PurchaseHistoryPolicy;
|
||||
use App\Policies\TenantPolicy;
|
||||
use Illuminate\Auth\Notifications\VerifyEmail;
|
||||
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
|
||||
class AuthServiceProvider extends ServiceProvider
|
||||
{
|
||||
@@ -29,6 +31,20 @@ class AuthServiceProvider extends ServiceProvider
|
||||
{
|
||||
$this->registerPolicies();
|
||||
|
||||
VerifyEmail::createUrlUsing(function (User $notifiable): string {
|
||||
$relativeUrl = URL::temporarySignedRoute(
|
||||
'verification.verify',
|
||||
now()->addMinutes((int) config('auth.verification.expire', 60)),
|
||||
[
|
||||
'id' => $notifiable->getKey(),
|
||||
'hash' => sha1($notifiable->getEmailForVerification()),
|
||||
],
|
||||
absolute: false,
|
||||
);
|
||||
|
||||
return URL::to($relativeUrl);
|
||||
});
|
||||
|
||||
Gate::before(function (User $user): ?bool {
|
||||
return $user->role === 'super_admin' ? true : null;
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user