Files
ladill-events/resources/views/events/payouts.blade.php
T
isaaccladandCursor 4c87c786da
Deploy Ladill Events / deploy (push) Successful in 42s
Add Events Pro/Business and BYO ticket gateways.
Cut ticket checkouts off Ladill Pay, settle to merchant gateways at 0% platform fee, and mirror Invoice freemium pricing (GHS 49 / 149).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-15 01:26:28 +00:00

184 lines
13 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<x-user-layout>
<x-slot name="title">Payments & Payouts</x-slot>
@php
$fmt = fn ($m) => 'GHS '.number_format($m / 100, 2);
$pa = is_array($payoutAccount ?? null) ? $payoutAccount : null;
$hasPayoutErrors = $errors->hasAny(['account_type', 'account_name', 'account_number', 'bank_code', 'bank_name']);
@endphp
<div class="mx-auto max-w-5xl space-y-6">
@foreach(['success', 'error'] as $flash)
@if(session($flash))
<div class="rounded-xl border px-4 py-3 text-sm {{ $flash === 'success' ? 'border-emerald-200 bg-emerald-50 text-emerald-700' : 'border-red-200 bg-red-50 text-red-700' }}">
{{ session($flash) }}
</div>
@endif
@endforeach
<div>
<h1 class="text-xl font-semibold text-slate-900">Payments & Payouts</h1>
<p class="mt-1 text-sm text-slate-500">Paid tickets and contributions settle directly to your connected gateway (0% Ladill fee). This wallet is for app subscriptions and older balances only.</p>
</div>
<div class="grid gap-4 sm:grid-cols-2">
<div class="rounded-2xl border border-slate-200 bg-white p-6">
<p class="text-xs font-medium uppercase tracking-wide text-slate-400">Total event revenue (net)</p>
<p class="mt-2 text-3xl font-semibold text-slate-900">{{ $fmt($revenueMinor) }}</p>
</div>
<div class="rounded-2xl border border-indigo-100 bg-indigo-50/60 p-6">
<p class="text-xs font-medium uppercase tracking-wide text-indigo-600">Available in wallet</p>
<p class="mt-2 text-3xl font-semibold text-slate-900">{{ $fmt($balanceMinor) }}</p>
<a href="{{ route('account.wallet') }}" class="mt-4 inline-block text-sm font-medium text-indigo-600 hover:text-indigo-800">Full wallet </a>
</div>
</div>
{{-- Withdraw --}}
<div class="overflow-hidden rounded-2xl border border-slate-200 bg-white" x-data="{ open: {{ $pa ? 'true' : 'false' }} }">
<button type="button" @click="open = !open" class="flex w-full items-center justify-between gap-3 px-5 py-4 text-left">
<div>
<h2 class="text-sm font-semibold text-slate-900">Withdraw to bank or MoMo</h2>
<p class="mt-0.5 text-xs text-slate-500">
@if($pa)
To {{ $pa['bank_name'] }} · {{ $pa['account_number'] }}
@else
Add a payout account to withdraw from your wallet.
@endif
</p>
</div>
<svg class="h-4 w-4 shrink-0 text-slate-400 transition" :class="open && 'rotate-180'" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5"/></svg>
</button>
<div x-show="open" x-cloak class="space-y-4 border-t border-slate-100 px-5 py-4">
@if($pa)
<div class="flex items-start gap-3 rounded-xl border border-slate-100 bg-slate-50/80 p-4">
<div class="flex h-9 w-9 shrink-0 items-center justify-center rounded-xl bg-indigo-100">
<svg class="h-5 w-5 text-indigo-600" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M2.25 8.25h19.5M2.25 9h19.5m-16.5 5.25h6m-6 2.25h3m-3.75 3h15a2.25 2.25 0 0 0 2.25-2.25V6.75A2.25 2.25 0 0 0 19.5 4.5h-15a2.25 2.25 0 0 0-2.25 2.25v10.5A2.25 2.25 0 0 0 4.5 19.5Z"/></svg>
</div>
<div class="min-w-0 flex-1">
<p class="text-[11px] font-semibold uppercase tracking-widest text-slate-400">{{ ($pa['account_type'] ?? '') === 'mobile_money' ? 'Mobile Money' : 'Bank Account' }}</p>
<p class="text-sm font-semibold text-slate-900">{{ $pa['account_name'] }}</p>
<p class="text-xs text-slate-500">{{ $pa['bank_name'] }} · {{ $pa['account_number'] }}</p>
</div>
<button type="button" @click="$dispatch('open-modal', 'events-payout-account')" class="shrink-0 text-xs font-semibold text-indigo-600 hover:text-indigo-800">Change</button>
</div>
<form action="{{ route('events.payouts.withdraw') }}" method="POST" class="space-y-3">
@csrf
<div>
<label class="block text-xs font-semibold text-slate-600">Amount (GHS)</label>
<div class="relative mt-1.5">
<span class="absolute inset-y-0 left-0 flex items-center pl-3.5 text-sm text-slate-400">GHS</span>
<input type="number" name="amount" min="1" max="{{ $balanceGhs }}" step="0.01" value="{{ old('amount') }}" placeholder="10.00"
class="block w-full rounded-xl border border-slate-200 py-2.5 pl-12 pr-4 text-sm text-slate-900 placeholder-slate-400 focus:border-indigo-400 focus:outline-none focus:ring-1 focus:ring-indigo-400/30" required>
</div>
@error('amount') <p class="mt-1 text-xs text-red-600">{{ $message }}</p> @enderror
</div>
<button type="submit" class="rounded-xl bg-slate-900 px-4 py-2 text-sm font-semibold text-white shadow-sm transition hover:bg-slate-800">Request withdrawal</button>
</form>
@else
<div class="flex flex-col items-center gap-3 py-2 text-center">
<p class="text-sm text-slate-600">Add a payout account to withdraw from your wallet.</p>
<button type="button" @click="$dispatch('open-modal', 'events-payout-account')" class="btn-primary">Add payout account</button>
</div>
@endif
</div>
</div>
<x-modal name="events-payout-account" :show="$hasPayoutErrors" maxWidth="md" focusable>
<div class="border-b border-slate-100 px-5 py-4 pr-12 sm:px-6 sm:pr-14">
<h3 class="text-base font-semibold text-slate-900">Payout account</h3>
<p class="mt-1 text-sm text-slate-500">Mobile money or bank account for withdrawals.</p>
</div>
<div class="px-5 py-4 sm:px-6 sm:py-5">
@include('events.partials.payout-account-form', ['payoutAccount' => $pa])
</div>
</x-modal>
{{-- Event payments --}}
<div class="rounded-2xl border border-slate-200 bg-white">
<div class="border-b border-slate-100 px-5 py-4">
<h2 class="text-sm font-semibold text-slate-900">Event payments</h2>
<p class="mt-0.5 text-xs text-slate-500">Legacy wallet credits from before BYO gateway settlement. New ticket revenue does not land here.</p>
</div>
@if($transactions->isEmpty())
<div class="px-6 py-10 text-center">
<svg class="mx-auto h-10 w-10 text-slate-300" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M2.25 18.75a60.07 60.07 0 0 1 15.797 2.101c.727.198 1.453-.342 1.453-1.096V18.75M3.75 4.5v.75A.75.75 0 0 1 3 6h-.75m0 0v-.375c0-.621.504-1.125 1.125-1.125H20.25M2.25 6v9m18-10.5v.75c0 .414.336.75.75.75h.75m-1.5-1.5h.375c.621 0 1.125.504 1.125 1.125v9.75c0 .621-.504 1.125-1.125 1.125h-.375m1.5-1.5H21a.75.75 0 0 0-.75.75v.75m0 0H3.75m0 0h-.375a1.125 1.125 0 0 1-1.125-1.125V15m1.5 1.5v-.75A.75.75 0 0 0 3 15h-.75M15 10.5a3 3 0 1 1-6 0 3 3 0 0 1 6 0Zm3 0h.008v.008H18V10.5Zm-12 0h.008v.008H6V10.5Z"/></svg>
<h3 class="mt-3 text-sm font-semibold text-slate-900">No payments yet</h3>
<p class="mt-1 text-sm text-slate-500">Paid registrations will appear here once attendees complete checkout.</p>
</div>
@else
<div class="overflow-x-auto">
<table class="min-w-full text-left text-sm">
<thead class="border-b border-slate-100 bg-slate-50/80 text-xs uppercase tracking-wide text-slate-500">
<tr>
<th class="px-5 py-3 font-medium">Event</th>
<th class="px-5 py-3 font-medium">Attendee</th>
<th class="px-5 py-3 font-medium">Type</th>
<th class="px-5 py-3 font-medium text-right">Gross</th>
<th class="px-5 py-3 font-medium text-right">Fee</th>
<th class="px-5 py-3 font-medium text-right">Net</th>
<th class="px-5 py-3 font-medium">Paid</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-100">
@foreach($transactions as $tx)
@php
$eventName = $tx->qrCode?->content()['name'] ?? $tx->qrCode?->label ?? 'Event';
$isContribution = ($tx->qrCode?->content()['mode'] ?? 'ticketing') === 'contributions';
@endphp
<tr class="hover:bg-slate-50/50">
<td class="px-5 py-3 font-medium text-slate-900">{{ $eventName }}</td>
<td class="px-5 py-3 text-slate-600">{{ $tx->attendee_name }}</td>
<td class="px-5 py-3 text-slate-600">{{ $tx->tier_name }}</td>
<td class="px-5 py-3 text-right text-slate-600">{{ $fmt($tx->amount_minor) }}</td>
<td class="px-5 py-3 text-right text-slate-400">{{ $fmt($tx->platformFeeMinor()) }}</td>
<td class="px-5 py-3 text-right font-medium text-slate-900">{{ $fmt($tx->netAmountMinor()) }}</td>
<td class="px-5 py-3 text-slate-500">
{{ $tx->paid_at?->format('M j, Y g:i A') ?? '—' }}
@if($tx->payment_reference)
<span class="block text-[11px] text-slate-400">{{ $tx->payment_reference }}</span>
@endif
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@endif
</div>
{{-- Withdrawal history --}}
@if(!empty($withdrawals))
<div class="rounded-2xl border border-slate-200 bg-white">
<div class="border-b border-slate-100 px-5 py-4">
<h2 class="text-sm font-semibold text-slate-900">Withdrawal history</h2>
<p class="mt-0.5 text-xs text-slate-500">Recent requests to your bank or mobile money account.</p>
</div>
<div class="divide-y divide-slate-100">
@foreach($withdrawals as $withdrawal)
@php
$status = (string) ($withdrawal['status'] ?? 'pending');
$statusClass = match ($status) {
'completed', 'success' => 'text-emerald-700 bg-emerald-50',
'failed' => 'text-red-700 bg-red-50',
default => 'text-amber-700 bg-amber-50',
};
@endphp
<div class="flex flex-wrap items-center justify-between gap-3 px-5 py-3">
<div>
<p class="text-sm font-medium text-slate-900">GHS {{ number_format((float) ($withdrawal['amount_ghs'] ?? 0), 2) }}</p>
<p class="text-xs text-slate-500">
@if(!empty($withdrawal['created_at']))
{{ \Illuminate\Support\Carbon::parse($withdrawal['created_at'])->format('M j, Y g:i A') }}
@endif
</p>
</div>
<span class="rounded-full px-2.5 py-0.5 text-xs font-medium capitalize {{ $statusClass }}">{{ $status }}</span>
</div>
@endforeach
</div>
</div>
@endif
</div>
</x-user-layout>