Collect MoMo MSISDN and open waiting page on Pay.
Deploy Ladill Mini / deploy (push) Successful in 42s

Payment QR was still Paystack-only: no customer_phone and mobile iframe sheet. Require MoMo number, pass it to Ladill Pay, and full-page redirect for mtn_momo waiting URLs.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
isaacclad
2026-07-13 22:52:52 +00:00
co-authored by Cursor
parent de6fa9b3eb
commit 53a83b84c5
6 changed files with 92 additions and 26 deletions
+17 -4
View File
@@ -159,6 +159,7 @@ function mobileKeyboardBottomOffset() {
Alpine.data('miniPaymentLanding', (config = {}) => ({
amount: config.amount ?? '',
phone: config.phone ?? '',
loading: false,
errorMsg: config.errorMsg ?? '',
showSheet: false,
@@ -214,6 +215,12 @@ Alpine.data('miniPaymentLanding', (config = {}) => ({
return;
}
const phone = String(this.phone || '').trim();
if (!phone) {
this.errorMsg = 'Enter your MoMo number to pay.';
return;
}
this.errorMsg = '';
this.loading = true;
@@ -226,11 +233,14 @@ Alpine.data('miniPaymentLanding', (config = {}) => ({
'X-CSRF-TOKEN': config.csrf,
'X-Requested-With': 'XMLHttpRequest',
},
body: JSON.stringify({ amount: value }),
body: JSON.stringify({ amount: value, customer_phone: phone }),
});
const data = await res.json().catch(() => ({}));
if (!res.ok || data.error || data.message) {
this.errorMsg = data.error || data.message || 'Could not start payment. Please try again.';
const apiError = typeof data.error === 'string'
? data.error
: (typeof data.message === 'string' ? data.message : '');
if (!res.ok || apiError) {
this.errorMsg = apiError || 'Could not start payment. Please try again.';
this.loading = false;
return;
}
@@ -239,7 +249,10 @@ Alpine.data('miniPaymentLanding', (config = {}) => ({
this.loading = false;
return;
}
if (window.innerWidth < 768) {
// MoMo waiting page must be full-page (prompt + status poll), not the Paystack iframe sheet.
const useSheet = data.provider !== 'mtn_momo' && window.innerWidth < 768;
if (useSheet) {
this.checkoutUrl = data.checkout_url;
this.showSheet = true;
this.loading = false;
+13 -8
View File
@@ -358,12 +358,13 @@
});
const data = await res.json();
if (data.error) { this.errorMsg = data.error; this.loading = false; return; }
if (window.innerWidth < 768) {
if (!data.checkout_url) { this.errorMsg = 'Could not start payment.'; this.loading = false; return; }
if (data.provider === 'mtn_momo' || window.innerWidth >= 768) {
window.location.href = data.checkout_url;
} else {
this.checkoutUrl = data.checkout_url;
this.showSheet = true;
this.loading = false;
} else {
window.location.href = data.checkout_url;
}
} catch(e) {
this.errorMsg = 'Network error. Please try again.';
@@ -671,8 +672,11 @@
const data = await res.json();
if (data.error) { this.errorMsg = data.error; this.loading = false; return; }
if (!data.paid) { window.location.href = data.success_url; return; }
if (window.innerWidth < 768) { this.checkoutUrl = data.checkout_url; this.showSheet = true; this.loading = false; }
else { window.location.href = data.checkout_url; }
if (data.provider === 'mtn_momo' || window.innerWidth >= 768) {
window.location.href = data.checkout_url;
} else {
this.checkoutUrl = data.checkout_url; this.showSheet = true; this.loading = false;
}
} catch(e) { this.errorMsg = 'Network error. Please try again.'; this.loading = false; }
}
}" class="min-h-screen bg-slate-50 pb-12">
@@ -1205,12 +1209,13 @@
});
const data = await res.json();
if (data.error) { this.errorMsg = data.error; this.loading = false; return; }
if (window.innerWidth < 768) {
if (!data.checkout_url) { this.errorMsg = 'Could not start payment.'; this.loading = false; return; }
if (data.provider === 'mtn_momo' || window.innerWidth >= 768) {
window.location.href = data.checkout_url;
} else {
this.checkoutUrl = data.checkout_url;
this.showSheet = true;
this.loading = false;
} else {
window.location.href = data.checkout_url;
}
} catch(e) {
this.errorMsg = 'Network error. Please try again.';
@@ -21,13 +21,14 @@
payUrl: @js($payUrl),
csrf: @js($csrf),
amount: @js(old('amount')),
phone: @js(old('customer_phone')),
errorMsg: @js(session('error')),
})">
{{-- Mobile: Ladill Mini branding + fixed payment sheet --}}
<div class="md:hidden">
<div class="fixed inset-0 overflow-hidden bg-slate-50">
<div class="flex h-full flex-col items-center justify-center px-6 pb-56 text-center pt-[max(2.5rem,env(safe-area-inset-top))]">
<div class="flex h-full flex-col items-center justify-center px-6 pb-72 text-center pt-[max(2.5rem,env(safe-area-inset-top))]">
<img src="{{ asset('images/launcher-icons/mini.svg') }}?v={{ @filemtime(public_path('images/launcher-icons/mini.svg')) ?: '1' }}"
alt="Ladill Mini" class="h-14 w-14 object-contain">
<h1 class="mt-4 text-xl font-bold text-slate-900">{{ $businessName }}</h1>
@@ -57,6 +58,13 @@
placeholder="0.00">
</div>
<label for="phone-mobile" class="mt-3 block text-xs font-medium text-slate-500">MTN MoMo number</label>
<input type="tel" id="phone-mobile" x-model="phone" required
inputmode="tel" autocomplete="tel"
@keydown.enter.prevent="submitPay()"
class="mt-1.5 w-full rounded-2xl border-slate-200 py-3.5 px-4 text-base font-medium text-slate-900 focus:border-indigo-500 focus:ring-indigo-500"
placeholder="e.g. 024XXXXXXX">
<button type="button" @click="submitPay()" :disabled="loading"
class="mt-4 flex w-full items-center justify-center gap-2 rounded-2xl bg-indigo-600 py-4 text-base font-semibold text-white hover:bg-indigo-700 disabled:opacity-60">
<svg x-show="loading" x-cloak class="h-4 w-4 animate-spin" fill="none" viewBox="0 0 24 24">
@@ -66,7 +74,7 @@
<span x-text="loading ? 'Opening payment…' : 'Pay'">Pay</span>
</button>
<p class="mt-3 text-center text-[11px] text-slate-400">Secured by Paystack. Powered by Ladill Pay</p>
<p class="mt-3 text-center text-[11px] text-slate-400">Pay with MTN MoMo. Powered by Ladill Pay</p>
</div>
</div>
@@ -95,6 +103,14 @@
class="w-full rounded-2xl border-slate-200 py-4 pl-16 pr-4 text-3xl font-bold tracking-tight text-slate-900 focus:border-indigo-500 focus:ring-indigo-500"
placeholder="0.00">
</div>
<label for="phone-desktop" class="mt-3 block text-xs font-medium text-slate-500">MTN MoMo number</label>
<input type="tel" id="phone-desktop" x-model="phone" required
inputmode="tel" autocomplete="tel"
@keydown.enter.prevent="submitPay()"
class="mt-1.5 w-full rounded-2xl border-slate-200 py-3.5 px-4 text-base font-medium text-slate-900 focus:border-indigo-500 focus:ring-indigo-500"
placeholder="e.g. 024XXXXXXX">
<button type="button" @click="submitPay()" :disabled="loading"
class="mt-4 flex w-full items-center justify-center gap-2 rounded-2xl bg-indigo-600 py-4 text-base font-semibold text-white hover:bg-indigo-700 disabled:opacity-60">
<svg x-show="loading" x-cloak class="h-4 w-4 animate-spin" fill="none" viewBox="0 0 24 24">
@@ -105,7 +121,7 @@
</button>
</div>
<p class="mt-4 text-center text-[11px] text-slate-400">Secured by Paystack. Powered by Ladill Pay</p>
<p class="mt-4 text-center text-[11px] text-slate-400">Pay with MTN MoMo. Powered by Ladill Pay</p>
</div>
</main>