🔧 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>
		
			
				
	
	
		
			470 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			470 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?php
 | 
						|
 | 
						|
if (!defined('ABSPATH')) {
 | 
						|
    exit;
 | 
						|
}
 | 
						|
 | 
						|
class WPDD_Order_Manager {
 | 
						|
    
 | 
						|
    public static function init() {
 | 
						|
        add_action('admin_menu', array(__CLASS__, 'add_menu_page'));
 | 
						|
        add_action('admin_post_wpdd_cancel_order', array(__CLASS__, 'handle_cancel_order'));
 | 
						|
        add_action('admin_post_wpdd_refund_order', array(__CLASS__, 'handle_refund_order'));
 | 
						|
        add_action('admin_post_wpdd_release_earnings', array(__CLASS__, 'handle_release_earnings'));
 | 
						|
        add_action('admin_enqueue_scripts', array(__CLASS__, 'enqueue_scripts'));
 | 
						|
    }
 | 
						|
    
 | 
						|
    public static function add_menu_page() {
 | 
						|
        if (!current_user_can('manage_options')) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        
 | 
						|
        add_submenu_page(
 | 
						|
            'edit.php?post_type=wpdd_product',
 | 
						|
            __('Order Management', 'wp-digital-download'),
 | 
						|
            __('Order Manager', 'wp-digital-download'),
 | 
						|
            'manage_options',
 | 
						|
            'wpdd-order-manager',
 | 
						|
            array(__CLASS__, 'render_page')
 | 
						|
        );
 | 
						|
    }
 | 
						|
    
