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)
    );
}


 

Usage Instructions

Table of Contents