diff --git a/app/Http/Middleware/RedirectLegacyQrToLadillLink.php b/app/Http/Middleware/RedirectLegacyQrToLadillLink.php new file mode 100644 index 0000000..3e3d799 --- /dev/null +++ b/app/Http/Middleware/RedirectLegacyQrToLadillLink.php @@ -0,0 +1,24 @@ +path(), '/'))) { + return $next($request); + } + + if (LadillLink::isInternalRequest($request)) { + return $next($request); + } + + return LadillLink::legacyRedirect($request); + } +} diff --git a/app/Services/Events/EventRegistrationService.php b/app/Services/Events/EventRegistrationService.php index e965b03..6502a8f 100644 --- a/app/Services/Events/EventRegistrationService.php +++ b/app/Services/Events/EventRegistrationService.php @@ -8,6 +8,7 @@ use App\Services\Billing\BillingClient; use App\Services\Billing\PaystackService; use App\Services\Billing\SmsService; use App\Services\Pay\PayClient; +use App\Support\LadillLink; use Illuminate\Support\Str; use RuntimeException; @@ -125,7 +126,7 @@ class EventRegistrationService 'fee_tier' => $feeTier, 'source_service' => 'events', 'source_ref' => (string) $qrCode->id, - 'callback_url' => route('qr.public.event.callback', ['shortCode' => $qrCode->short_code]), + 'callback_url' => LadillLink::path($qrCode->short_code, 'register/callback'), 'customer_name' => $registration->attendee_name, 'customer_email' => $email, 'customer_phone' => $registration->attendee_phone, diff --git a/app/Support/LadillLink.php b/app/Support/LadillLink.php index 3a51bc0..d29e89e 100644 --- a/app/Support/LadillLink.php +++ b/app/Support/LadillLink.php @@ -2,11 +2,16 @@ namespace App\Support; +use Illuminate\Http\RedirectResponse; +use Illuminate\Http\Request; + /** * Canonical short-link URLs for the Ladill platform (ladl.link). */ final class LadillLink { + public const INTERNAL_HEADER = 'X-Ladill-Internal'; + public static function baseUrl(): string { $domain = (string) config('link.public_domain', 'ladl.link'); @@ -23,4 +28,33 @@ final class LadillLink { return self::url($slug).'/'.ltrim($suffix, '/'); } + + public static function isInternalRequest(Request $request): bool + { + return $request->header(self::INTERNAL_HEADER) === '1'; + } + + public static function legacyRedirect(Request $request): RedirectResponse + { + $shortCode = $request->route('shortCode'); + if (! is_string($shortCode) || $shortCode === '') { + abort(404); + } + + $path = ltrim($request->path(), '/'); + $prefix = 'q/'.$shortCode; + $suffix = ''; + if ($path !== $prefix && str_starts_with($path, $prefix.'/')) { + $suffix = substr($path, strlen($prefix) + 1); + } + + $target = $suffix === '' ? self::url($shortCode) : self::path($shortCode, $suffix); + if ($qs = $request->getQueryString()) { + $target .= '?'.$qs; + } + + $status = in_array($request->method(), ['GET', 'HEAD'], true) ? 301 : 307; + + return redirect()->away($target, $status); + } } diff --git a/bootstrap/app.php b/bootstrap/app.php index 2e2ca96..a32e4a0 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -22,6 +22,7 @@ return Application::configure(basePath: dirname(__DIR__)) ]); $middleware->alias([ 'platform.session' => \App\Http\Middleware\EnsurePlatformSession::class, + 'redirect.legacy.qr' => \App\Http\Middleware\RedirectLegacyQrToLadillLink::class, ]); }) ->withExceptions(function (Exceptions $exceptions): void { diff --git a/routes/web.php b/routes/web.php index 6ef10a8..2b28b6c 100644 --- a/routes/web.php +++ b/routes/web.php @@ -39,6 +39,7 @@ Route::get('/sso/logout-frontchannel', [SsoLoginController::class, 'frontchannel Route::get('/sso/platform-signed-out', [SsoLoginController::class, 'platformSignedOut'])->name('sso.platform-signed-out'); Route::get('/signed-out', fn () => auth()->check() ? redirect()->route('events.dashboard') : view('events.signed-out'))->name('events.signed-out'); +Route::middleware('redirect.legacy.qr')->group(function () { Route::get('/q/{shortCode}', [QrScanController::class, 'resolve'])->name('qr.public.resolve'); Route::get('/q/{shortCode}/view', [QrScanController::class, 'view'])->name('qr.public.view'); Route::get('/q/{shortCode}/event-logo', [QrScanController::class, 'eventLogo'])->name('qr.public.event.logo'); @@ -47,6 +48,7 @@ Route::get('/q/{shortCode}/itinerary-cover', [QrScanController::class, 'itinerar Route::post('/q/{shortCode}/register', [EventRegistrationController::class, 'register'])->name('qr.public.event.register'); Route::get('/q/{shortCode}/register/callback', [EventRegistrationController::class, 'callback'])->name('qr.public.event.callback'); Route::get('/q/{shortCode}/registered/{ref}', [EventRegistrationController::class, 'confirmed'])->name('qr.public.event.confirmed'); +}); Route::middleware(['auth', 'platform.session'])->group(function () { Route::get('/wallet/balance', [WalletBalanceController::class, 'balance'])->name('wallet.balance');