Major improvements: Fix download limits, enhance license display, fix software filenames

🔧 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>
This commit is contained in:
2025-09-09 19:16:57 -07:00
parent ce48f1615f
commit a160fe3964
28 changed files with 3709 additions and 156 deletions

View File

@@ -0,0 +1,189 @@
<?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;
}
}