From 97a30ee6b3ed7d6481be058728c1a44adf4dc2ad Mon Sep 17 00:00:00 2001 From: isaacclad Date: Tue, 21 Jul 2026 22:53:52 +0000 Subject: [PATCH] Fix proxied checkout CSRF for Merchant, Give, Invoice and Events. Public order/pay POSTs hit ladl.link first; satellite CSRF tokens cannot bind to Link (or platform/satellite) sessions, which returned 419 and showed "Could not start checkout." Except public checkout paths and return access_code from owner-gateway Paystack init. Sync contained Paystack sheet. --- .../Payments/MerchantGatewayService.php | 13 +- bootstrap/app.php | 7 + .../views/partials/paystack-sheet.blade.php | 528 ++++++++++++++++-- .../views/public/qr/storefront.blade.php | 3 + 4 files changed, 493 insertions(+), 58 deletions(-) diff --git a/app/Services/Payments/MerchantGatewayService.php b/app/Services/Payments/MerchantGatewayService.php index e795ec8..d972b49 100644 --- a/app/Services/Payments/MerchantGatewayService.php +++ b/app/Services/Payments/MerchantGatewayService.php @@ -47,8 +47,19 @@ class MerchantGatewayService throw new RuntimeException($response->json('message') ?: 'Paystack could not start checkout.'); } + $accessCode = (string) ($response->json('data.access_code') ?? ''); + $checkoutUrl = (string) $response->json('data.authorization_url'); + if ($accessCode === '' && $checkoutUrl !== '') { + $path = ltrim((string) (parse_url($checkoutUrl, PHP_URL_PATH) ?: ''), '/'); + $maybe = explode('/', $path)[0] ?? ''; + if (preg_match('/^[A-Za-z0-9_-]{6,}$/', $maybe) === 1) { + $accessCode = $maybe; + } + } + return [ - 'checkout_url' => (string) $response->json('data.authorization_url'), + 'checkout_url' => $checkoutUrl, + 'access_code' => $accessCode !== '' ? $accessCode : null, 'reference' => (string) ($response->json('data.reference') ?: $reference), 'provider' => PaymentGatewaySetting::PROVIDER_PAYSTACK, ]; diff --git a/bootstrap/app.php b/bootstrap/app.php index a32e4a0..e4c57fd 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -13,6 +13,13 @@ return Application::configure(basePath: dirname(__DIR__)) health: '/up', ) ->withMiddleware(function (Middleware $middleware): void { + // Storefront/booking POSTs are proxied ladl.link → platform → Merchant without + // a browser session cookie that matches merchant CSRF tokens. + $middleware->validateCsrfTokens(except: [ + 'q/*/order', + 'q/*/booking', + ]); + $middleware->redirectGuestsTo(fn (Request $request) => route('sso.connect', [ 'redirect' => $request->fullUrl(), ])); diff --git a/resources/views/partials/paystack-sheet.blade.php b/resources/views/partials/paystack-sheet.blade.php index 4aee647..040a60b 100644 --- a/resources/views/partials/paystack-sheet.blade.php +++ b/resources/views/partials/paystack-sheet.blade.php @@ -44,7 +44,8 @@ @once @endonce +{{-- Override Paystack's full-viewport popup styles so checkout sits in our sheet mount. --}} +@once('ladill-paystack-contain-css') + +@endonce