From ab192e443f7a5cc17f710847b88d23b1e16478fe Mon Sep 17 00:00:00 2001 From: isaacclad Date: Wed, 15 Jul 2026 08:29:13 +0000 Subject: [PATCH] Embed event checkouts and send seller email to gateways. Ticket/contribution payments open in a desktop modal or mobile sheet, initialize with the organizer email, and break out of the iframe after callback. Co-authored-by: Cursor --- .../Public/EventRegistrationController.php | 12 +++- .../Events/EventRegistrationService.php | 7 ++- .../views/partials/paystack-sheet.blade.php | 58 +++++++++++++------ .../views/public/payment-return.blade.php | 27 +++++++++ resources/views/public/qr/landing.blade.php | 23 +++----- 5 files changed, 90 insertions(+), 37 deletions(-) create mode 100644 resources/views/public/payment-return.blade.php diff --git a/app/Http/Controllers/Public/EventRegistrationController.php b/app/Http/Controllers/Public/EventRegistrationController.php index 7459182..ded32b6 100644 --- a/app/Http/Controllers/Public/EventRegistrationController.php +++ b/app/Http/Controllers/Public/EventRegistrationController.php @@ -55,15 +55,21 @@ class EventRegistrationController extends Controller $reference = (string) $request->query('reference', ''); if ($reference === '') { - return redirect()->away(LadillLink::url($shortCode))->with('error', 'Missing payment reference.'); + return view('public.payment-return', [ + 'redirect' => LadillLink::url($shortCode), + ]); } try { $registration = $this->eventService->complete($reference); - return redirect()->away($this->confirmedUrl($registration->qrCode, $registration)); + return view('public.payment-return', [ + 'redirect' => $this->confirmedUrl($registration->qrCode, $registration), + ]); } catch (\Throwable $e) { - return redirect()->away(LadillLink::url($shortCode))->with('order_error', 'Payment could not be verified. Reference: ' . $reference); + return view('public.payment-return', [ + 'redirect' => LadillLink::url($shortCode), + ]); } } diff --git a/app/Services/Events/EventRegistrationService.php b/app/Services/Events/EventRegistrationService.php index 102101b..03c4d04 100644 --- a/app/Services/Events/EventRegistrationService.php +++ b/app/Services/Events/EventRegistrationService.php @@ -130,11 +130,16 @@ class EventRegistrationService ? sprintf('%s — %s', $content['name'] ?? $qrCode->label, $tierName) : sprintf('%s ticket — %s', $content['name'] ?? $qrCode->label, $tierName); + $sellerEmail = trim((string) ($qrCode->user->email ?? '')); + if ($sellerEmail === '' || ! str_contains($sellerEmail, '@')) { + $sellerEmail = 'seller+'.($qrCode->user->public_id ?? 'events').'@checkout.ladill.local'; + } + $checkout = $this->gateway->initializeCheckout( $qrCode->user, $amountMinor, (string) ($registration->currency ?? 'GHS'), - $email, + $sellerEmail, LadillLink::path($qrCode->short_code, 'register/callback'), $registration->reference, [ diff --git a/resources/views/partials/paystack-sheet.blade.php b/resources/views/partials/paystack-sheet.blade.php index 559bf73..eed268f 100644 --- a/resources/views/partials/paystack-sheet.blade.php +++ b/resources/views/partials/paystack-sheet.blade.php @@ -1,16 +1,15 @@ {{-- - Paystack mobile bottom-sheet iframe. - Requires Alpine.js ancestor with: showSheet (bool), checkoutUrl (string). - On mobile (< md) the sheet slides up; on desktop nothing renders. + Payment checkout shell: bottomsheet on mobile, centered modal on desktop. + Requires Alpine ancestor with: showSheet (bool), checkoutUrl (string). --}} diff --git a/resources/views/public/payment-return.blade.php b/resources/views/public/payment-return.blade.php new file mode 100644 index 0000000..78e28fd --- /dev/null +++ b/resources/views/public/payment-return.blade.php @@ -0,0 +1,27 @@ + + + + + + Payment + + + +

Finishing payment…

+ + + diff --git a/resources/views/public/qr/landing.blade.php b/resources/views/public/qr/landing.blade.php index a4a944c..5d60e45 100644 --- a/resources/views/public/qr/landing.blade.php +++ b/resources/views/public/qr/landing.blade.php @@ -358,13 +358,9 @@ }); const data = await res.json(); if (data.error) { this.errorMsg = data.error; this.loading = false; return; } - if (window.innerWidth < 768) { - this.checkoutUrl = data.checkout_url; - this.showSheet = true; - this.loading = false; - } else { - window.location.href = data.checkout_url; - } + this.checkoutUrl = data.checkout_url; + this.showSheet = true; + this.loading = false; } catch(e) { this.errorMsg = 'Network error. Please try again.'; this.loading = false; @@ -678,8 +674,7 @@ if (data.error) { this.errorMsg = data.error; this.loading = false; return; } if (data.paid) { if (!data.checkout_url) { this.errorMsg = 'Could not start checkout. Please try again.'; this.loading = false; return; } - if (window.innerWidth < 768) { this.checkoutUrl = data.checkout_url; this.showSheet = true; this.loading = false; } - else { window.location.href = data.checkout_url; } + this.checkoutUrl = data.checkout_url; this.showSheet = true; this.loading = false; return; } if (data.success_url) { window.location.href = data.success_url; return; } @@ -1252,13 +1247,9 @@ }); const data = await res.json(); if (data.error) { this.errorMsg = data.error; this.loading = false; return; } - if (window.innerWidth < 768) { - this.checkoutUrl = data.checkout_url; - this.showSheet = true; - this.loading = false; - } else { - window.location.href = data.checkout_url; - } + this.checkoutUrl = data.checkout_url; + this.showSheet = true; + this.loading = false; } catch(e) { this.errorMsg = 'Network error. Please try again.'; this.loading = false;