The Withdrawal Requests feature allows financial admins to manage user withdrawal requests. Key components include:
On page load, the /api:BVaDN9hl/withdrawal/get?filter%5Bsearch%5D=&filter%5Bstatus%5D=&page=1&per_page=15 endpoint is called to retrieve the withdrawal requests data. It runs pp\Http\Controllers\Admin\WithdrawalController@get.
public function get(Request $request)
{
$validator = Validator::make($request->all(), [
'page' => 'required|numeric',
'per_page' => 'required|numeric',
'filter[search]' => 'nullable|string',
'filter[status]' => 'nullable|string',
]);
if ($validator->fails()) {
return response()->json(['errors' => $validator->errors()], 422);
}
$data = $validator->validated();
$per_page = $data['per_page'];
$filter = QueryBuilder::for(UserTransaction::query())
->allowedFilters([
AllowedFilter::custom('search', new SearchFilter),
AllowedFilter::exact('status', 'is_fulfilled'),
])
->where('type', '=', TransactionActionEnum::WITHDRAWAL->value)
->whereNotIn('users_id', User::query()->adminAccounts()->pluck('id'));
$transactions = DB::table('user_transactions', 'ut')
->whereIn('ut.id', $filter->pluck('id'))
->leftJoin('users as u', 'u.id', '=', 'ut.users_id')
->leftJoin('k_y_c_s as k', 'k.users_id', '=', 'ut.users_id')
->whereNotNull('u.id')
->select(
'ut.created_at as created_at',
'u.firstname as firstname',
'u.lastname as lastname',
'u.email as email',
'ut.amount as amount',
'ut.type as action',
'ut.id as utid',
DB::raw('u.kyc->>\'kyc_contact\' as contact '),
DB::raw('k.phone->>\'phone\' as kyc'),
'ut.is_fulfilled as status',
)
->orderBy('ut.created_at', 'desc')
->paginate($per_page);
$transactions->getCollection()->map(function ($transaction) {
$transaction->amount = abs($transaction->amount);
if ($transaction->contact) {
$transaction->phone_number = $transaction->contact;
} elseif ($transaction->kyc) {
$transaction->phone_number = $transaction->kyc;
} else {
$transaction->phone_number = "-";
}
});
return WithdrawalsResource::collection($transactions);
}
The snippet above retrieves withdrawal requests data from the database and returns it as a JSON response.
{
"data": [
{
"tid": 1234,
"name": "Sam Uzima",
"email": "u******il.com",
"phone_number": "+2547*****66",
"date": "Mar 09, 2025",
"time": "06:01PM",
"amount": "7,770.00",
"status": "pending",
"bg_color": "/docs/1.0/financial-admin/withdrawal#7C022533",
"color": "/docs/1.0/financial-admin/withdrawal#7C0225"
},
---
{
---
}
],
"links": {
"first": "http://127.0.0.1:8080/api:BVaDN9hl/withdrawal/get?page=1",
"last": "http://127.0.0.1:8080/api:BVaDN9hl/withdrawal/get?page=17",
"prev": null,
"next": "http://127.0.0.1:8080/api:BVaDN9hl/withdrawal/get?page=2"
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 17,
"links": [
{
"url": null,
"label": "« Previous",
"active": false
},
{
"url": null,
"label": "...",
"active": false
},
{
"url": "http://127.0.0.1:8080/api:BVaDN9hl/withdrawal/get?page=2",
"label": "Next »",
"active": false
}
],
"path": "http://127.0.0.1:8080/api:BVaDN9hl/withdrawal/get",
"per_page": 15,
"to": 15,
"total": 243
}
}
The Withdrawal Requests section displays all withdrawal requests. The table includes the following columns:
The Process Withdrawal feature allows financial admins to process a withdrawal for pending transactions. To process a withdrawal, follow these steps:
Process button on the right side of the withdrawal request.
Process button triggers the /api:BVaDN9hl/withdrawal/check?gtid=6452 endpoint, which runs pp\Http\Controllers\Admin\WithdrawalController@check. public function check(Request $request)
{
$validator = Validator::make($request->all(), [
'gtid' => 'required|numeric',
]);
if ($validator->fails()) {
return response()->json(['errors' => $validator->errors()], 422);
}
$data = $validator->validated();
$id = $data['gtid'];
$transaction = UserTransaction::find($id);
if (!$transaction) {
return RespondWithError::make()->handle(
code: RespondWithError::$ERROR_CODE_NOT_FOUND,
message: "Withdrawal not found"
);
}
$result = DB::table('goal_transactions', 'gt')
->where('gt.user_transactions_id', '=', $transaction->id)
->leftJoin('goals as g', 'g.id', 'gt.goals_id')
->select(
'gt.id as gtid',
'gt.amount as amount',
'g.title as title',
'g.target as target',
'gt.goals_id as goals_id',
)
->get();
foreach ($result as $item) {
$item->value = number_format(MaGetGoalCurrentValue::make()->handle($item->goals_id), 2);
$item->amount = number_format(abs($item->amount), 2);
$item->target = number_format($item->target, 2);
}
return response()->json([
'response' => $result
]);
}
/api:BVaDN9hl/withdrawal/fulfill endpoint, which runs pp\Http\Controllers\Admin\WithdrawalController@fulfill.public function fulfill(Request $request)
{
$validator = Validator::make($request->all(), [
'timestamp' => [Rule::requiredIf(fn() => is_null($request->input('investment_pool_id'))), 'numeric'],
'tid' => ['required', 'exists:user_transactions,id'],
'link' => [Rule::requiredIf(fn() => is_null($request->input('investment_pool_id'))), 'url'],
'investment_pool_id' => ['nullable', 'exists:investment_pools,id'],
'comment' => ['nullable', 'string'],
]);
if ($validator->fails()) {
return RespondWithError::make()->handle(
code: RespondWithError::$ERROR_CODE_VALIDATION,
message: $validator->errors()->first(),
payload: $validator->errors()->all()
);
}
$data = $validator->validated();
//check if date is a future date
if (isset($data['timestamp']) && Carbon::createFromTimestampMs($data['timestamp'])->isFuture()) {
return RespondWithError::make()->handle(
code: RespondWithError::$ERROR_CODE_VALIDATION,
message: "Invalid date. Please select a date in the past."
);
}
$userTransaction = UserTransaction::query()->find($data['tid']);
$penalty = [];
try {
$penalty = CalculateWithdrawalPenalty::make()->handle($userTransaction->id);
} catch (\Error $e) {
}
try {
DB::beginTransaction();
$totalPenalty = $penalty['penalty'];
$goalTransaction = GoalTransaction::query()->where('user_transactions_id', $userTransaction->id)->first();
$goalTransaction->update([
'amount' => $goalTransaction->amount + $totalPenalty
]);
$userTransaction->update([
'amount' => $userTransaction->amount - $totalPenalty,
'meta' => [
...(array)$userTransaction->meta,
'penalty' => $penalty
],
]);
if (isset($data['comment'])) {
$userTransaction->addComment($data['comment']);
}
if ($request->input('investment_pool_id')) {
$userTransaction->update([
'source_type' => InvestmentPool::class,
'source_id' => $data['investment_pool_id']
]);
} else {
$userTransaction = AdminFulfillAction::make()->handle(...[
'transaction_id' => $data['tid'],
'link' => $data['link'],
'timestamp' => $data['timestamp'],
]);
}
DB::commit();
UserWithdrawalProcessedJob::dispatch(
user: $userTransaction->user,
user_transaction: $userTransaction,
send_to_user: true
);
return RespondWithSuccess::make()->handle($userTransaction->refresh());
} catch (\Exception $e) {
DB::rollBack();
return RespondWithError::make()->handle(
code: RespondWithError::$INTERNAL_SERVER_ERROR,
message: $e->getMessage(),
payload: $e->getTrace()
);
}
}