Files
ladill-events/app/Http/Controllers/Events/PayoutsController.php
T
isaaccladandCursor d25682bc6c
Deploy Ladill Events / deploy (push) Successful in 38s
Complete Ladill Pay payouts UI for Events.
Show per-event payment history with fees and net amounts, and let organizers manage payout accounts and withdrawals without leaving the app.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-04 12:06:43 +00:00

126 lines
4.2 KiB
PHP

<?php
namespace App\Http\Controllers\Events;
use App\Http\Controllers\Controller;
use App\Models\QrCode;
use App\Models\QrEventRegistration;
use App\Services\Billing\BillingClient;
use App\Services\Identity\IdentityClient;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\View\View;
use Throwable;
class PayoutsController extends Controller
{
public function __construct(
private BillingClient $billing,
private IdentityClient $identity,
) {}
public function index(): View
{
$account = ladill_account();
$eventIds = $account->qrCodes()
->where('type', QrCode::TYPE_EVENT)
->pluck('id');
$revenueMinor = (int) QrEventRegistration::query()
->whereIn('qr_code_id', $eventIds)
->where('status', QrEventRegistration::STATUS_CONFIRMED)
->where('amount_minor', '>', 0)
->get()
->sum(fn (QrEventRegistration $r) => $r->netAmountMinor());
$transactions = QrEventRegistration::query()
->whereIn('qr_code_id', $eventIds)
->where('status', QrEventRegistration::STATUS_CONFIRMED)
->where('amount_minor', '>', 0)
->with('qrCode:id,label,payload')
->orderByDesc('paid_at')
->orderByDesc('id')
->limit(50)
->get();
$balanceMinor = 0;
$payoutAccount = null;
$withdrawals = [];
try {
$balanceMinor = $this->billing->balanceMinor($account->public_id);
} catch (Throwable $e) {
Log::warning('Events payouts could not load wallet balance', [
'user' => $account->public_id,
'error' => $e->getMessage(),
]);
}
try {
$payoutAccount = $this->identity->payoutAccount($account->public_id);
$withdrawals = $this->identity->withdrawals($account->public_id);
} catch (Throwable $e) {
Log::warning('Events payouts could not load payout account or withdrawals', [
'user' => $account->public_id,
'error' => $e->getMessage(),
]);
}
return view('events.payouts', [
'revenueMinor' => $revenueMinor,
'balanceMinor' => $balanceMinor,
'balanceGhs' => round($balanceMinor / 100, 2),
'transactions' => $transactions,
'payoutAccount' => $payoutAccount,
'withdrawals' => $withdrawals,
]);
}
public function banks(Request $request): JsonResponse
{
$type = $request->query('type') === 'bank_account' ? 'bank' : 'mobile_money';
try {
return response()->json(['data' => $this->identity->banks($type)]);
} catch (Throwable $e) {
Log::warning('Events payouts could not load banks', ['error' => $e->getMessage()]);
return response()->json(['data' => []]);
}
}
public function updatePayoutAccount(Request $request): RedirectResponse
{
$data = $request->validate([
'account_type' => ['required', 'in:mobile_money,bank_account'],
'account_name' => ['required', 'string', 'max:200'],
'account_number' => ['required', 'string', 'max:30'],
'bank_code' => ['required', 'string', 'max:30'],
'bank_name' => ['required', 'string', 'max:200'],
'currency' => ['sometimes', 'string', 'size:3'],
]);
$data['currency'] = $data['currency'] ?? 'GHS';
$this->identity->updatePayoutAccount(ladill_account()->public_id, $data);
return redirect()
->route('events.payouts')
->with('success', 'Payout account saved.');
}
public function withdraw(Request $request): RedirectResponse
{
$data = $request->validate([
'amount' => ['required', 'numeric', 'min:1', 'max:50000'],
]);
$this->identity->withdraw(ladill_account()->public_id, (float) $data['amount']);
return redirect()
->route('events.payouts')
->with('success', 'Withdrawal request submitted. Funds are sent to your payout account once processed.');
}
}