🔧 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>
		
			
				
	
	
		
			189 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			189 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?php
 | 
						|
 | 
						|
if (!defined('ABSPATH')) {
 | 
						|
    exit;
 | 
						|
}
 | 
						|
 | 
						|
class WPDD_Earnings_Processor {
 | 
						|
    
 | 
						|
    public static function init() {
 | 
						|
        // Schedule the cron job
 | 
						|
        add_action('wp', array(__CLASS__, 'schedule_earnings_processing'));
 | 
						|
        add_action('wpdd_process_pending_earnings', array(__CLASS__, 'process_pending_earnings'));
 | 
						|
        
 | 
						|
        // Hook to plugin activation/deactivation
 | 
						|
        register_activation_hook(WPDD_PLUGIN_PATH . 'wp-digital-download.php', array(__CLASS__, 'schedule_earnings_processing'));
 | 
						|
        register_deactivation_hook(WPDD_PLUGIN_PATH . 'wp-digital-download.php', array(__CLASS__, 'clear_scheduled_earnings_processing'));
 | 
						|
    }
 | 
						|
    
 | 
						|
    public static function schedule_earnings_processing() {
 | 
						|
        if (!wp_next_scheduled('wpdd_process_pending_earnings')) {
 | 
						|
            // Run every hour to check for earnings that should be made available
 | 
						|
            wp_schedule_event(time(), 'hourly', 'wpdd_process_pending_earnings');
 | 
						|
        }
 | 
						|
    }
 | 
						|
    
 | 
						|
    public static function clear_scheduled_earnings_processing() {
 | 
						|
        wp_clear_scheduled_hook('wpdd_process_pending_earnings');
 | 
						|
    }
 | 
						|
    
