diff --git a/resources/js/app.js b/resources/js/app.js index e2def61..c39d9f2 100644 --- a/resources/js/app.js +++ b/resources/js/app.js @@ -218,6 +218,9 @@ Alpine.data('miniPaymentLanding', (config = {}) => ({ this.errorMsg = ''; this.loading = true; + // Open a blank checkout window synchronously so Paystack is not blocked after await. + window.LadillPayCheckout?.prepare?.(); + const controller = new AbortController(); const timer = setTimeout(() => controller.abort(), 45000); @@ -238,10 +241,12 @@ Alpine.data('miniPaymentLanding', (config = {}) => ({ ? data.error : (typeof data.message === 'string' ? data.message : ''); if (!res.ok || apiError) { + window.LadillPayCheckout?.cancel?.(); this.errorMsg = apiError || (`Could not start payment (${res.status}). Please try again.`); return; } if (!data.checkout_url) { + window.LadillPayCheckout?.cancel?.(); this.errorMsg = 'Could not start payment. Please try again.'; return; } @@ -253,10 +258,12 @@ Alpine.data('miniPaymentLanding', (config = {}) => ({ this.checkoutUrl = data.checkout_url; this.showSheet = true; } else { + window.LadillPayCheckout?.cancel?.(); window.location.assign(data.checkout_url); return; } } catch (e) { + window.LadillPayCheckout?.cancel?.(); this.errorMsg = e?.name === 'AbortError' ? 'Payment is taking too long. Please try again.' : 'Network error. Please try again.'; diff --git a/resources/views/components/user/service-topup-modal.blade.php b/resources/views/components/user/service-topup-modal.blade.php index acdbd94..65d6938 100644 --- a/resources/views/components/user/service-topup-modal.blade.php +++ b/resources/views/components/user/service-topup-modal.blade.php @@ -39,7 +39,7 @@ if (this.method !== 'paystack') return; event.preventDefault(); - this.loading = true; + this.loading = true; window.LadillPayCheckout?.prepare?.(); this.errorMsg = ''; try { @@ -59,7 +59,7 @@ this.showSheet = true; this.loading = false; } catch (error) { - this.errorMsg = 'Network error. Please try again.'; + window.LadillPayCheckout?.cancel?.(); this.errorMsg = 'Network error. Please try again.'; this.loading = false; } }, diff --git a/resources/views/partials/paystack-sheet.blade.php b/resources/views/partials/paystack-sheet.blade.php index a437868..17afe3c 100644 --- a/resources/views/partials/paystack-sheet.blade.php +++ b/resources/views/partials/paystack-sheet.blade.php @@ -10,8 +10,9 @@ Paystack (checkout.paystack.com) sends X-Frame-Options: SAMEORIGIN and cannot be embedded. Same-origin URLs (e.g. MoMo waiting pages) still load in an iframe. - External checkouts open in a named window, with an in-sheet Continue CTA as a - user-gesture fallback when the popup is blocked after an async fetch. + External checkouts open in a named window. Call LadillPayCheckout.prepare() + synchronously in the Pay click handler (before await fetch) so the popup is not + blocked after the async response. An in-sheet Continue CTA remains as fallback. --}} @php $audience = $audience ?? 'buyer'; @@ -42,6 +43,9 @@ (function () { if (window.LadillPayCheckout) return; + var PENDING_NAME = 'ladill_pay_checkout'; + var pendingWindow = null; + function isFrameable(url) { if (!url) return false; try { @@ -51,12 +55,50 @@ } } + function writePendingPlaceholder(win) { + if (!win) return; + try { + win.document.open(); + win.document.write('
Opening secure checkout…
'); + win.document.close(); + } catch (e) {} + } + + function prepare() { + cancel(); + try { + var win = window.open('about:blank', PENDING_NAME, 'width=480,height=720'); + if (win && !win.closed) { + pendingWindow = win; + writePendingPlaceholder(win); + return win; + } + } catch (e) {} + pendingWindow = null; + return null; + } + + function cancel() { + if (pendingWindow && !pendingWindow.closed) { + try { pendingWindow.close(); } catch (e) {} + } + pendingWindow = null; + } + function openCheckout(url) { if (!url) return null; try { + if (pendingWindow && !pendingWindow.closed) { + pendingWindow.location = url; + var win = pendingWindow; + pendingWindow = null; + return win; + } + pendingWindow = null; // Named window (no noopener) so payment-return can redirect window.opener. - return window.open(url, 'ladill_pay_checkout', 'width=480,height=720'); + return window.open(url, PENDING_NAME, 'width=480,height=720'); } catch (e) { + pendingWindow = null; return null; } } @@ -83,6 +125,7 @@ if (!url) return; this.frameable = isFrameable(url); if (this.frameable) { + cancel(); this.popupBlocked = false; return; } @@ -114,6 +157,8 @@ window.LadillPayCheckout = { isFrameable: isFrameable, + prepare: prepare, + cancel: cancel, open: openCheckout, ensureStore: ensureStore, }; @@ -183,12 +228,12 @@- A Paystack window should be open. If you do not see it, tap continue below. - Your browser blocked the popup. Tap continue to open Paystack. + A Paystack window should be open. If you do not see it, tap continue below. + Your browser blocked the popup. Tap continue to open Paystack.
- A Paystack window should be open. If you do not see it, click continue below. - Your browser blocked the popup. Click continue to open Paystack. + A Paystack window should be open. If you do not see it, click continue below. + Your browser blocked the popup. Click continue to open Paystack.