First Commit
This commit is contained in:
435
admin/class-wpdd-admin-payouts.php
Normal file
435
admin/class-wpdd-admin-payouts.php
Normal file
@@ -0,0 +1,435 @@
|
||||
<?php
|
||||
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class WPDD_Admin_Payouts {
|
||||
|
||||
public static function init() {
|
||||
add_action('admin_menu', array(__CLASS__, 'add_menu_page'));
|
||||
add_action('admin_post_wpdd_process_payout', array(__CLASS__, 'process_payout'));
|
||||
add_action('admin_post_wpdd_bulk_payouts', array(__CLASS__, 'process_bulk_payouts'));
|
||||
add_action('admin_enqueue_scripts', array(__CLASS__, 'enqueue_scripts'));
|
||||
}
|
||||
|
||||
public static function add_menu_page() {
|
||||
add_submenu_page(
|
||||
'edit.php?post_type=wpdd_product',
|
||||
__('Creator Payouts', 'wp-digital-download'),
|
||||
__('Payouts', 'wp-digital-download'),
|
||||
'manage_options',
|
||||
'wpdd-payouts',
|
||||
array(__CLASS__, 'render_page')
|
||||
);
|
||||
}
|
||||
|
||||
public static function enqueue_scripts($hook) {
|
||||
if ($hook !== 'wpdd_product_page_wpdd-payouts') {
|
||||
return;
|
||||
}
|
||||
|
||||
wp_enqueue_script('wpdd-admin-payouts', WPDD_PLUGIN_URL . 'assets/js/admin-payouts.js', array('jquery'), WPDD_VERSION, true);
|
||||
wp_localize_script('wpdd-admin-payouts', 'wpdd_payouts', array(
|
||||
'ajax_url' => admin_url('admin-ajax.php'),
|
||||
'nonce' => wp_create_nonce('wpdd_payouts'),
|
||||
'confirm_payout' => __('Are you sure you want to process this payout?', 'wp-digital-download'),
|
||||
'confirm_bulk' => __('Are you sure you want to process all selected payouts?', 'wp-digital-download')
|
||||
));
|
||||
}
|
||||
|
||||
public static function render_page() {
|
||||
global $wpdb;
|
||||
|
||||
// Get filter parameters
|
||||
$status_filter = isset($_GET['status']) ? sanitize_text_field($_GET['status']) : 'pending';
|
||||
$creator_filter = isset($_GET['creator']) ? intval($_GET['creator']) : 0;
|
||||
|
||||
// Get creators with balance
|
||||
$creators = WPDD_Creator::get_creators_with_balance();
|
||||
$currency = get_option('wpdd_currency', 'USD');
|
||||
$threshold = floatval(get_option('wpdd_payout_threshold', 0));
|
||||
|
||||
// Get payout history
|
||||
$query = "SELECT p.*, u.display_name, u.user_email
|
||||
FROM {$wpdb->prefix}wpdd_payouts p
|
||||
INNER JOIN {$wpdb->users} u ON p.creator_id = u.ID
|
||||
WHERE 1=1";
|
||||
|
||||
if ($status_filter && $status_filter !== 'all') {
|
||||
$query .= $wpdb->prepare(" AND p.status = %s", $status_filter);
|
||||
}
|
||||
|
||||
if ($creator_filter) {
|
||||
$query .= $wpdb->prepare(" AND p.creator_id = %d", $creator_filter);
|
||||
}
|
||||
|
||||
$query .= " ORDER BY p.created_at DESC LIMIT 100";
|
||||
|
||||
$payouts = $wpdb->get_results($query);
|
||||
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h1><?php _e('Creator Payouts', 'wp-digital-download'); ?></h1>
|
||||
|
||||
<?php if (isset($_GET['message'])) : ?>
|
||||
<?php if ($_GET['message'] === 'success') : ?>
|
||||
<div class="notice notice-success is-dismissible">
|
||||
<p><?php _e('Payout processed successfully.', 'wp-digital-download'); ?></p>
|
||||
</div>
|
||||
<?php elseif ($_GET['message'] === 'error') : ?>
|
||||
<div class="notice notice-error is-dismissible">
|
||||
<p><?php _e('Error processing payout. Please try again.', 'wp-digital-download'); ?></p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="wpdd-payout-stats">
|
||||
<h2><?php _e('Pending Payouts', 'wp-digital-download'); ?></h2>
|
||||
|
||||
<?php if (!empty($creators)) : ?>
|
||||
<form method="post" action="<?php echo admin_url('admin-post.php'); ?>">
|
||||
<input type="hidden" name="action" value="wpdd_bulk_payouts">
|
||||
<?php wp_nonce_field('wpdd_bulk_payouts', 'wpdd_nonce'); ?>
|
||||
|
||||
<table class="wp-list-table widefat fixed striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="check-column">
|
||||
<input type="checkbox" id="select-all-creators">
|
||||
</th>
|
||||
<th><?php _e('Creator', 'wp-digital-download'); ?></th>
|
||||
<th><?php _e('PayPal Email', 'wp-digital-download'); ?></th>
|
||||
<th><?php _e('Current Balance', 'wp-digital-download'); ?></th>
|
||||
<th><?php _e('Total Sales', 'wp-digital-download'); ?></th>
|
||||
<th><?php _e('Net Earnings', 'wp-digital-download'); ?></th>
|
||||
<th><?php _e('Actions', 'wp-digital-download'); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($creators as $creator) :
|
||||
$total_earnings = WPDD_Creator::get_creator_total_earnings($creator->ID);
|
||||
$net_earnings = WPDD_Creator::get_creator_net_earnings($creator->ID);
|
||||
$can_payout = !empty($creator->paypal_email) && floatval($creator->balance) > 0;
|
||||
$auto_eligible = $threshold > 0 && floatval($creator->balance) >= $threshold;
|
||||
?>
|
||||
<tr <?php echo $auto_eligible ? 'style="background-color: #d4edda;"' : ''; ?>>
|
||||
<td>
|
||||
<?php if ($can_payout) : ?>
|
||||
<input type="checkbox" name="creator_ids[]" value="<?php echo esc_attr($creator->ID); ?>">
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td>
|
||||
<strong><?php echo esc_html($creator->display_name); ?></strong><br>
|
||||
<small><?php echo esc_html($creator->user_email); ?></small>
|
||||
</td>
|
||||
<td>
|
||||
<?php if (!empty($creator->paypal_email)) : ?>
|
||||
<?php echo esc_html($creator->paypal_email); ?>
|
||||
<?php else : ?>
|
||||
<span style="color: #dc3545;"><?php _e('Not set', 'wp-digital-download'); ?></span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td>
|
||||
<strong><?php echo wpdd_format_price($creator->balance, $currency); ?></strong>
|
||||
<?php if ($auto_eligible) : ?>
|
||||
<br><span class="dashicons dashicons-yes" style="color: #28a745;"></span>
|
||||
<small><?php _e('Auto-payout eligible', 'wp-digital-download'); ?></small>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><?php echo wpdd_format_price($total_earnings, $currency); ?></td>
|
||||
<td><?php echo wpdd_format_price($net_earnings, $currency); ?></td>
|
||||
<td>
|
||||
<?php if ($can_payout) : ?>
|
||||
<form method="post" action="<?php echo admin_url('admin-post.php'); ?>" style="display: inline;">
|
||||
<input type="hidden" name="action" value="wpdd_process_payout">
|
||||
<input type="hidden" name="creator_id" value="<?php echo esc_attr($creator->ID); ?>">
|
||||
<?php wp_nonce_field('wpdd_process_payout_' . $creator->ID, 'wpdd_nonce'); ?>
|
||||
<button type="submit" class="button button-primary wpdd-payout-btn">
|
||||
<?php _e('Process Payout', 'wp-digital-download'); ?>
|
||||
</button>
|
||||
</form>
|
||||
<?php else : ?>
|
||||
<button class="button" disabled><?php _e('Cannot Process', 'wp-digital-download'); ?></button>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="tablenav bottom">
|
||||
<div class="alignleft actions">
|
||||
<button type="submit" class="button button-primary" name="bulk_action" value="process">
|
||||
<?php _e('Process Selected Payouts', 'wp-digital-download'); ?>
|
||||
</button>
|
||||
<?php if ($threshold > 0) : ?>
|
||||
<button type="submit" class="button" name="bulk_action" value="auto">
|
||||
<?php printf(__('Process All Above %s', 'wp-digital-download'), wpdd_format_price($threshold, $currency)); ?>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<?php else : ?>
|
||||
<p><?php _e('No creators with pending payouts.', 'wp-digital-download'); ?></p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="wpdd-payout-history">
|
||||
<h2><?php _e('Payout History', 'wp-digital-download'); ?></h2>
|
||||
|
||||
<div class="tablenav top">
|
||||
<div class="alignleft actions">
|
||||
<select name="status_filter" onchange="window.location.href='<?php echo admin_url('edit.php?post_type=wpdd_product&page=wpdd-payouts'); ?>&status=' + this.value">
|
||||
<option value="all" <?php selected($status_filter, 'all'); ?>><?php _e('All Statuses', 'wp-digital-download'); ?></option>
|
||||
<option value="pending" <?php selected($status_filter, 'pending'); ?>><?php _e('Pending', 'wp-digital-download'); ?></option>
|
||||
<option value="completed" <?php selected($status_filter, 'completed'); ?>><?php _e('Completed', 'wp-digital-download'); ?></option>
|
||||
<option value="failed" <?php selected($status_filter, 'failed'); ?>><?php _e('Failed', 'wp-digital-download'); ?></option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if (!empty($payouts)) : ?>
|
||||
<table class="wp-list-table widefat fixed striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><?php _e('Date', 'wp-digital-download'); ?></th>
|
||||
<th><?php _e('Creator', 'wp-digital-download'); ?></th>
|
||||
<th><?php _e('Amount', 'wp-digital-download'); ?></th>
|
||||
<th><?php _e('PayPal Email', 'wp-digital-download'); ?></th>
|
||||
<th><?php _e('Transaction ID', 'wp-digital-download'); ?></th>
|
||||
<th><?php _e('Status', 'wp-digital-download'); ?></th>
|
||||
<th><?php _e('Method', 'wp-digital-download'); ?></th>
|
||||
<th><?php _e('Processed By', 'wp-digital-download'); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($payouts as $payout) :
|
||||
$processor = $payout->processed_by ? get_userdata($payout->processed_by) : null;
|
||||
?>
|
||||
<tr>
|
||||
<td>
|
||||
<?php echo esc_html(date_i18n(get_option('date_format') . ' ' . get_option('time_format'), strtotime($payout->created_at))); ?>
|
||||
<?php if ($payout->processed_at) : ?>
|
||||
<br><small><?php _e('Processed:', 'wp-digital-download'); ?> <?php echo esc_html(date_i18n(get_option('date_format'), strtotime($payout->processed_at))); ?></small>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td>
|
||||
<strong><?php echo esc_html($payout->display_name); ?></strong><br>
|
||||
<small><?php echo esc_html($payout->user_email); ?></small>
|
||||
</td>
|
||||
<td><strong><?php echo wpdd_format_price($payout->amount, $payout->currency); ?></strong></td>
|
||||
<td><?php echo esc_html($payout->paypal_email); ?></td>
|
||||
<td>
|
||||
<?php echo esc_html($payout->transaction_id ?: '-'); ?>
|
||||
<?php if ($payout->notes) : ?>
|
||||
<br><small><?php echo esc_html($payout->notes); ?></small>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td>
|
||||
<?php
|
||||
$status_class = '';
|
||||
switch($payout->status) {
|
||||
case 'completed':
|
||||
$status_class = 'notice-success';
|
||||
break;
|
||||
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($payout->status)); ?>
|
||||
</span>
|
||||
</td>
|
||||
<td><?php echo esc_html(ucfirst($payout->payout_method)); ?></td>
|
||||
<td>
|
||||
<?php if ($processor) : ?>
|
||||
<?php echo esc_html($processor->display_name); ?>
|
||||
<?php else : ?>
|
||||
<?php echo $payout->payout_method === 'automatic' ? __('System', 'wp-digital-download') : '-'; ?>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php else : ?>
|
||||
<p><?php _e('No payout history found.', 'wp-digital-download'); ?></p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.wpdd-payout-stats,
|
||||
.wpdd-payout-history {
|
||||
margin-top: 30px;
|
||||
background: #fff;
|
||||
padding: 20px;
|
||||
border: 1px solid #ccd0d4;
|
||||
box-shadow: 0 1px 1px rgba(0,0,0,.04);
|
||||
}
|
||||
.wpdd-payout-btn:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
#select-all-creators {
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
jQuery(document).ready(function($) {
|
||||
$('#select-all-creators').on('change', function() {
|
||||
$('input[name="creator_ids[]"]').prop('checked', this.checked);
|
||||
});
|
||||
|
||||
$('.wpdd-payout-btn').on('click', function(e) {
|
||||
if (!confirm(wpdd_payouts.confirm_payout)) {
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
$('button[name="bulk_action"]').on('click', function(e) {
|
||||
var checkedCount = $('input[name="creator_ids[]"]:checked').length;
|
||||
if (checkedCount === 0) {
|
||||
alert('Please select at least one creator for payout.');
|
||||
e.preventDefault();
|
||||
} else if (!confirm(wpdd_payouts.confirm_bulk)) {
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
public static function process_payout() {
|
||||
if (!current_user_can('manage_options')) {
|
||||
wp_die(__('You do not have permission to perform this action.', 'wp-digital-download'));
|
||||
}
|
||||
|
||||
$creator_id = isset($_POST['creator_id']) ? intval($_POST['creator_id']) : 0;
|
||||
|
||||
if (!$creator_id || !wp_verify_nonce($_POST['wpdd_nonce'], 'wpdd_process_payout_' . $creator_id)) {
|
||||
wp_redirect(admin_url('edit.php?post_type=wpdd_product&page=wpdd-payouts&message=error'));
|
||||
exit;
|
||||
}
|
||||
|
||||
$result = self::create_payout($creator_id);
|
||||
|
||||
if ($result) {
|
||||
wp_redirect(admin_url('edit.php?post_type=wpdd_product&page=wpdd-payouts&message=success'));
|
||||
} else {
|
||||
wp_redirect(admin_url('edit.php?post_type=wpdd_product&page=wpdd-payouts&message=error'));
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function process_bulk_payouts() {
|
||||
if (!current_user_can('manage_options')) {
|
||||
wp_die(__('You do not have permission to perform this action.', 'wp-digital-download'));
|
||||
}
|
||||
|
||||
if (!wp_verify_nonce($_POST['wpdd_nonce'], 'wpdd_bulk_payouts')) {
|
||||
wp_redirect(admin_url('edit.php?post_type=wpdd_product&page=wpdd-payouts&message=error'));
|
||||
exit;
|
||||
}
|
||||
|
||||
$bulk_action = isset($_POST['bulk_action']) ? sanitize_text_field($_POST['bulk_action']) : '';
|
||||
|
||||
if ($bulk_action === 'auto') {
|
||||
// Process all creators above threshold
|
||||
$threshold = floatval(get_option('wpdd_payout_threshold', 0));
|
||||
if ($threshold > 0) {
|
||||
$creators = WPDD_Creator::get_creators_with_balance();
|
||||
foreach ($creators as $creator) {
|
||||
if (floatval($creator->balance) >= $threshold && !empty($creator->paypal_email)) {
|
||||
self::create_payout($creator->ID);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Process selected creators
|
||||
$creator_ids = isset($_POST['creator_ids']) ? array_map('intval', $_POST['creator_ids']) : array();
|
||||
foreach ($creator_ids as $creator_id) {
|
||||
self::create_payout($creator_id);
|
||||
}
|
||||
}
|
||||
|
||||
wp_redirect(admin_url('edit.php?post_type=wpdd_product&page=wpdd-payouts&message=success'));
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function create_payout($creator_id, $method = 'manual') {
|
||||
global $wpdb;
|
||||
|
||||
$balance = WPDD_Creator::get_creator_balance($creator_id);
|
||||
$paypal_email = get_user_meta($creator_id, 'wpdd_paypal_email', true);
|
||||
|
||||
if ($balance <= 0 || empty($paypal_email)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$currency = get_option('wpdd_currency', 'USD');
|
||||
$current_user_id = get_current_user_id();
|
||||
|
||||
// Create payout record
|
||||
$wpdb->insert(
|
||||
$wpdb->prefix . 'wpdd_payouts',
|
||||
array(
|
||||
'creator_id' => $creator_id,
|
||||
'amount' => $balance,
|
||||
'currency' => $currency,
|
||||
'paypal_email' => $paypal_email,
|
||||
'status' => 'pending',
|
||||
'payout_method' => $method,
|
||||
'processed_by' => $current_user_id,
|
||||
'created_at' => current_time('mysql')
|
||||
),
|
||||
array('%d', '%f', '%s', '%s', '%s', '%s', '%d', '%s')
|
||||
);
|
||||
|
||||
$payout_id = $wpdb->insert_id;
|
||||
|
||||
// Try to process via PayPal API
|
||||
$result = WPDD_PayPal_Payouts::process_payout($payout_id);
|
||||
|
||||
if ($result['success']) {
|
||||
// Update payout status
|
||||
$wpdb->update(
|
||||
$wpdb->prefix . 'wpdd_payouts',
|
||||
array(
|
||||
'status' => 'completed',
|
||||
'transaction_id' => $result['transaction_id'],
|
||||
'processed_at' => current_time('mysql')
|
||||
),
|
||||
array('id' => $payout_id),
|
||||
array('%s', '%s', '%s'),
|
||||
array('%d')
|
||||
);
|
||||
|
||||
// Reset creator balance
|
||||
update_user_meta($creator_id, 'wpdd_creator_balance', 0);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
// Update with error
|
||||
$wpdb->update(
|
||||
$wpdb->prefix . 'wpdd_payouts',
|
||||
array(
|
||||
'status' => 'failed',
|
||||
'notes' => $result['error']
|
||||
),
|
||||
array('id' => $payout_id),
|
||||
array('%s', '%s'),
|
||||
array('%d')
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user