From de6e6f533140d352111d881e14414b1b65bc82f4 Mon Sep 17 00:00:00 2001 From: isaacclad Date: Tue, 21 Jul 2026 21:43:42 +0000 Subject: [PATCH] Fix Mini payment bottomsheet wrap and Inline launch. Always show the mobile bottomsheet/desktop modal shell, extract access_code from checkout URLs when needed, and open the sheet immediately on Pay. --- resources/js/app.js | 29 ++- .../views/partials/paystack-sheet.blade.php | 202 +++++++++--------- tests/Feature/MiniPaymentCheckoutTest.php | 2 +- tests/Feature/ResponsivePaystackSheetTest.php | 8 +- 4 files changed, 128 insertions(+), 113 deletions(-) diff --git a/resources/js/app.js b/resources/js/app.js index 208402a..a6bc8de 100644 --- a/resources/js/app.js +++ b/resources/js/app.js @@ -231,6 +231,21 @@ Alpine.data('miniPaymentLanding', (config = {}) => ({ } }, + accessCodeFromCheckoutUrl(url) { + if (!url) return ''; + try { + const u = new URL(url, window.location.href); + const host = u.hostname.toLowerCase(); + if (host !== 'checkout.paystack.com' && !host.endsWith('.paystack.com')) { + return ''; + } + const code = u.pathname.replace(/^\/+/, '').split('/')[0] || ''; + return /^[A-Za-z0-9_-]{6,}$/.test(code) ? code : ''; + } catch (e) { + return ''; + } + }, + openInlineCheckout(data = {}) { // Never navigate to checkout.paystack.com. Same-origin MoMo waiting can navigate. if (data.provider === 'mtn_momo' && this.isSameOrigin(data.checkout_url)) { @@ -238,8 +253,14 @@ Alpine.data('miniPaymentLanding', (config = {}) => ({ return false; } - this.checkoutUrl = data.checkout_url || ''; - this.accessCode = data.access_code || ''; + const checkoutUrl = data.checkout_url || ''; + let accessCode = data.access_code || ''; + if (!accessCode) { + accessCode = this.accessCodeFromCheckoutUrl(checkoutUrl); + } + + this.checkoutUrl = checkoutUrl; + this.accessCode = accessCode; this.publicKey = data.public_key || ''; this.returnUrl = data.callback_url || ''; this.showSheet = true; @@ -259,8 +280,8 @@ Alpine.data('miniPaymentLanding', (config = {}) => ({ this.accessCode = ''; this.publicKey = ''; this.returnUrl = ''; - // Keep sheet closed until we have a checkout payload so Inline syncs once with access_code. - this.showSheet = false; + // Open bottomsheet/modal immediately so payment feels in-app while checkout starts. + this.showSheet = true; const controller = new AbortController(); const timer = setTimeout(() => controller.abort(), 45000); diff --git a/resources/views/partials/paystack-sheet.blade.php b/resources/views/partials/paystack-sheet.blade.php index e5dfd9a..ecdef88 100644 --- a/resources/views/partials/paystack-sheet.blade.php +++ b/resources/views/partials/paystack-sheet.blade.php @@ -296,124 +296,118 @@ ); } " - @keydown.escape.window="if (showSheet && $store.ladillPayCheckout && ($store.ladillPayCheckout.frameable || $store.ladillPayCheckout.error || $store.ladillPayCheckout.launching)) showSheet = false" + @keydown.escape.window="if (showSheet) showSheet = false" @ladill-pay-cancelled.window="showSheet = false" - class="fixed inset-0 z-[9999]" - :class="($store.ladillPayCheckout && $store.ladillPayCheckout.inline && !$store.ladillPayCheckout.launching && !$store.ladillPayCheckout.error && !$store.ladillPayCheckout.frameable) ? 'pointer-events-none' : ''" + class="fixed inset-0 z-[9999] flex items-end justify-center md:items-center md:p-6" role="dialog" aria-modal="true" :aria-hidden="(!showSheet).toString()" aria-label="{{ $sheetAria }}"> - {{-- Brief loader only — then Paystack Inline is the sole payment UI (avoids double modal). --}} -
-
- -

Opening payment…

-
+ x-transition:leave-end="opacity-0" + @click="showSheet = false; window.LadillPayCheckout?.cancel?.()">
- {{-- Full Ladill shell only for same-origin iframe (e.g. MoMo waiting) or recovery errors. --}} -
-
-
- -
-
- -
-
-

{{ $sheetTitle }}

- @if (! empty($sheetSubtitle)) -

{{ $sheetSubtitle }}

- @endif -
- + {{-- Always wrap payment: bottom sheet on mobile, centered modal on desktop. --}} +
+
+ +
+
+

{{ $sheetTitle }}

+ @if (! empty($sheetSubtitle)) +

{{ $sheetSubtitle }}

+ @endif
+
-
- - -
- -
-

Checkout unavailable

-

-
- - -
-
- @if (! empty($sheetFooter)) -

- {{ $sheetFooter }} -

- @endif
+
+ + +
+ +
+

+

+
+ + +
+
+ @if (! empty($sheetFooter)) +

+ {{ $sheetFooter }} +

+ @endif
diff --git a/tests/Feature/MiniPaymentCheckoutTest.php b/tests/Feature/MiniPaymentCheckoutTest.php index 9c50346..61e1d7e 100644 --- a/tests/Feature/MiniPaymentCheckoutTest.php +++ b/tests/Feature/MiniPaymentCheckoutTest.php @@ -44,7 +44,7 @@ class MiniPaymentCheckoutTest extends TestCase ->assertSee('md:items-center', false) ->assertSee('Complete your payment', false) ->assertSee('data-ladill-pay-sheet', false) - ->assertSee('data-ladill-pay-loading', false) + ->assertSee('data-ladill-pay-panel', false) ->assertSee('resumeTransaction', false) ->assertSee('/q/'.$qr->short_code.'/pay', false) ->assertDontSee('https://ladl.link/'.$qr->short_code.'/pay', false) diff --git a/tests/Feature/ResponsivePaystackSheetTest.php b/tests/Feature/ResponsivePaystackSheetTest.php index 18da9d0..9cf6dd0 100644 --- a/tests/Feature/ResponsivePaystackSheetTest.php +++ b/tests/Feature/ResponsivePaystackSheetTest.php @@ -28,13 +28,13 @@ class ResponsivePaystackSheetTest extends TestCase $this->assertStringContainsString('LadillPayCheckout', $html); $this->assertStringContainsString('resumeTransaction', $html); $this->assertStringContainsString('js.paystack.co/v2/inline.js', $html); - $this->assertStringContainsString('Opening payment', $html); - $this->assertStringContainsString('data-ladill-pay-loading', $html); - $this->assertStringContainsString('pointer-events-none', $html); + $this->assertStringContainsString('Starting secure checkout', $html); + $this->assertStringContainsString('Complete payment on this screen', $html); + $this->assertStringContainsString('not in a separate browser', $html); + $this->assertStringContainsString('items-end justify-center md:items-center', $html); // Must not push Paystack to a separate browser as the primary path. $this->assertStringNotContainsString('Continue to Paystack', $html); $this->assertStringNotContainsString('window.open(', $html); - $this->assertStringNotContainsString('Complete payment in the Paystack window', $html); // Must not use Tailwind `hidden` + x-show on the panel (desktop stays invisible). $this->assertStringNotContainsString('hidden w-full max-w-lg', $html); $this->assertStringNotContainsString('Paystack checkout', $html);