Balance Calculation Function
PHP
Difficulty Level:
Snippet Description
<?php
/**
* Wallet Balance Management Functions
* Add to theme's functions.php or custom plugin
*/
// Calculate and update user wallet balance
function update_user_wallet_balance($user_id, $amount, $transaction_type, $reference_id = null, $reference_type = null, $description = '') {
global $wpdb;
// Get current balances
$available_balance = (float) get_user_meta($user_id, 'wallet_available_balance', true) ?: 0;
$pending_balance = (float) get_user_meta($user_id, 'wallet_pending_balance', true) ?: 0;
$total_earned = (float) get_user_meta($user_id, 'wallet_total_earned', true) ?: 0;
$balance_before = $available_balance;
$new_balance = $available_balance;
// Calculate new balance based on transaction type
switch($transaction_type) {
case 'donation_received':
$pending_balance += $amount;
$total_earned += $amount;
break;
case 'donation_cleared':
$pending_balance -= $amount;
$new_balance += $amount;
break;
case 'withdrawal':
$new_balance -= $amount;
break;
case 'reversal':
$new_balance -= $amount;
$total_earned -= $amount;
break;
case 'platform_fee':
$new_balance -= $amount;
break;
case 'top_up':
$new_balance += $amount;
break;
}
// Update user meta
update_user_meta($user_id, 'wallet_available_balance', $new_balance);
update_user_meta($user_id, 'wallet_pending_balance', $pending_balance);
update_user_meta($user_id, 'wallet_total_earned', $total_earned);
// Log transaction in CCT
$transaction_id = $wpdb->insert(
$wpdb->prefix . 'jet_cct_wallet_transactions',
array(
'user_id' => $user_id,
'transaction_type' => $transaction_type,
'amount' => $amount,
'reference_id' => $reference_id,
'reference_type' => $reference_type,
'description' => $description,
'balance_before' => $balance_before,
'balance_after' => $new_balance,
'created_at' => current_time('mysql'),
'status' => 'completed'
),
array('%d', '%s', '%f', '%s', '%s', '%s', '%f', '%f', '%s', '%s')
);
do_action('wallet_balance_updated', $user_id, $new_balance, $transaction_type);
return $transaction_id;
}
// Get user's available balance
function get_user_wallet_balance($user_id) {
return (float) get_user_meta($user_id, 'wallet_available_balance', true) ?: 0;
}
// Get user's pending balance
function get_user_pending_balance($user_id) {
return (float) get_user_meta($user_id, 'wallet_pending_balance', true) ?: 0;
}
// Check if user has sufficient balance
function user_has_sufficient_balance($user_id, $amount) {
$available = get_user_wallet_balance($user_id);
return $available >= $amount;
}
Default Bank Account Logic
<?php
/**
* Bank Account Management Functions
*/
// Set a bank account as default
function set_default_bank_account($account_id, $user_id) {
global $wpdb;
$table = $wpdb->prefix . 'jet_cct_bank_accounts';
// First, unset all default accounts for this user
$wpdb->update(
$table,
array('is_default' => 0),
array('user_id' => $user_id),
array('%d'),
array('%d')
);
// Set the new default
$updated = $wpdb->update(
$table,
array('is_default' => 1),
array('_ID' => $account_id, 'user_id' => $user_id),
array('%d'),
array('%d', '%d')
);
if ($updated) {
do_action('bank_account_set_default', $account_id, $user_id);
return true;
}
return false;
}
// Get user's default bank account
function get_default_bank_account($user_id) {
global $wpdb;
$table = $wpdb->prefix . 'jet_cct_bank_accounts';
$account = $wpdb->get_row($wpdb->prepare(
"SELECT * FROM $table WHERE user_id = %d AND is_default = 1 LIMIT 1",
$user_id
));
return $account;
}
// Auto-set first bank account as default
add_action('jet-forms/handler/after-send', 'auto_set_first_bank_account_default', 10, 2);
function auto_set_first_bank_account_default($form_data, $handler) {
// Only run for bank account form
if ($handler->form_id != 'add-bank-account-form') return;
$user_id = get_current_user_id();
global $wpdb;
$table = $wpdb->prefix . 'jet_cct_bank_accounts';
// Check if user has any default accounts
$has_default = $wpdb->get_var($wpdb->prepare(
"SELECT COUNT(*) FROM $table WHERE user_id = %d AND is_default = 1",
$user_id
));
// If no default exists, set this new account as default
if (!$has_default) {
$new_account_id = $wpdb->insert_id; // Get the ID of just inserted account
set_default_bank_account($new_account_id, $user_id);
}
}
// Prevent deletion of last bank account if it's default
add_action('jet-engine/forms/handler/before-delete-item', 'prevent_last_default_account_deletion', 10, 2);
function prevent_last_default_account_deletion($item_id, $handler) {
global $wpdb;
$table = $wpdb->prefix . 'jet_cct_bank_accounts';
$user_id = get_current_user_id();
// Count user's bank accounts
$count = $wpdb->get_var($wpdb->prepare(
"SELECT COUNT(*) FROM $table WHERE user_id = %d",
$user_id
));
// Check if this is the default account
$is_default = $wpdb->get_var($wpdb->prepare(
"SELECT is_default FROM $table WHERE _ID = %d",
$item_id
));
if ($count == 1 || $is_default == 1) {
wp_die('You cannot delete your default or only bank account.');
}
}
Withdrawal Status Updates
<?php
/**
* Withdrawal Management Functions
*/
// Update withdrawal status
function update_withdrawal_status($withdrawal_id, $new_status, $admin_notes = '') {
global $wpdb;
$table = $wpdb->prefix . 'jet_cct_withdrawals';
// Get withdrawal details
$withdrawal = $wpdb->get_row($wpdb->prepare(
"SELECT * FROM $table WHERE _ID = %d",
$withdrawal_id
));
if (!$withdrawal) return false;
$old_status = $withdrawal->status;
$user_id = $withdrawal->user_id;
$amount = $withdrawal->amount;
// Prepare update data
$update_data = array('status' => $new_status);
if (!empty($admin_notes)) {
$update_data['admin_notes'] = $admin_notes;
}
// Set processed_at timestamp for completed/approved status
if (in_array($new_status, array('completed', 'approved'))) {
$update_data['processed_at'] = current_time('mysql');
}
// Update the withdrawal
$updated = $wpdb->update(
$table,
$update_data,
array('_ID' => $withdrawal_id),
array('%s', '%s', '%s'),
array('%d')
);
if (!$updated) return false;
// Handle status-specific actions
switch($new_status) {
case 'approved':
// Deduct from available balance when approved
update_user_wallet_balance(
$user_id,
$amount,
'withdrawal',
$withdrawal_id,
'withdrawal',
'Withdrawal approved and processed'
);
// Update total withdrawn
$total_withdrawn = (float) get_user_meta($user_id, 'wallet_total_withdrawn', true) ?: 0;
update_user_meta($user_id, 'wallet_total_withdrawn', $total_withdrawn + $amount);
do_action('withdrawal_approved', $withdrawal_id, $user_id, $amount);
break;
case 'rejected':
// No balance change needed on rejection
do_action('withdrawal_rejected', $withdrawal_id, $user_id, $amount, $admin_notes);
break;
case 'completed':
// Mark as completed (funds already deducted on approval)
do_action('withdrawal_completed', $withdrawal_id, $user_id, $amount);
break;
}
// Send notification to user
send_withdrawal_status_notification($user_id, $withdrawal_id, $old_status, $new_status);
return true;
}
// Validate withdrawal request before submission
add_filter('jet-forms/handler/before-send', 'validate_withdrawal_request', 10, 2);
function validate_withdrawal_request($is_valid, $handler) {
// Only run for withdrawal form
if ($handler->form_id != 'request-withdrawal-form') return $is_valid;
$user_id = get_current_user_id();
$amount = floatval($_POST['amount']);
// Check sufficient balance
if (!user_has_sufficient_balance($user_id, $amount)) {
$handler->add_error('amount', 'Insufficient balance for this withdrawal.');
return false;
}
// Check minimum withdrawal amount
$min_withdrawal = 10; // Set your minimum
if ($amount < $min_withdrawal) {
$handler->add_error('amount', sprintf('Minimum withdrawal amount is $%s', $min_withdrawal));
return false;
}
// Check if user has a bank account
$has_bank = get_default_bank_account($user_id);
if (!$has_bank) {
$handler->add_error('bank_account', 'Please add a bank account first.');
return false;
}
return $is_valid;
}
// Send email notification on status change
function send_withdrawal_status_notification($user_id, $withdrawal_id, $old_status, $new_status) {
$user = get_userdata($user_id);
$email = $user->user_email;
$subject = 'Withdrawal Request Status Update';
$status_messages = array(
'approved' => 'Your withdrawal request has been approved and is being processed.',
'completed' => 'Your withdrawal has been completed and funds have been sent to your bank account.',
'rejected' => 'Your withdrawal request has been rejected. Please contact support for more information.'
);
$message = sprintf(
'Hi %s,<br><br>%s<br><br>Withdrawal ID: %d<br>Status: %s',
$user->display_name,
$status_messages[$new_status] ?? 'Your withdrawal status has been updated.',
$withdrawal_id,
ucfirst($new_status)
);
wp_mail($email, $subject, $message, array('Content-Type: text/html; charset=UTF-8'));
}
// Admin action hooks for status updates
add_action('admin_post_approve_withdrawal', 'handle_approve_withdrawal');
function handle_approve_withdrawal() {
if (!current_user_can('manage_options')) wp_die('Unauthorized');
$withdrawal_id = intval($_POST['withdrawal_id']);
$admin_notes = sanitize_textarea_field($_POST['admin_notes']);
update_withdrawal_status($withdrawal_id, 'approved', $admin_notes);
wp_redirect(admin_url('admin.php?page=withdrawals&message=approved'));
exit;
}
add_action('admin_post_reject_withdrawal', 'handle_reject_withdrawal');
function handle_reject_withdrawal() {
if (!current_user_can('manage_options')) wp_die('Unauthorized');
$withdrawal_id = intval($_POST['withdrawal_id']);
$admin_notes = sanitize_textarea_field($_POST['admin_notes']);
update_withdrawal_status($withdrawal_id, 'rejected', $admin_notes);
wp_redirect(admin_url('admin.php?page=withdrawals&message=rejected'));
exit;
}
Donation Hook Integration
<?php
/**
* Hook into donation completion to credit wallet
*/
// When a donation is completed, credit the campaign creator's wallet
add_action('donation_completed', 'credit_wallet_on_donation', 10, 3);
function credit_wallet_on_donation($donation_id, $campaign_creator_id, $amount) {
// Add to pending balance initially (funds under review)
update_user_wallet_balance(
$campaign_creator_id,
$amount,
'donation_received',
$donation_id,
'donation',
sprintf('Donation received - Donation ID: %d', $donation_id)
);
// Schedule automatic clearance after 7 days
wp_schedule_single_event(
time() + (7 * DAY_IN_SECONDS),
'clear_pending_donation',
array($campaign_creator_id, $amount, $donation_id)
);
}
// Clear pending donation to available balance
add_action('clear_pending_donation', 'move_pending_to_available', 10, 3);
function move_pending_to_available($user_id, $amount, $donation_id) {
update_user_wallet_balance(
$user_id,
$amount,
'donation_cleared',
$donation_id,
'donation',
sprintf('Donation cleared - Donation ID: %d', $donation_id)
);
}