🔧 Bug Fixes: - Fixed download limits defaulting to 5 instead of 0 for unlimited downloads - Fixed software license filename sanitization (spaces→dashes, dots→underscores, proper .zip extension) - Software downloads now show as "Test-Plugin-v2-2-0.zip" instead of "Test Plugin v2.2.0" ✨ UI/UX Enhancements: - Redesigned license key display to span full table width with FontAwesome copy icons - Added responsive CSS styling for license key rows - Integrated FontAwesome CDN for modern copy icons 🏗️ Architecture Improvements: - Added comprehensive filename sanitization in both download handler and API paths - Enhanced software license product handling for local package files - Improved error handling and logging throughout download processes 📦 Infrastructure: - Added Gitea workflows for automated releases on push to main - Created comprehensive .gitignore excluding test files and browser automation - Updated documentation with all recent improvements and technical insights 🔍 Technical Details: - Software license products served from wp-content/uploads/wpdd-packages/ - Download flow: token → process_download_by_token() → process_download() → deliver_file() - Dual path coverage for both API downloads and regular file delivery - Version placeholder system for automated deployment 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			245 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			245 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?php
 | 
						|
 | 
						|
if (!defined('ABSPATH')) {
 | 
						|
    exit;
 | 
						|
}
 | 
						|
 | 
						|
class WPDD_Creator {
 | 
						|
    
 | 
						|
    public static function init() {
 | 
						|
        add_action('show_user_profile', array(__CLASS__, 'add_profile_fields'));
 | 
						|
        add_action('edit_user_profile', array(__CLASS__, 'add_profile_fields'));
 | 
						|
        add_action('personal_options_update', array(__CLASS__, 'save_profile_fields'));
 | 
						|
        add_action('edit_user_profile_update', array(__CLASS__, 'save_profile_fields'));
 | 
						|
        add_action('user_register', array(__CLASS__, 'set_default_fields'));
 | 
						|
        add_action('wpdd_order_completed', array(__CLASS__, 'add_earnings_to_balance'));
 | 
						|
    }
 | 
						|
    
 | 
						|
    public static function add_profile_fields($user) {
 | 
						|
        // Only show for creators and admins
 | 
						|
        if (!self::is_creator($user->ID) && !current_user_can('manage_options')) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        ?>
 | 
						|
        <h3><?php _e('Creator Payout Settings', 'wp-digital-download'); ?></h3>
 | 
						|
        <table class="form-table">
 | 
						|
            <tr>
 | 
						|
                <th><label for="wpdd_paypal_email"><?php _e('PayPal Email', 'wp-digital-download'); ?></label></th>
 | 
						|
                <td>
 | 
						|
                    <input type="email" name="wpdd_paypal_email" id="wpdd_paypal_email" 
 | 
						|
                           value="<?php echo esc_attr(get_user_meta($user->ID, 'wpdd_paypal_email', true)); ?>" 
 | 
						|
                           class="regular-text" />
 | 
						|
                    <p class="description"><?php _e('PayPal email address for receiving payouts', 'wp-digital-download'); ?></p>
 | 
						|
                </td>
 | 
						|
            </tr>
 | 
						|
            <?php if (current_user_can('manage_options')) : ?>
 | 
						|
            <tr>
 | 
						|
                <th><label><?php _e('Creator Balance', 'wp-digital-download'); ?></label></th>
 | 
						|
                <td>
 | 
						|
                    <?php 
 | 
						|
                    $balance = self::get_creator_balance($user->ID);
 | 
						|
                    $currency = get_option('wpdd_currency', 'USD');
 | 
						|
                    echo '<strong>' . wpdd_format_price($balance, $currency) . '</strong>';
 | 
						|
                    ?>
 | 
						|
                    <p class="description"><?php _e('Current unpaid earnings balance', 'wp-digital-download'); ?></p>
 | 
						|
                </td>
 | 
						|
            </tr>
 | 
						|
            <tr>
 | 
						|
                <th><label><?php _e('Total Earnings', 'wp-digital-download'); ?></label></th>
 | 
						|
                <td>
 | 
						|
                    <?php 
 | 
						|
                    $total = self::get_creator_total_earnings($user->ID);
 | 
						|
                    echo '<strong>' . wpdd_format_price($total, $currency) . '</strong>';
 | 
						|
                    ?>
 | 
						|
                    <p class="description"><?php _e('Total lifetime earnings', 'wp-digital-download'); ?></p>
 | 
						|
                </td>
 | 
						|
            </tr>
 | 
						|
            <?php endif; ?>
 | 
						|
        </table>
 | 
						|
        <?php
 | 
						|
    }
 | 
						|
    
