Withdrawal Requests


Contents

Overview

The Withdrawal Requests feature allows financial admins to manage user withdrawal requests. Key components include:

  • Withdrawal Requests: View all withdrawal requests.
  • Process Withdrawal: Process a withdrawal for pending transactions.

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.

API 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
    }
}

Withdrawal Requests

The Withdrawal Requests section displays all withdrawal requests. The table includes the following columns:

  • Name: The user's name, email, and phone number.
  • Date/Time: The date and time the withdrawal request was made.
  • Amount: The amount requested for withdrawal.
  • Status: The status of the withdrawal request.
  • Action: The action to process the withdrawal request.

Process Withdrawal

The Process Withdrawal feature allows financial admins to process a withdrawal for pending transactions. To process a withdrawal, follow these steps:

  1. Hit the Process button on the right side of the withdrawal request.
    • The 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
    ]);
}
  • The snippet above retrieves the withdrawal request data from the database and returns it as a JSON response.
  1. Review the withdrawal request details.
  2. Select the Transaction Date & Upload Proof.
    • Submitting the withdrawal request for processing hits /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()
        );
    }
}
  • The snippet above fulfills the withdrawal request by updating the user's transaction details in the database.