Files

132 lines
4.4 KiB
PHP

<?php
namespace App\Http\Controllers\API\v1\Allowance;
use App\AllowanceClaimCode;
use App\AllowancePayout;
use App\Http\Controllers\Controller;
use App\Http\Controllers\Util;
use App\Result;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
class ClaimCodeController extends Controller
{
public function __invoke(Request $request)
{
$this->validate($request, [
'method' => 'nullable|string|in:cash,bank',
]);
$electionId = Util::getCurrentElection();
$method = $request->input('method', 'cash');
// voter auth guard; Auth::id() should be voter id under voterAPI
$voterId = Auth::id();
$voter = $request->user();
if (!$voter || (int) $voter->id !== (int) $voterId) {
return response()->json([
'status' => 'failed',
'message' => 'Unauthenticated.',
], 401);
}
// Enforce method by attendance
if ($method === 'cash' && (int) $voter->kehadiran !== 1) {
return response()->json([
'status' => 'failed',
'message' => 'Kod tuntutan tunai hanya untuk kehadiran Fizikal.',
], 422);
}
if ($method === 'bank' && (int) $voter->kehadiran !== 2) {
return response()->json([
'status' => 'failed',
'message' => 'Kod tuntutan bank hanya untuk kehadiran Maya.',
], 422);
}
$hasVoted = Result::where('election_id', $electionId)->where('voter_id', $voterId)->exists();
// Tunai: normally must have voted; exception — fizikal hadir & layak mesyuarat tetapi tidak dibenarkan
// mengundi kerana saham < RM500 (same rule as voter UI).
$kehadiran = (int) $voter->kehadiran;
$saham = (float) $voter->saham;
$canVoteBySaham = $saham >= 500.0;
$canProceedToVote = $kehadiran !== 0
&& ($kehadiran !== 1 || $voter->fizikal_registration_verified_at !== null);
$eligibleClaimCashWithoutVote = $method === 'cash'
&& $kehadiran === 1
&& $canProceedToVote
&& !$canVoteBySaham;
if (!$hasVoted && !$eligibleClaimCashWithoutVote) {
return response()->json([
'status' => 'failed',
'message' => 'Sila undi terlebih dahulu sebelum menuntut elaun.',
], 422);
}
// If already paid, don't generate codes
$alreadyPaid = AllowancePayout::where('election_id', $electionId)
->where('voter_id', $voterId)
->where('method', $method)
->where('status', 'paid')
->exists();
if ($alreadyPaid) {
return response()->json([
'status' => 'failed',
'message' => 'Elaun anda telah direkodkan sebagai sudah dibayar.',
], 422);
}
// Generate a 6-digit numeric code
$codeInt = random_int(0, 999999);
$code = str_pad((string) $codeInt, 6, '0', STR_PAD_LEFT);
$expiresAt = Carbon::now()->addHours(6);
$row = AllowanceClaimCode::updateOrCreate(
[
'election_id' => $electionId,
'voter_id' => $voterId,
'method' => $method,
],
[
'code_hash' => Hash::make($code),
'code_last4' => substr($code, -4),
'expires_at' => $expiresAt,
'used_at' => null,
'used_by_admin_id' => null,
]
);
activity()
->performedOn($row)
->withProperties([
'election_id' => $electionId,
'voter_id' => (int) $voterId,
'voter_name' => $voter->name,
'attendance' => (int) $voter->kehadiran,
'method' => $method,
'expires_at' => $expiresAt->toDateTimeString(),
'code_last4' => $row->code_last4,
'ip' => $request->ip(),
'user_agent' => $request->userAgent(),
])
->log("generate claim code ({$method}): {$voter->name}");
return response()->json([
'status' => 'success',
'message' => 'Kod tuntutan berjaya dijana.',
'code' => $code, // return plaintext ONCE to voter
'expires_at' => $expiresAt,
'method' => $method,
]);
}
}