 | 
						|
    public static function save_profile_fields($user_id) {
 | 
						|
        if (!current_user_can('edit_user', $user_id)) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        
 | 
						|
        if (isset($_POST['wpdd_paypal_email'])) {
 | 
						|
            $email = sanitize_email($_POST['wpdd_paypal_email']);
 | 
						|
            if (!empty($email) && !is_email($email)) {
 | 
						|
                return;
 | 
						|
            }
 | 
						|
            update_user_meta($user_id, 'wpdd_paypal_email', $email);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    
 | 
						|
    public static function set_default_fields($user_id) {
 | 
						|
        if (self::is_creator($user_id)) {
 | 
						|
            update_user_meta($user_id, 'wpdd_creator_balance', 0);
 | 
						|
            update_user_meta($user_id, 'wpdd_total_earnings', 0);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    
 | 
						|
    public static function is_creator($user_id) {
 | 
						|
        $user = get_userdata($user_id);
 | 
						|
        return $user && in_array('wpdd_creator', (array) $user->roles);
 | 
						|
    }
 | 
						|
    
 | 
						|
    public static function get_creator_balance($user_id) {
 | 
						|
        global $wpdb;
 | 
						|
        
 | 
						|
        // Get balance from user meta (for backward compatibility and manual adjustments)
 | 
						|
        $meta_balance = floatval(get_user_meta($user_id, 'wpdd_creator_balance', true));
 | 
						|
        
 | 
						|
        // If we have the creator_earnings table, calculate from there
 | 
						|
        $table_exists = $wpdb->get_var("SHOW TABLES LIKE '{$wpdb->prefix}wpdd_creator_earnings'") == $wpdb->prefix . 'wpdd_creator_earnings';
 | 
						|
        
 | 
						|
        if ($table_exists) {
 | 
						|
            // Check if payout_status column exists
 | 
						|
            $columns = $wpdb->get_results("SHOW COLUMNS FROM {$wpdb->prefix}wpdd_creator_earnings");
 | 
						|
            $has_payout_status = false;
 | 
						|
            foreach ($columns as $column) {
 | 
						|
                if ($column->Field == 'payout_status') {
 | 
						|
                    $has_payout_status = true;
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
            }
 | 
						|
            
 | 
						|
            if ($has_payout_status) {
 | 
						|
                // Calculate available earnings (not pending, not paid)
 | 
						|
                $available_earnings = $wpdb->get_var($wpdb->prepare(
 | 
						|
                    "SELECT SUM(creator_earning) 
 | 
						|
                     FROM {$wpdb->prefix}wpdd_creator_earnings 
 | 
						|
                     WHERE creator_id = %d 
 | 
						|
                     AND payout_status = 'available'",
 | 
						|
                    $user_id
 | 
						|
                ));
 | 
						|
                
 | 
						|
                // Calculate balance adjustments
 | 
						|
                $adjustments_table_exists = $wpdb->get_var("SHOW TABLES LIKE '{$wpdb->prefix}wpdd_balance_adjustments'") == $wpdb->prefix . 'wpdd_balance_adjustments';
 | 
						|
                $total_adjustments = 0;
 | 
						|
                
 | 
						|
                if ($adjustments_table_exists) {
 | 
						|
                    $total_adjustments = $wpdb->get_var($wpdb->prepare(
 | 
						|
                        "SELECT SUM(CASE 
 | 
						|
                            WHEN adjustment_type = 'add' THEN amount 
 | 
						|
                            WHEN adjustment_type = 'subtract' THEN -amount 
 | 
						|
                            ELSE 0 
 | 
						|
                         END) 
 | 
						|
                         FROM {$wpdb->prefix}wpdd_balance_adjustments 
 | 
						|
                         WHERE creator_id = %d",
 | 
						|
                        $user_id
 | 
						|
                    ));
 | 
						|
                }
 | 
						|
                
 | 
						|
                $calculated_balance = floatval($available_earnings) + floatval($total_adjustments);
 | 
						|
                
 | 
						|
                // Update the meta if different
 | 
						|
                if (abs($calculated_balance - $meta_balance) > 0.01) {
 | 
						|
                    update_user_meta($user_id, 'wpdd_creator_balance', $calculated_balance);
 | 
						|
                }
 | 
						|
                
 | 
						|
                return $calculated_balance;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        
 | 
						|
        // Fall back to meta balance
 | 
						|
        return $meta_balance;
 | 
						|
    }
 | 
						|
    
 | 
						|
    public static function get_creator_total_earnings($user_id) {
 | 
						|
        global $wpdb;
 | 
						|
        
 | 
						|
        $total = $wpdb->get_var($wpdb->prepare(
 | 
						|
            "SELECT SUM(o.amount) 
 | 
						|
             FROM {$wpdb->prefix}wpdd_orders o
 | 
						|
             INNER JOIN {$wpdb->posts} p ON o.product_id = p.ID
 | 
						|
             WHERE p.post_author = %d 
 | 
						|
             AND o.status = 'completed'",
 | 
						|
            $user_id
 | 
						|
        ));
 | 
						|
        
 | 
						|
        return floatval($total);
 | 
						|
    }
 | 
						|
    
 | 
						|
    public static function get_creator_net_earnings($user_id) {
 | 
						|
        $total = self::get_creator_total_earnings($user_id);
 | 
						|
        $commission_rate = floatval(get_option('wpdd_commission_rate', 0));
 | 
						|
        $net = $total * (1 - ($commission_rate / 100));
 | 
						|
        return $net;
 | 
						|
    }
 | 
						|
    
 | 
						|
    public static function add_earnings_to_balance($order_id) {
 | 
						|
        global $wpdb;
 | 
						|
        
 | 
						|
        $order = $wpdb->get_row($wpdb->prepare(
 | 
						|
            "SELECT * FROM {$wpdb->prefix}wpdd_orders WHERE id = %d",
 | 
						|
            $order_id
 | 
						|
        ));
 | 
						|
        
 | 
						|
        if (!$order || $order->status !== 'completed') {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        
 | 
						|
        $product = get_post($order->product_id);
 | 
						|
        if (!$product) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        
 | 
						|
        $creator_id = $product->post_author;
 | 
						|
        $commission_rate = floatval(get_option('wpdd_commission_rate', 0));
 | 
						|
        $creator_share = $order->amount * (1 - ($commission_rate / 100));
 | 
						|
        
 | 
						|
        // Update creator balance
 | 
						|
        $current_balance = self::get_creator_balance($creator_id);
 | 
						|
        update_user_meta($creator_id, 'wpdd_creator_balance', $current_balance + $creator_share);
 | 
						|
        
 | 
						|
        // Calculate when earnings will be available (holding period)
 | 
						|
        $holding_days = intval(get_option('wpdd_earnings_holding_days', 15));
 | 
						|
        $available_at = ($holding_days > 0) ? 
 | 
						|
            date('Y-m-d H:i:s', strtotime('+' . $holding_days . ' days')) : 
 | 
						|
            current_time('mysql');
 | 
						|
        
 | 
						|
        $initial_status = ($holding_days > 0) ? 'pending' : 'available';
 | 
						|
        
 | 
						|
        // Log the earning
 | 
						|
        $wpdb->insert(
 | 
						|
            $wpdb->prefix . 'wpdd_creator_earnings',
 | 
						|
            array(
 | 
						|
                'creator_id' => $creator_id,
 | 
						|
                'order_id' => $order_id,
 | 
						|
                'product_id' => $order->product_id,
 | 
						|
                'sale_amount' => $order->amount,
 | 
						|
                'commission_rate' => $commission_rate,
 | 
						|
                'creator_earning' => $creator_share,
 | 
						|
                'payout_status' => $initial_status,
 | 
						|
                'available_at' => $available_at,
 | 
						|
                'created_at' => current_time('mysql')
 | 
						|
            ),
 | 
						|
            array('%d', '%d', '%d', '%f', '%f', '%f', '%s', '%s', '%s')
 | 
						|
        );
 | 
						|
    }
 | 
						|
    
 | 
						|
    public static function get_creators_with_balance() {
 | 
						|
        global $wpdb;
 | 
						|
        
 | 
						|
        $threshold = floatval(get_option('wpdd_payout_threshold', 0));
 | 
						|
        
 | 
						|
        $query = "SELECT u.ID, u.display_name, u.user_email, 
 | 
						|
                         um1.meta_value as paypal_email,
 | 
						|
                         um2.meta_value as balance
 | 
						|
                  FROM {$wpdb->users} u
 | 
						|
                  INNER JOIN {$wpdb->usermeta} um ON u.ID = um.user_id
 | 
						|
                  LEFT JOIN {$wpdb->usermeta} um1 ON u.ID = um1.user_id AND um1.meta_key = 'wpdd_paypal_email'
 | 
						|
                  LEFT JOIN {$wpdb->usermeta} um2 ON u.ID = um2.user_id AND um2.meta_key = 'wpdd_creator_balance'
 | 
						|
                  WHERE um.meta_key = '{$wpdb->prefix}capabilities'
 | 
						|
                  AND um.meta_value LIKE '%wpdd_creator%'
 | 
						|
                  AND CAST(um2.meta_value AS DECIMAL(10,2)) > 0";
 | 
						|
        
 | 
						|
        if ($threshold > 0) {
 | 
						|
            $query .= $wpdb->prepare(" AND CAST(um2.meta_value AS DECIMAL(10,2)) >= %f", $threshold);
 | 
						|
        }
 | 
						|
        
 | 
						|
        return $wpdb->get_results($query);
 | 
						|
    }
 | 
						|
}
 |