 | 
						|
    public static function process_pending_earnings() {
 | 
						|
        global $wpdb;
 | 
						|
        
 | 
						|
        // Find all earnings that are pending and past their available_at date
 | 
						|
        $pending_earnings = $wpdb->get_results(
 | 
						|
            "SELECT * FROM {$wpdb->prefix}wpdd_creator_earnings 
 | 
						|
             WHERE payout_status = 'pending' 
 | 
						|
             AND available_at <= NOW() 
 | 
						|
             AND available_at IS NOT NULL"
 | 
						|
        );
 | 
						|
        
 | 
						|
        if (empty($pending_earnings)) {
 | 
						|
            return; // Nothing to process
 | 
						|
        }
 | 
						|
        
 | 
						|
        // Update all pending earnings to available
 | 
						|
        $updated = $wpdb->query(
 | 
						|
            "UPDATE {$wpdb->prefix}wpdd_creator_earnings 
 | 
						|
             SET payout_status = 'available' 
 | 
						|
             WHERE payout_status = 'pending' 
 | 
						|
             AND available_at <= NOW() 
 | 
						|
             AND available_at IS NOT NULL"
 | 
						|
        );
 | 
						|
        
 | 
						|
        if ($updated > 0) {
 | 
						|
            // Log the processing
 | 
						|
            error_log("WPDD: Processed $updated pending earnings to available status");
 | 
						|
            
 | 
						|
            // Update creator balances for affected creators
 | 
						|
            $affected_creators = $wpdb->get_col(
 | 
						|
                "SELECT DISTINCT creator_id 
 | 
						|
                 FROM {$wpdb->prefix}wpdd_creator_earnings 
 | 
						|
                 WHERE payout_status = 'available' 
 | 
						|
                 AND available_at <= NOW()"
 | 
						|
            );
 | 
						|
            
 | 
						|
            foreach ($affected_creators as $creator_id) {
 | 
						|
                // Trigger balance recalculation
 | 
						|
                $current_balance = WPDD_Creator::get_creator_balance($creator_id);
 | 
						|
                update_user_meta($creator_id, 'wpdd_creator_balance', $current_balance);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    
 | 
						|
    /**
 | 
						|
     * Manually process a specific earning (for admin override)
 | 
						|
     */
 | 
						|
    public static function release_earning_immediately($earning_id) {
 | 
						|
        global $wpdb;
 | 
						|
        
 | 
						|
        $result = $wpdb->update(
 | 
						|
            $wpdb->prefix . 'wpdd_creator_earnings',
 | 
						|
            array(
 | 
						|
                'payout_status' => 'available',
 | 
						|
                'available_at' => current_time('mysql')
 | 
						|
            ),
 | 
						|
            array(
 | 
						|
                'id' => $earning_id,
 | 
						|
                'payout_status' => 'pending'
 | 
						|
            ),
 | 
						|
            array('%s', '%s'),
 | 
						|
            array('%d', '%s')
 | 
						|
        );
 | 
						|
        
 | 
						|
        if ($result) {
 | 
						|
            // Get the creator and update their balance
 | 
						|
            $earning = $wpdb->get_row($wpdb->prepare(
 | 
						|
                "SELECT creator_id FROM {$wpdb->prefix}wpdd_creator_earnings WHERE id = %d",
 | 
						|
                $earning_id
 | 
						|
            ));
 | 
						|
            
 | 
						|
            if ($earning) {
 | 
						|
                $current_balance = WPDD_Creator::get_creator_balance($earning->creator_id);
 | 
						|
                update_user_meta($earning->creator_id, 'wpdd_creator_balance', $current_balance);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        
 | 
						|
        return $result > 0;
 | 
						|
    }
 | 
						|
    
 | 
						|
    /**
 | 
						|
     * Cancel/refund a specific earning (for order cancellations)
 | 
						|
     */
 | 
						|
    public static function cancel_earning($earning_id, $reason = 'Order cancelled/refunded') {
 | 
						|
        global $wpdb;
 | 
						|
        
 | 
						|
        // Get the earning details
 | 
						|
        $earning = $wpdb->get_row($wpdb->prepare(
 | 
						|
            "SELECT * FROM {$wpdb->prefix}wpdd_creator_earnings WHERE id = %d",
 | 
						|
            $earning_id
 | 
						|
        ));
 | 
						|
        
 | 
						|
        if (!$earning) {
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
        
 | 
						|
        // Only cancel if not already paid out
 | 
						|
        if ($earning->payout_status === 'paid') {
 | 
						|
            return false; // Cannot cancel paid earnings
 | 
						|
        }
 | 
						|
        
 | 
						|
        // Update to cancelled status
 | 
						|
        $result = $wpdb->update(
 | 
						|
            $wpdb->prefix . 'wpdd_creator_earnings',
 | 
						|
            array(
 | 
						|
                'payout_status' => 'cancelled',
 | 
						|
                'available_at' => null
 | 
						|
            ),
 | 
						|
            array('id' => $earning_id),
 | 
						|
            array('%s', '%s'),
 | 
						|
            array('%d')
 | 
						|
        );
 | 
						|
        
 | 
						|
        if ($result) {
 | 
						|
            // Log the cancellation
 | 
						|
            $wpdb->insert(
 | 
						|
                $wpdb->prefix . 'wpdd_balance_adjustments',
 | 
						|
                array(
 | 
						|
                    'creator_id' => $earning->creator_id,
 | 
						|
                    'adjustment_type' => 'subtract',
 | 
						|
                    'amount' => $earning->creator_earning,
 | 
						|
                    'previous_balance' => WPDD_Creator::get_creator_balance($earning->creator_id),
 | 
						|
                    'new_balance' => WPDD_Creator::get_creator_balance($earning->creator_id) - $earning->creator_earning,
 | 
						|
                    'reason' => $reason . ' (Order #' . $earning->order_id . ')',
 | 
						|
                    'adjusted_by' => get_current_user_id(),
 | 
						|
                    'created_at' => current_time('mysql')
 | 
						|
                ),
 | 
						|
                array('%d', '%s', '%f', '%f', '%f', '%s', '%d', '%s')
 | 
						|
            );
 | 
						|
            
 | 
						|
            // Update creator balance
 | 
						|
            $current_balance = WPDD_Creator::get_creator_balance($earning->creator_id);
 | 
						|
            update_user_meta($earning->creator_id, 'wpdd_creator_balance', $current_balance - $earning->creator_earning);
 | 
						|
        }
 | 
						|
        
 | 
						|
        return $result > 0;
 | 
						|
    }
 | 
						|
    
 | 
						|
    /**
 | 
						|
     * Get earnings summary for a creator
 | 
						|
     */
 | 
						|
    public static function get_earnings_summary($creator_id) {
 | 
						|
        global $wpdb;
 | 
						|
        
 | 
						|
        $summary = $wpdb->get_row($wpdb->prepare(
 | 
						|
            "SELECT 
 | 
						|
                COUNT(*) as total_earnings,
 | 
						|
                SUM(CASE WHEN payout_status = 'pending' THEN creator_earning ELSE 0 END) as pending_amount,
 | 
						|
                SUM(CASE WHEN payout_status = 'available' THEN creator_earning ELSE 0 END) as available_amount,
 | 
						|
                SUM(CASE WHEN payout_status = 'paid' THEN creator_earning ELSE 0 END) as paid_amount,
 | 
						|
                SUM(CASE WHEN payout_status = 'cancelled' THEN creator_earning ELSE 0 END) as cancelled_amount,
 | 
						|
                SUM(creator_earning) as total_amount
 | 
						|
             FROM {$wpdb->prefix}wpdd_creator_earnings 
 | 
						|
             WHERE creator_id = %d",
 | 
						|
            $creator_id
 | 
						|
        ));
 | 
						|
        
 | 
						|
        return $summary;
 | 
						|
    }
 | 
						|
} |