 | 
						|
    public static function enqueue_scripts($hook) {
 | 
						|
        if ($hook !== 'wpdd_product_page_wpdd-order-manager') {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        
 | 
						|
        wp_enqueue_script('wpdd-order-manager', WPDD_PLUGIN_URL . 'assets/js/admin-order-manager.js', array('jquery'), WPDD_VERSION, true);
 | 
						|
        wp_localize_script('wpdd-order-manager', 'wpdd_order_manager', array(
 | 
						|
            'ajax_url' => admin_url('admin-ajax.php'),
 | 
						|
            'nonce' => wp_create_nonce('wpdd_order_manager'),
 | 
						|
            'confirm_cancel' => __('Are you sure you want to cancel this order? This action cannot be undone.', 'wp-digital-download'),
 | 
						|
            'confirm_refund' => __('Are you sure you want to process this refund? The customer will lose access to the product.', 'wp-digital-download'),
 | 
						|
            'confirm_release' => __('Are you sure you want to release these earnings immediately?', 'wp-digital-download')
 | 
						|
        ));
 | 
						|
    }
 | 
						|
    
 | 
						|
    public static function render_page() {
 | 
						|
        global $wpdb;
 | 
						|
        
 | 
						|
        // Get filter parameters
 | 
						|
        $status_filter = isset($_GET['status']) ? sanitize_text_field($_GET['status']) : 'all';
 | 
						|
        $creator_filter = isset($_GET['creator']) ? intval($_GET['creator']) : 0;
 | 
						|
        $date_from = isset($_GET['date_from']) ? sanitize_text_field($_GET['date_from']) : date('Y-m-d', strtotime('-30 days'));
 | 
						|
        $date_to = isset($_GET['date_to']) ? sanitize_text_field($_GET['date_to']) : date('Y-m-d');
 | 
						|
        
 | 
						|
        // Build query
 | 
						|
        $where_conditions = array('1=1');
 | 
						|
        $query_params = array();
 | 
						|
        
 | 
						|
        if ($status_filter !== 'all') {
 | 
						|
            $where_conditions[] = 'o.status = %s';
 | 
						|
            $query_params[] = $status_filter;
 | 
						|
        }
 | 
						|
        
 | 
						|
        if ($creator_filter > 0) {
 | 
						|
            $where_conditions[] = 'p.post_author = %d';
 | 
						|
            $query_params[] = $creator_filter;
 | 
						|
        }
 | 
						|
        
 | 
						|
        if ($date_from) {
 | 
						|
            $where_conditions[] = 'DATE(o.purchase_date) >= %s';
 | 
						|
            $query_params[] = $date_from;
 | 
						|
        }
 | 
						|
        
 | 
						|
        if ($date_to) {
 | 
						|
            $where_conditions[] = 'DATE(o.purchase_date) <= %s';
 | 
						|
            $query_params[] = $date_to;
 | 
						|
        }
 | 
						|
        
 | 
						|
        $where_clause = implode(' AND ', $where_conditions);
 | 
						|
        
 | 
						|
        // Get orders with earnings status
 | 
						|
        $orders = $wpdb->get_results($wpdb->prepare(
 | 
						|
            "SELECT o.*, 
 | 
						|
                    p.post_title as product_name, 
 | 
						|
                    u.display_name as creator_name,
 | 
						|
                    e.payout_status,
 | 
						|
                    e.available_at,
 | 
						|
                    e.creator_earning,
 | 
						|
                    e.id as earning_id
 | 
						|
             FROM {$wpdb->prefix}wpdd_orders o
 | 
						|
             LEFT JOIN {$wpdb->posts} p ON o.product_id = p.ID
 | 
						|
             LEFT JOIN {$wpdb->users} u ON p.post_author = u.ID
 | 
						|
             LEFT JOIN {$wpdb->prefix}wpdd_creator_earnings e ON o.id = e.order_id
 | 
						|
             WHERE $where_clause
 | 
						|
             ORDER BY o.purchase_date DESC
 | 
						|
             LIMIT 100",
 | 
						|
            $query_params
 | 
						|
        ));
 | 
						|
        
 | 
						|
        // Get creators for filter
 | 
						|
        $creators = get_users(array('role' => 'wpdd_creator'));
 | 
						|
        
 | 
						|
        ?>
 | 
						|
        <div class="wrap">
 | 
						|
            <h1><?php _e('Order Management', 'wp-digital-download'); ?></h1>
 | 
						|
            
 | 
						|
            <?php if (isset($_GET['message'])) : ?>
 | 
						|
                <?php if ($_GET['message'] === 'cancelled') : ?>
 | 
						|
                    <div class="notice notice-success is-dismissible">
 | 
						|
                        <p><?php _e('Order cancelled successfully.', 'wp-digital-download'); ?></p>
 | 
						|
                    </div>
 | 
						|
                <?php elseif ($_GET['message'] === 'refunded') : ?>
 | 
						|
                    <div class="notice notice-success is-dismissible">
 | 
						|
                        <p><?php _e('Refund processed successfully.', 'wp-digital-download'); ?></p>
 | 
						|
                    </div>
 | 
						|
                <?php elseif ($_GET['message'] === 'released') : ?>
 | 
						|
                    <div class="notice notice-success is-dismissible">
 | 
						|
                        <p><?php _e('Earnings released successfully.', 'wp-digital-download'); ?></p>
 | 
						|
                    </div>
 | 
						|
                <?php elseif ($_GET['message'] === 'error') : ?>
 | 
						|
                    <div class="notice notice-error is-dismissible">
 | 
						|
                        <p><?php _e('Error processing request. Please try again.', 'wp-digital-download'); ?></p>
 | 
						|
                    </div>
 | 
						|
                <?php endif; ?>
 | 
						|
            <?php endif; ?>
 | 
						|
            
 | 
						|
            <!-- Filter Form -->
 | 
						|
            <div class="wpdd-filter-box" style="background: white; padding: 20px; margin: 20px 0; border: 1px solid #ccd0d4;">
 | 
						|
                <h3><?php _e('Filters', 'wp-digital-download'); ?></h3>
 | 
						|
                <form method="get" action="">
 | 
						|
                    <input type="hidden" name="post_type" value="wpdd_product">
 | 
						|
                    <input type="hidden" name="page" value="wpdd-order-manager">
 | 
						|
                    
 | 
						|
                    <table class="form-table">
 | 
						|
                        <tr>
 | 
						|
                            <th><label for="status"><?php _e('Order Status', 'wp-digital-download'); ?></label></th>
 | 
						|
                            <td>
 | 
						|
                                <select name="status" id="status">
 | 
						|
                                    <option value="all"><?php _e('All Statuses', 'wp-digital-download'); ?></option>
 | 
						|
                                    <option value="completed" <?php selected($status_filter, 'completed'); ?>><?php _e('Completed', 'wp-digital-download'); ?></option>
 | 
						|
                                    <option value="pending" <?php selected($status_filter, 'pending'); ?>><?php _e('Pending', 'wp-digital-download'); ?></option>
 | 
						|
                                    <option value="failed" <?php selected($status_filter, 'failed'); ?>><?php _e('Failed', 'wp-digital-download'); ?></option>
 | 
						|
                                    <option value="cancelled" <?php selected($status_filter, 'cancelled'); ?>><?php _e('Cancelled', 'wp-digital-download'); ?></option>
 | 
						|
                                </select>
 | 
						|
                            </td>
 | 
						|
                        </tr>
 | 
						|
                        <tr>
 | 
						|
                            <th><label for="creator"><?php _e('Creator', 'wp-digital-download'); ?></label></th>
 | 
						|
                            <td>
 | 
						|
                                <select name="creator" id="creator">
 | 
						|
                                    <option value="0"><?php _e('All Creators', 'wp-digital-download'); ?></option>
 | 
						|
                                    <?php foreach ($creators as $creator) : ?>
 | 
						|
                                    <option value="<?php echo esc_attr($creator->ID); ?>" <?php selected($creator_filter, $creator->ID); ?>>
 | 
						|
                                        <?php echo esc_html($creator->display_name); ?>
 | 
						|
                                    </option>
 | 
						|
                                    <?php endforeach; ?>
 | 
						|
                                </select>
 | 
						|
                            </td>
 | 
						|
                        </tr>
 | 
						|
                        <tr>
 | 
						|
                            <th><label for="date_from"><?php _e('Date Range', 'wp-digital-download'); ?></label></th>
 | 
						|
                            <td>
 | 
						|
                                <input type="date" name="date_from" id="date_from" value="<?php echo esc_attr($date_from); ?>">
 | 
						|
                                <?php _e('to', 'wp-digital-download'); ?>
 | 
						|
                                <input type="date" name="date_to" id="date_to" value="<?php echo esc_attr($date_to); ?>">
 | 
						|
                            </td>
 | 
						|
                        </tr>
 | 
						|
                    </table>
 | 
						|
                    
 | 
						|
                    <p class="submit">
 | 
						|
                        <input type="submit" class="button button-primary" value="<?php _e('Filter Orders', 'wp-digital-download'); ?>">
 | 
						|
                    </p>
 | 
						|
                </form>
 | 
						|
            </div>
 | 
						|
            
 | 
						|
            <!-- Orders Table -->
 | 
						|
            <div class="wpdd-orders-table" style="background: white; padding: 20px; margin: 20px 0; border: 1px solid #ccd0d4;">
 | 
						|
                <h3><?php _e('Orders', 'wp-digital-download'); ?> (<?php echo count($orders); ?>)</h3>
 | 
						|
                
 | 
						|
                <?php if (empty($orders)) : ?>
 | 
						|
                    <p><?php _e('No orders found for the selected criteria.', 'wp-digital-download'); ?></p>
 | 
						|
                <?php else : ?>
 | 
						|
                    <table class="wp-list-table widefat fixed striped">
 | 
						|
                        <thead>
 | 
						|
                            <tr>
 | 
						|
                                <th><?php _e('Order', 'wp-digital-download'); ?></th>
 | 
						|
                                <th><?php _e('Customer', 'wp-digital-download'); ?></th>
 | 
						|
                                <th><?php _e('Product', 'wp-digital-download'); ?></th>
 | 
						|
                                <th><?php _e('Creator', 'wp-digital-download'); ?></th>
 | 
						|
                                <th><?php _e('Amount', 'wp-digital-download'); ?></th>
 | 
						|
                                <th><?php _e('Date', 'wp-digital-download'); ?></th>
 | 
						|
                                <th><?php _e('Status', 'wp-digital-download'); ?></th>
 | 
						|
                                <th><?php _e('Earnings Status', 'wp-digital-download'); ?></th>
 | 
						|
                                <th><?php _e('Actions', 'wp-digital-download'); ?></th>
 | 
						|
                            </tr>
 | 
						|
                        </thead>
 | 
						|
                        <tbody>
 | 
						|
                            <?php foreach ($orders as $order) : ?>
 | 
						|
                            <tr>
 | 
						|
                                <td>
 | 
						|
                                    <strong><?php echo esc_html($order->order_number); ?></strong><br>
 | 
						|
                                    <small>#<?php echo esc_html($order->id); ?></small>
 | 
						|
                                </td>
 | 
						|
                                <td>
 | 
						|
                                    <strong><?php echo esc_html($order->customer_name); ?></strong><br>
 | 
						|
                                    <small><?php echo esc_html($order->customer_email); ?></small>
 | 
						|
                                </td>
 | 
						|
                                <td>
 | 
						|
                                    <strong><?php echo esc_html($order->product_name); ?></strong><br>
 | 
						|
                                    <small>ID: <?php echo esc_html($order->product_id); ?></small>
 | 
						|
                                </td>
 | 
						|
                                <td><?php echo esc_html($order->creator_name); ?></td>
 | 
						|
                                <td><strong><?php echo wpdd_format_price($order->amount); ?></strong></td>
 | 
						|
                                <td><?php echo esc_html(date_i18n(get_option('date_format'), strtotime($order->purchase_date))); ?></td>
 | 
						|
                                <td>
 | 
						|
                                    <?php 
 | 
						|
                                    $status_class = '';
 | 
						|
                                    switch($order->status) {
 | 
						|
                                        case 'completed':
 | 
						|
                                            $status_class = 'notice-success';
 | 
						|
                                            break;
 | 
						|
                                        case 'cancelled':
 | 
						|
                                        case 'failed':
 | 
						|
                                            $status_class = 'notice-error';
 | 
						|
                                            break;
 | 
						|
                                        case 'pending':
 | 
						|
                                            $status_class = 'notice-warning';
 | 
						|
                                            break;
 | 
						|
                                    }
 | 
						|
                                    ?>
 | 
						|
                                    <span class="notice <?php echo esc_attr($status_class); ?>" style="padding: 2px 8px; display: inline-block;">
 | 
						|
                                        <?php echo esc_html(ucfirst($order->status)); ?>
 | 
						|
                                    </span>
 | 
						|
                                </td>
 | 
						|
                                <td>
 | 
						|
                                    <?php if ($order->payout_status) : ?>
 | 
						|
                                        <?php 
 | 
						|
                                        $earnings_class = '';
 | 
						|
                                        $earnings_text = ucfirst($order->payout_status);
 | 
						|
                                        switch($order->payout_status) {
 | 
						|
                                            case 'pending':
 | 
						|
                                                $earnings_class = 'notice-info';
 | 
						|
                                                if ($order->available_at) {
 | 
						|
                                                    $earnings_text .= '<br><small>Until: ' . date('M j', strtotime($order->available_at)) . '</small>';
 | 
						|
                                                }
 | 
						|
                                                break;
 | 
						|
                                            case 'available':
 | 
						|
                                                $earnings_class = 'notice-warning';
 | 
						|
                                                break;
 | 
						|
                                            case 'paid':
 | 
						|
                                                $earnings_class = 'notice-success';
 | 
						|
                                                break;
 | 
						|
                                            case 'cancelled':
 | 
						|
                                                $earnings_class = 'notice-error';
 | 
						|
                                                break;
 | 
						|
                                        }
 | 
						|
                                        ?>
 | 
						|
                                        <span class="notice <?php echo esc_attr($earnings_class); ?>" style="padding: 2px 8px; display: inline-block;">
 | 
						|
                                            <?php echo $earnings_text; ?>
 | 
						|
                                        </span>
 | 
						|
                                        <?php if ($order->creator_earning > 0) : ?>
 | 
						|
                                            <br><small><?php echo wpdd_format_price($order->creator_earning); ?></small>
 | 
						|
                                        <?php endif; ?>
 | 
						|
                                    <?php else : ?>
 | 
						|
                                        <span class="notice notice-error" style="padding: 2px 8px; display: inline-block;">
 | 
						|
                                            <?php _e('No Earnings', 'wp-digital-download'); ?>
 | 
						|
                                        </span>
 | 
						|
                                    <?php endif; ?>
 | 
						|
                                </td>
 | 
						|
                                <td>
 | 
						|
                                    <div class="button-group">
 | 
						|
                                        <?php if ($order->status === 'completed') : ?>
 | 
						|
                                            
 | 
						|
                                            <?php if ($order->payout_status === 'pending') : ?>
 | 
						|
                                            <!-- Release Earnings Button -->
 | 
						|
                                            <form method="post" action="<?php echo admin_url('admin-post.php'); ?>" style="display: inline;">
 | 
						|
                                                <input type="hidden" name="action" value="wpdd_release_earnings">
 | 
						|
                                                <input type="hidden" name="earning_id" value="<?php echo esc_attr($order->earning_id); ?>">
 | 
						|
                                                <input type="hidden" name="order_id" value="<?php echo esc_attr($order->id); ?>">
 | 
						|
                                                <?php wp_nonce_field('wpdd_release_earnings_' . $order->earning_id, 'wpdd_nonce'); ?>
 | 
						|
                                                <button type="submit" class="button button-secondary wpdd-release-btn" title="<?php _e('Release earnings immediately', 'wp-digital-download'); ?>">
 | 
						|
                                                    <span class="dashicons dashicons-unlock"></span> <?php _e('Release', 'wp-digital-download'); ?>
 | 
						|
                                                </button>
 | 
						|
                                            </form>
 | 
						|
                                            <?php endif; ?>
 | 
						|
                                            
 | 
						|
                                            <?php if ($order->payout_status !== 'paid') : ?>
 | 
						|
                                            <!-- Cancel Order Button -->
 | 
						|
                                            <form method="post" action="<?php echo admin_url('admin-post.php'); ?>" style="display: inline;">
 | 
						|
                                                <input type="hidden" name="action" value="wpdd_cancel_order">
 | 
						|
                                                <input type="hidden" name="order_id" value="<?php echo esc_attr($order->id); ?>">
 | 
						|
                                                <?php wp_nonce_field('wpdd_cancel_order_' . $order->id, 'wpdd_nonce'); ?>
 | 
						|
                                                <button type="submit" class="button button-link-delete wpdd-cancel-btn" title="<?php _e('Cancel order and revoke access', 'wp-digital-download'); ?>">
 | 
						|
                                                    <span class="dashicons dashicons-no"></span> <?php _e('Cancel', 'wp-digital-download'); ?>
 | 
						|
                                                </button>
 | 
						|
                                            </form>
 | 
						|
                                            
 | 
						|
                                            <!-- Refund Button -->
 | 
						|
                                            <form method="post" action="<?php echo admin_url('admin-post.php'); ?>" style="display: inline;">
 | 
						|
                                                <input type="hidden" name="action" value="wpdd_refund_order">
 | 
						|
                                                <input type="hidden" name="order_id" value="<?php echo esc_attr($order->id); ?>">
 | 
						|
                                                <?php wp_nonce_field('wpdd_refund_order_' . $order->id, 'wpdd_nonce'); ?>
 | 
						|
                                                <button type="submit" class="button button-link-delete wpdd-refund-btn" title="<?php _e('Process refund (manual PayPal refund required)', 'wp-digital-download'); ?>">
 | 
						|
                                                    <span class="dashicons dashicons-undo"></span> <?php _e('Refund', 'wp-digital-download'); ?>
 | 
						|
                                                </button>
 | 
						|
                                            </form>
 | 
						|
                                            <?php else : ?>
 | 
						|
                                                <span class="description"><?php _e('Earnings already paid out', 'wp-digital-download'); ?></span>
 | 
						|
                                            <?php endif; ?>
 | 
						|
                                            
 | 
						|
                                        <?php else : ?>
 | 
						|
                                            <span class="description"><?php printf(__('Order is %s', 'wp-digital-download'), $order->status); ?></span>
 | 
						|
                                        <?php endif; ?>
 | 
						|
                                    </div>
 | 
						|
                                </td>
 | 
						|
                            </tr>
 | 
						|
                            <?php endforeach; ?>
 | 
						|
                        </tbody>
 | 
						|
                    </table>
 | 
						|
                <?php endif; ?>
 | 
						|
            </div>
 | 
						|
            
 | 
						|
            <style>
 | 
						|
                .button-group {
 | 
						|
                    white-space: nowrap;
 | 
						|
                }
 | 
						|
                .button-group .button {
 | 
						|
                    margin: 2px;
 | 
						|
                    font-size: 11px;
 | 
						|
                    padding: 3px 8px;
 | 
						|
                    height: auto;
 | 
						|
                }
 | 
						|
                .button-group .dashicons {
 | 
						|
                    font-size: 12px;
 | 
						|
                    width: 12px;
 | 
						|
                    height: 12px;
 | 
						|
                    vertical-align: middle;
 | 
						|
                }
 | 
						|
            </style>
 | 
						|
        </div>
 | 
						|
        <?php
 | 
						|
    }
 | 
						|
    
 | 
						|
    public static function handle_cancel_order() {
 | 
						|
        if (!current_user_can('manage_options')) {
 | 
						|
            wp_die(__('Permission denied', 'wp-digital-download'));
 | 
						|
        }
 | 
						|
        
 | 
						|
        $order_id = intval($_POST['order_id']);
 | 
						|
        
 | 
						|
        if (!wp_verify_nonce($_POST['wpdd_nonce'], 'wpdd_cancel_order_' . $order_id)) {
 | 
						|
            wp_redirect(admin_url('edit.php?post_type=wpdd_product&page=wpdd-order-manager&message=error'));
 | 
						|
            exit;
 | 
						|
        }
 | 
						|
        
 | 
						|
        $success = self::cancel_order($order_id);
 | 
						|
        
 | 
						|
        $message = $success ? 'cancelled' : 'error';
 | 
						|
        wp_redirect(admin_url('edit.php?post_type=wpdd_product&page=wpdd-order-manager&message=' . $message));
 | 
						|
        exit;
 | 
						|
    }
 | 
						|
    
 | 
						|
    public static function handle_refund_order() {
 | 
						|
        if (!current_user_can('manage_options')) {
 | 
						|
            wp_die(__('Permission denied', 'wp-digital-download'));
 | 
						|
        }
 | 
						|
        
 | 
						|
        $order_id = intval($_POST['order_id']);
 | 
						|
        
 | 
						|
        if (!wp_verify_nonce($_POST['wpdd_nonce'], 'wpdd_refund_order_' . $order_id)) {
 | 
						|
            wp_redirect(admin_url('edit.php?post_type=wpdd_product&page=wpdd-order-manager&message=error'));
 | 
						|
            exit;
 | 
						|
        }
 | 
						|
        
 | 
						|
        $success = self::refund_order($order_id);
 | 
						|
        
 | 
						|
        $message = $success ? 'refunded' : 'error';
 | 
						|
        wp_redirect(admin_url('edit.php?post_type=wpdd_product&page=wpdd-order-manager&message=' . $message));
 | 
						|
        exit;
 | 
						|
    }
 | 
						|
    
 | 
						|
    public static function handle_release_earnings() {
 | 
						|
        if (!current_user_can('manage_options')) {
 | 
						|
            wp_die(__('Permission denied', 'wp-digital-download'));
 | 
						|
        }
 | 
						|
        
 | 
						|
        $earning_id = intval($_POST['earning_id']);
 | 
						|
        $order_id = intval($_POST['order_id']);
 | 
						|
        
 | 
						|
        if (!wp_verify_nonce($_POST['wpdd_nonce'], 'wpdd_release_earnings_' . $earning_id)) {
 | 
						|
            wp_redirect(admin_url('edit.php?post_type=wpdd_product&page=wpdd-order-manager&message=error'));
 | 
						|
            exit;
 | 
						|
        }
 | 
						|
        
 | 
						|
        $success = WPDD_Earnings_Processor::release_earning_immediately($earning_id);
 | 
						|
        
 | 
						|
        $message = $success ? 'released' : 'error';
 | 
						|
        wp_redirect(admin_url('edit.php?post_type=wpdd_product&page=wpdd-order-manager&message=' . $message));
 | 
						|
        exit;
 | 
						|
    }
 | 
						|
    
 | 
						|
    private static function cancel_order($order_id) {
 | 
						|
        global $wpdb;
 | 
						|
        
 | 
						|
        // Update order status
 | 
						|
        $result = $wpdb->update(
 | 
						|
            $wpdb->prefix . 'wpdd_orders',
 | 
						|
            array('status' => 'cancelled'),
 | 
						|
            array('id' => $order_id),
 | 
						|
            array('%s'),
 | 
						|
            array('%d')
 | 
						|
        );
 | 
						|
        
 | 
						|
        if ($result) {
 | 
						|
            // Revoke download access
 | 
						|
            $wpdb->delete(
 | 
						|
                $wpdb->prefix . 'wpdd_download_links',
 | 
						|
                array('order_id' => $order_id),
 | 
						|
                array('%d')
 | 
						|
            );
 | 
						|
            
 | 
						|
            // Cancel associated earnings
 | 
						|
            $earning_id = $wpdb->get_var($wpdb->prepare(
 | 
						|
                "SELECT id FROM {$wpdb->prefix}wpdd_creator_earnings WHERE order_id = %d",
 | 
						|
                $order_id
 | 
						|
            ));
 | 
						|
            
 | 
						|
            if ($earning_id) {
 | 
						|
                WPDD_Earnings_Processor::cancel_earning($earning_id, 'Order cancelled by admin');
 | 
						|
            }
 | 
						|
        }
 | 
						|
        
 | 
						|
        return $result > 0;
 | 
						|
    }
 | 
						|
    
 | 
						|
    private static function refund_order($order_id) {
 | 
						|
        global $wpdb;
 | 
						|
        
 | 
						|
        // Update order status 
 | 
						|
        $result = $wpdb->update(
 | 
						|
            $wpdb->prefix . 'wpdd_orders',
 | 
						|
            array('status' => 'refunded'),
 | 
						|
            array('id' => $order_id),
 | 
						|
            array('%s'),
 | 
						|
            array('%d')
 | 
						|
        );
 | 
						|
        
 | 
						|
        if ($result) {
 | 
						|
            // Revoke download access
 | 
						|
            $wpdb->delete(
 | 
						|
                $wpdb->prefix . 'wpdd_download_links',
 | 
						|
                array('order_id' => $order_id),
 | 
						|
                array('%d')
 | 
						|
            );
 | 
						|
            
 | 
						|
            // Cancel associated earnings
 | 
						|
            $earning_id = $wpdb->get_var($wpdb->prepare(
 | 
						|
                "SELECT id FROM {$wpdb->prefix}wpdd_creator_earnings WHERE order_id = %d",
 | 
						|
                $order_id
 | 
						|
            ));
 | 
						|
            
 | 
						|
            if ($earning_id) {
 | 
						|
                WPDD_Earnings_Processor::cancel_earning($earning_id, 'Order refunded by admin');
 | 
						|
            }
 | 
						|
        }
 | 
						|
        
 | 
						|
        return $result > 0;
 | 
						|
    }
 | 
						|
}
 |