Withdrawal Approvals
Process vendor withdrawal requests through the admin panel, managing payouts with approval, rejection, and completion tracking.
Overview
The withdrawal approval system allows administrators to review vendor withdrawal requests and process payments. Admins can approve, reject, or mark withdrawals as completed once payment is sent.
Page Location: WordPress Admin → WP Sell Services → Withdrawals
Access Required: manage_options capability (administrators only)

Accessing the Withdrawals Page
- Log in to WordPress admin
- Go to WP Sell Services → Withdrawals (submenu of WP Sell Services)
- Page slug:
wpss-withdrawals
Dashboard Statistics
The withdrawals page displays 4 status cards showing:
Pending
- Count of pending withdrawal requests
- Total amount in pending withdrawals
- Color: Yellow/Warning (#dba617)
Approved
- Count of approved (not yet completed) withdrawals
- Color: Blue (#2271b1)
Completed
- Count of completed withdrawals
- Total amount successfully paid out
- Color: Green (#00a32a)
Rejected
- Count of rejected withdrawal requests
- Color: Red (#d63638)
Statistics calculated from wpss_withdrawals table using SQL aggregation.
Withdrawal List Table
The withdrawals table shows all requests with the following columns:
Columns
| Column | Data Source | Description |
|---|---|---|
| ID | w.id | Withdrawal request ID |
| Vendor | u.displayname, u.useremail | Vendor name, email, avatar |
| Amount | w.amount | Withdrawal amount (formatted price) |
| Method | w.method | Payment method label |
| Status | w.status | Current status badge |
| Date | w.created_at | Request date, processed date if applicable |
| Actions | – | Action buttons based on status |
Vendor Column Details
Shows:
- Avatar (32x32px, rounded)
- Display name (linked to user edit page)
- Email address (smaller text)
Method Column Details
Shows payment method label plus account details:
PayPal:
- Shows email from
detailsJSON
Bank Transfer:
- Shows bank name
- Last 4 digits of account number (masked:
***1234)
Status Column Details
Status displayed as badge with color coding:
| Status | Badge Color | Background | Text Color |
|---|---|---|---|
pending | Yellow | #fff3cd | #856404 |
approved | Blue | #d1e7f3 | #0a4b78 |
completed | Green | #d4edda | #155724 |
rejected | Red | #f8d7da | #721c24 |
Also shows truncated admin note (5 words) with full note in tooltip if present.
Date Column Details
Shows:
- Request date (formatted with WordPress date/time format)
- Processed date below if
processed_atis not null (labeled “Processed: [date]”)
Actions Column
Actions shown based on current status:
For Pending Status:
- Approve button (primary blue button)
- Reject button (standard button)
For Approved Status:
- Mark Completed button (primary blue button)
- Reject button (standard button)
For Completed/Rejected:
- Status message only (“Payment sent” or “Request rejected”)
Filtering Withdrawals
Use the status filter tabs above the table:
Filter Tabs
- All – Shows all withdrawals
- Pending – Shows only
status = 'pending' - Approved – Shows only
status = 'approved' - Completed – Shows only
status = 'completed' - Rejected – Shows only
status = 'rejected'
Each tab shows count in parentheses from statistics.
Processing Withdrawals
Modal Interface
When you click an action button (Approve, Reject, Mark Completed), a modal opens:
Modal Title: Changes based on action:
- “Approve Withdrawal”
- “Mark as Completed”
- “Reject Withdrawal”
Modal Content:
- Confirmation message with amount and vendor name
- Optional admin note textarea
- Cancel and Confirm buttons
Approving a Withdrawal
- Click Approve button on pending withdrawal
- Modal opens with confirmation: “Approve this withdrawal request for [amount] from [vendor]?”
- Optionally add admin note
- Click Confirm
- AJAX request to
wpssprocesswithdrawalwith: - Status updates to
approved - Page reloads to show updated status
What Happens:
- Database:
status→'approved' - Database:
processed_at→ current timestamp - Database:
processed_by→ current admin user ID - Database:
admin_note→ saved if provided - Vendor receives notification: “Your withdrawal request for [amount] has been approved.”
Marking as Completed
- Click Mark Completed button on approved withdrawal
- Modal opens: “Mark this withdrawal as completed. This means payment has been sent to [vendor].”
- Optionally add admin note (e.g., transaction reference)
- Click Confirm
- Status updates to
completed
What Happens:
- Database:
status→'completed' - Database:
processed_at→ current timestamp - Database:
processed_by→ current admin user ID - Vendor receives notification: “Your withdrawal request for [amount] has been completed.”
Rejecting a Withdrawal
- Click Reject button (available for pending or approved)
- Modal opens: “Reject this withdrawal request from [vendor]? The funds will be returned to their available balance.”
- Add admin note explaining rejection reason
- Click Confirm
- Status updates to
rejected
What Happens:
- Database:
status→'rejected' - Database:
processed_at→ current timestamp - Database:
processed_by→ current admin user ID - Funds remain in vendor’s available balance (not deducted)
- Vendor receives notification: “Your withdrawal request for [amount] has been rejected.”
Withdrawal Statuses
The system uses 4 statuses defined in EarningsService:
public const WITHDRAWAL_PENDING = 'pending';
public const WITHDRAWAL_APPROVED = 'approved';
public const WITHDRAWAL_COMPLETED = 'completed';
public const WITHDRAWAL_REJECTED = 'rejected';
Status Flow
Normal Flow:
pending → approved → completed
Rejection Flow:
pending → rejected
OR
approved → rejected
Status Meanings:
- Pending: Newly submitted, awaiting admin review
- Approved: Admin approved, payment needs to be sent
- Completed: Payment sent, withdrawal finished
- Rejected: Request denied, funds returned to vendor balance
Payment Methods
Supported withdrawal methods (from EarningsService::getwithdrawalmethods()):
'paypal' => 'PayPal',
'bank_transfer' => 'Bank Transfer',
Additional methods can be added via filter:
apply_filters( 'wpss_withdrawal_methods', $methods );
Method Details Storage
Payment account details stored as JSON in details column:
PayPal:
{
"email": "vendor@example.com"
}
Bank Transfer:
{
"bank_name": "Chase Bank",
"account_number": "1234567890",
"routing_number": "021000021",
"account_holder": "John Smith"
}
Vendor Withdrawal Limits
Minimum Withdrawal Amount
Default: $50.00
Retrieved from settings via EarningsService::getminwithdrawal_amount():
- Checks
wpsspayouts['minwithdrawal'] - Falls back to
wpssvendor['minpayout_amount'] - Default: 50.0
Vendors cannot request withdrawal below this amount.
Available Balance Calculation
Before allowing withdrawal, system calculates:
$total_earned = // Sum of vendor_earnings from completed orders
$withdrawn = // Sum of completed withdrawals
$pending_withdrawal = // Sum of pending + approved withdrawals
$available = $total_earned - $withdrawn - $pending_withdrawal
Withdrawal amount cannot exceed available balance.
Auto-Withdrawal System [PRO]
Note: Auto-withdrawal features are part of the Pro version.
Checking if Enabled
EarningsService::is_auto_withdrawal_enabled()
// Returns bool from wpss_payouts['auto_withdrawal_enabled']
Auto-Withdrawal Settings
Retrieved from wpss_payouts option:
autowithdrawalthreshold– Minimum balance to trigger (default: 500)autowithdrawalschedule– Frequency: ‘weekly’ or ‘monthly’ (default: ‘monthly’)
Eligible Vendors
Auto-withdrawal processes vendors who meet ALL criteria:
- Available balance ≥ threshold
- Payout method configured (
wpsspayoutmethoduser meta) - Payout details complete (
wpsspayoutdetailsuser meta)
Processing Auto-Withdrawals
Triggered via WordPress cron: wpssprocessauto_withdrawals
Schedule:
- Weekly: Next Monday at 2:00 AM
- Monthly: 1st day of next month at 2:00 AM
Process:
- Get eligible vendors via
geteligiblevendorsforauto_withdrawal() - Create withdrawal request for each with
is_auto = 1 - Notify admin via email (if
withdrawal_autoemail type enabled) - Notify vendor via in-app notification
- Log results in
wpsslastautowithdrawalrunoption
Database Query Details
Getting Withdrawals
Main query in get_withdrawals():
SELECT w.*, u.display_name as vendor_name, u.user_email as vendor_email
FROM {$wpdb->prefix}wpss_withdrawals w
LEFT JOIN {$wpdb->users} u ON w.vendor_id = u.ID
WHERE [status filter]
ORDER BY w.created_at DESC
LIMIT [per_page] OFFSET [offset]
Supports:
- Pagination (20 per page default)
- Status filtering
- Sorting by created_at DESC
Getting Statistics
Statistics query:
SELECT
COUNT(*) as total,
SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) as pending,
SUM(CASE WHEN status = 'approved' THEN 1 ELSE 0 END) as approved,
SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as completed,
SUM(CASE WHEN status = 'rejected' THEN 1 ELSE 0 END) as rejected,
SUM(CASE WHEN status = 'pending' THEN amount ELSE 0 END) as pending_amount,
SUM(CASE WHEN status = 'completed' THEN amount ELSE 0 END) as completed_amount
FROM {$wpdb->prefix}wpss_withdrawals
WordPress Hooks
Action: Withdrawal Processed
Fires when withdrawal status is updated:
/**
* Fires when withdrawal is processed.
*
* @param int $withdrawal_id Withdrawal ID.
* @param string $status New status (approved/completed/rejected).
* @param object $withdrawal Withdrawal data object.
*/
do_action( 'wpss_withdrawal_processed', $withdrawal_id, $status, $withdrawal );
Action: Withdrawal Requested
Fires when vendor creates withdrawal request:
/**
* Fires when withdrawal is requested.
*
* @param int $withdrawal_id Withdrawal ID.
* @param int $vendor_id Vendor user ID.
* @param float $amount Withdrawal amount.
*/
do_action( 'wpss_withdrawal_requested', $withdrawal_id, $vendor_id, $amount );
Action: Auto-Withdrawal Created [PRO]
Fires when auto-withdrawal is created:
/**
* Fires when auto withdrawal is created.
*
* @param int $withdrawal_id Withdrawal ID.
* @param int $vendor_id Vendor user ID.
* @param float $amount Withdrawal amount.
*/
do_action( 'wpss_auto_withdrawal_created', $withdrawal_id, $vendor_id, $amount );
AJAX Implementation
Action: wpssprocesswithdrawal
Nonce: wpsswithdrawalsadmin
Parameters:
withdrawal_id(int) – Withdrawal request IDaction_type(string) –approve,complete, orrejectadmin_note(string) – Optional admin note
Process:
- Verify nonce and admin capability
- Validate withdrawal ID exists
- Map action_type to status constant
- Call
EarningsService::process_withdrawal() - Return JSON success/error
Response:
{
"success": true,
"data": {
"message": "Withdrawal updated successfully."
}
}
Email Notifications
To Vendor
Notification created via NotificationService::create() when withdrawal is processed:
Title: “Withdrawal Update”
Message: “Your withdrawal request for [amount] has been [status].”
Data: { withdrawal_id: [id] }
To Admin [PRO]
When vendor requests withdrawal (if withdrawal_requested email type enabled):
To: admin_email from WordPress settings
Subject: “[Platform Name] New Withdrawal Request”
Message: “Vendor [name] has requested a withdrawal of [amount]. Please review in the admin panel.”
Technical Details
Page Hook: sell-servicespagewpss-withdrawals
AJAX URL: admin_url('admin-ajax.php')
Database Table: {$wpdb->prefix}wpss_withdrawals
Table Columns:
id– Auto-increment primary keyvendorid– Foreign key to wpusersamount– DECIMAL withdrawal amountmethod– VARCHAR payment methoddetails– TEXT JSON payment detailsstatus– VARCHAR (pending/approved/completed/rejected)admin_note– TEXT optional admin noteprocessed_at– DATETIME processing timestampprocessed_by– INT admin user IDis_auto– TINYINT auto-withdrawal flag [PRO]created_at– DATETIME request timestamp
Pagination
The withdrawals list supports pagination:
- Default: 20 items per page
- URL parameter:
paged - Total pages calculated:
ceil(total / per_page) - Uses WordPress
paginate_links()for pagination display
Styling
Custom CSS included inline in page:
- Grid layout for statistics cards (4 columns)
- Status badges with color coding
- Responsive table design
- Modal overlay with centered content
- Vendor info flex layout with avatar
Common Workflows
Process Pending Withdrawal
- Vendor submits withdrawal request
- Admin clicks Pending filter tab
- Reviews request details and vendor balance
- Clicks Approve
- Adds note: “Approved – will process via PayPal”
- Confirms approval
- Processes payment externally via PayPal
- Returns to withdrawals page
- Clicks Mark Completed
- Adds note: “PayPal Transaction ID: [txn_id]”
- Confirms completion
Reject Invalid Request
- Admin reviews pending withdrawal
- Notices vendor’s available balance is insufficient
- Clicks Reject
- Adds note: “Insufficient available balance. Please verify earnings.”
- Confirms rejection
- Vendor receives notification and can review balance
Best Practices
Before Approving:
- Verify vendor has sufficient available balance
- Check payment account details are complete
- Review vendor’s withdrawal history
- Ensure no disputes or issues with recent orders
Adding Admin Notes:
- Document payment reference numbers
- Note payment method/platform used
- Record any special circumstances
- Provide clear rejection reasons
Payment Processing:
- Process approved withdrawals within 3-5 business days
- Keep external payment records (PayPal transactions, bank transfers)
- Mark completed only after payment is sent
- Respond to vendor questions about delays
Next Steps
- Vendor Management – Managing vendor accounts
- Commission System – Understanding earnings calculation
- Vendor Dashboard – Vendor earnings view
