initial: bootstrap from BukidBountyApp base

This commit is contained in:
Jonathan Sykes
2026-06-06 18:43:00 +08:00
commit eb4a5731fb
5674 changed files with 160857 additions and 0 deletions

View File

@@ -0,0 +1,269 @@
<?php
declare(strict_types=1);
namespace App\Http\Controllers\Market;
use App\Http\Controllers\Helpers\ResponseHelper;
use App\Http\Controllers\Helpers\PaymentProcessor;
use App\Http\Controllers\Helpers\QrphDecoder;
use App\Models\SystemSetting;
use App\Models\Accounting\MemberLedger;
use App\Models\User;
use App\Models\GlobalTransaction;
use Hypervel\Http\Request;
use Hypervel\Support\Facades\Auth;
use Hypervel\Support\Facades\DB;
use Hypervel\Support\Str;
use App\Enums\Market\ProductTransactionType;
use App\Enums\Market\TransactionFlow;
class CreditController
{
public function getWalletData(Request $request)
{
$user = User::find(Auth::id());
if (!$user) return ResponseHelper::returnUnauthorized();
$history = MemberLedger::where('user_id', $user->id)
->where('is_active', true)
->orderBy('created_at', 'desc')
->limit(20)
->get();
return response()->json([
'success' => true,
'balance' => $user->total_balance,
'credit' => $user->total_credit,
'history' => $history
]);
}
public function topUp(Request $request)
{
// Check if Top Up is enabled globally
if (!(\App\Models\SystemSetting::getValue('top_up_enabled', true))) {
return ResponseHelper::returnError('Credit top-up is currently disabled by administrators.');
}
$amount = (float) $request->input('amount');
$method = $request->input('method', 'GCASH');
if ($amount <= 0) {
return ResponseHelper::returnError('Amount must be greater than zero');
}
$user = User::find(Auth::id());
if (!$user) return ResponseHelper::returnUnauthorized();
// Start Transaction
DB::beginTransaction();
try {
// 1. Simulate Payment Success (in real life this would be a webhook/callback)
$payment = PaymentProcessor::initiatePayment($amount, $method, $user->hashkey);
if (!$payment['success']) {
throw new \Exception('Payment initiation failed');
}
// 2. Update User Balance
$user->total_balance += $amount;
$user->save();
// 3. Record in MemberLedger
$ledger = new MemberLedger([
'hashkey' => Str::random(64),
'user_id' => $user->id,
'amount' => $amount,
'transaction_type' => 'TOP_UP',
'flow' => 'IN',
'balance_after' => $user->total_balance,
'description' => "Credit Top-up via {$method}",
'reference_id' => $payment['transaction_id'],
'created_by' => $user->id,
'is_active' => true,
]);
$ledger->save();
// 4. Record in GlobalTransaction (for compatibility with existing reports)
$globalTxn = new GlobalTransaction([
'hashkey' => Str::random(64),
'user_id' => $user->id,
'amount' => $amount,
'type' => ProductTransactionType::TOP_UP,
'status' => 'COMPLETED',
'description' => "Credit Top-up via {$method}",
'flow' => TransactionFlow::INCOME,
'created_by' => $user->id,
]);
$globalTxn->save();
DB::commit();
return response()->json([
'success' => true,
'message' => 'Top-up successful',
'balance' => $user->total_balance,
'transaction_id' => $payment['transaction_id']
]);
} catch (\Exception $e) {
DB::rollBack();
return ResponseHelper::returnError('Top-up failed: ' . $e->getMessage());
}
}
public function transferCredit(Request $request)
{
$recipientHash = $request->input('recipient_hash');
$amount = (float) $request->input('amount');
if ($amount <= 0) return ResponseHelper::returnError('Invalid amount');
$sender = User::find(Auth::id());
if (!$sender) return ResponseHelper::returnUnauthorized();
if ($sender->total_balance < $amount) {
return ResponseHelper::returnError('Insufficient balance');
}
$recipient = User::where('hashkey', $recipientHash)->first();
if (!$recipient) return ResponseHelper::returnError('Recipient not found');
if ($sender->id === $recipient->id) {
return ResponseHelper::returnError('Cannot transfer to yourself');
}
DB::beginTransaction();
try {
// Deduct from sender
$sender->total_balance -= $amount;
$sender->save();
// Add to recipient
$recipient->total_balance += $amount;
$recipient->save();
$txnRef = Str::random(12);
// Record Ledger for Sender
MemberLedger::create([
'hashkey' => Str::random(64),
'user_id' => $sender->id,
'amount' => $amount,
'transaction_type' => 'TRANSFER_OUT',
'flow' => 'OUT',
'balance_after' => $sender->total_balance,
'description' => "Credit Transfer to {$recipient->fullname}",
'reference_id' => $txnRef,
'created_by' => $sender->id,
]);
// Record Ledger for Recipient
MemberLedger::create([
'hashkey' => Str::random(64),
'user_id' => $recipient->id,
'amount' => $amount,
'transaction_type' => 'TRANSFER_IN',
'flow' => 'IN',
'balance_after' => $recipient->total_balance,
'description' => "Credit Transfer from {$sender->fullname}",
'reference_id' => $txnRef,
'created_by' => $sender->id,
]);
DB::commit();
return response()->json(['success' => true, 'message' => 'Transfer successful']);
} catch (\Exception $e) {
DB::rollBack();
return ResponseHelper::returnError('Transfer failed');
}
}
public function searchUsers(Request $request)
{
$query = $request->input('q');
if (empty($query)) return response()->json(['success' => true, 'data' => []]);
$users = User::where('fullname', 'like', "%{$query}%")
->orWhere('name', 'like', "%{$query}%")
->orWhere('mobile_number', 'like', "%{$query}%")
->where('id', '!=', Auth::id())
->limit(10)
->get(['hashkey', 'fullname', 'name', 'mobile_number']);
return response()->json(['success' => true, 'data' => $users]);
}
/**
* GET the current QRPH payment code (available to all authenticated users for top-up display).
*/
public function getQrphCode(Request $request)
{
$raw = SystemSetting::getValue('qrph_payment_code', null);
$imgHashkey = SystemSetting::getValue('qrph_payment_image_hashkey', null);
if (empty($raw)) {
return response()->json(['success' => true, 'qrph' => null, 'decoded' => null, 'image_url' => null]);
}
$decoded = QrphDecoder::decode($raw);
$imageUrl = $imgHashkey ? "/RequestData/File/{$imgHashkey}" : null;
return response()->json([
'success' => true,
'qrph' => $raw,
'decoded' => $decoded,
'image_url' => $imageUrl,
]);
}
/**
* SET the QRPH payment code — ULTIMATE only (enforced by route middleware).
* Accepts optional image_hashkey from a prior /File/Upload/QrphPayment call.
*/
public function setQrphCode(Request $request)
{
$raw = trim($request->input('qrph_code', ''));
$imgHashkey = trim($request->input('image_hashkey', ''));
if (empty($raw)) {
SystemSetting::setValue('qrph_payment_code', '');
SystemSetting::setValue('qrph_payment_image_hashkey', '');
return response()->json(['success' => true, 'message' => 'QRPH code cleared.']);
}
$decoded = QrphDecoder::decode($raw);
SystemSetting::setValue('qrph_payment_code', $raw);
if (!empty($imgHashkey)) {
SystemSetting::setValue('qrph_payment_image_hashkey', $imgHashkey);
}
$imageUrl = $imgHashkey ? "/RequestData/File/{$imgHashkey}" : null;
return response()->json([
'success' => true,
'message' => 'QRPH code saved.',
'decoded' => $decoded,
'image_url' => $imageUrl,
]);
}
/**
* Decode a QRPH string without saving — for preview before saving.
*/
public function decodeQrph(Request $request)
{
$raw = trim($request->input('qrph_code', ''));
if (empty($raw)) {
return ResponseHelper::returnError('No QR string provided.');
}
return response()->json([
'success' => true,
'decoded' => QrphDecoder::decode($raw),
]);
}
}