Adding more functionality
This commit is contained in:
@@ -10,6 +10,10 @@ class WPDD_Admin_Payouts {
|
|||||||
add_action('admin_menu', array(__CLASS__, 'add_menu_page'));
|
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_process_payout', array(__CLASS__, 'process_payout'));
|
||||||
add_action('admin_post_wpdd_bulk_payouts', array(__CLASS__, 'process_bulk_payouts'));
|
add_action('admin_post_wpdd_bulk_payouts', array(__CLASS__, 'process_bulk_payouts'));
|
||||||
|
add_action('admin_post_wpdd_process_payout_request', array(__CLASS__, 'process_payout_request'));
|
||||||
|
add_action('admin_post_wpdd_reject_payout_request', array(__CLASS__, 'reject_payout_request'));
|
||||||
|
add_action('admin_post_wpdd_manual_payout', array(__CLASS__, 'process_manual_payout'));
|
||||||
|
add_action('admin_post_wpdd_adjust_balance', array(__CLASS__, 'adjust_creator_balance'));
|
||||||
add_action('admin_enqueue_scripts', array(__CLASS__, 'enqueue_scripts'));
|
add_action('admin_enqueue_scripts', array(__CLASS__, 'enqueue_scripts'));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,6 +54,15 @@ class WPDD_Admin_Payouts {
|
|||||||
$currency = get_option('wpdd_currency', 'USD');
|
$currency = get_option('wpdd_currency', 'USD');
|
||||||
$threshold = floatval(get_option('wpdd_payout_threshold', 0));
|
$threshold = floatval(get_option('wpdd_payout_threshold', 0));
|
||||||
|
|
||||||
|
// Get payout requests (requested status)
|
||||||
|
$payout_requests = $wpdb->get_results(
|
||||||
|
"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 p.status = 'requested'
|
||||||
|
ORDER BY p.created_at ASC"
|
||||||
|
);
|
||||||
|
|
||||||
// Get payout history
|
// Get payout history
|
||||||
$query = "SELECT p.*, u.display_name, u.user_email
|
$query = "SELECT p.*, u.display_name, u.user_email
|
||||||
FROM {$wpdb->prefix}wpdd_payouts p
|
FROM {$wpdb->prefix}wpdd_payouts p
|
||||||
@@ -81,9 +94,172 @@ class WPDD_Admin_Payouts {
|
|||||||
<div class="notice notice-error is-dismissible">
|
<div class="notice notice-error is-dismissible">
|
||||||
<p><?php _e('Error processing payout. Please try again.', 'wp-digital-download'); ?></p>
|
<p><?php _e('Error processing payout. Please try again.', 'wp-digital-download'); ?></p>
|
||||||
</div>
|
</div>
|
||||||
|
<?php elseif ($_GET['message'] === 'balance_adjusted') : ?>
|
||||||
|
<div class="notice notice-success is-dismissible">
|
||||||
|
<p><?php _e('Creator balance adjusted successfully.', 'wp-digital-download'); ?></p>
|
||||||
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if (!empty($payout_requests)) : ?>
|
||||||
|
<div class="wpdd-payout-requests" style="background: #fff3cd; border: 1px solid #ffeaa7; border-radius: 4px; padding: 20px; margin: 20px 0;">
|
||||||
|
<h2 style="margin-top: 0;"><?php _e('Payout Requests', 'wp-digital-download'); ?> <span class="count">(<?php echo count($payout_requests); ?>)</span></h2>
|
||||||
|
<p><?php _e('Creators have requested the following payouts:', 'wp-digital-download'); ?></p>
|
||||||
|
|
||||||
|
<table class="wp-list-table widefat fixed striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<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('Request Date', 'wp-digital-download'); ?></th>
|
||||||
|
<th><?php _e('Actions', 'wp-digital-download'); ?></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($payout_requests as $request) : ?>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<strong><?php echo esc_html($request->display_name); ?></strong><br>
|
||||||
|
<small><?php echo esc_html($request->user_email); ?></small>
|
||||||
|
</td>
|
||||||
|
<td><strong><?php echo wpdd_format_price($request->amount, $request->currency); ?></strong></td>
|
||||||
|
<td><?php echo esc_html($request->paypal_email); ?></td>
|
||||||
|
<td><?php echo esc_html(date_i18n(get_option('date_format') . ' ' . get_option('time_format'), strtotime($request->created_at))); ?></td>
|
||||||
|
<td>
|
||||||
|
<form method="post" action="<?php echo admin_url('admin-post.php'); ?>" style="display: inline;">
|
||||||
|
<input type="hidden" name="action" value="wpdd_process_payout_request">
|
||||||
|
<input type="hidden" name="payout_id" value="<?php echo esc_attr($request->id); ?>">
|
||||||
|
<?php wp_nonce_field('wpdd_process_payout_request_' . $request->id, 'wpdd_nonce'); ?>
|
||||||
|
<button type="submit" class="button button-primary">
|
||||||
|
<?php _e('Process Now', 'wp-digital-download'); ?>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
<form method="post" action="<?php echo admin_url('admin-post.php'); ?>" style="display: inline; margin-left: 5px;">
|
||||||
|
<input type="hidden" name="action" value="wpdd_reject_payout_request">
|
||||||
|
<input type="hidden" name="payout_id" value="<?php echo esc_attr($request->id); ?>">
|
||||||
|
<?php wp_nonce_field('wpdd_reject_payout_request_' . $request->id, 'wpdd_nonce'); ?>
|
||||||
|
<button type="submit" class="button button-secondary" onclick="return confirm('<?php _e('Are you sure you want to reject this payout request?', 'wp-digital-download'); ?>')">
|
||||||
|
<?php _e('Reject', 'wp-digital-download'); ?>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<!-- Manual Actions Section -->
|
||||||
|
<div class="wpdd-manual-actions" style="display: flex; gap: 20px; margin: 20px 0;">
|
||||||
|
<!-- Manual Payout Form -->
|
||||||
|
<div style="background: white; border: 1px solid #ddd; border-radius: 4px; padding: 20px; flex: 1;">
|
||||||
|
<h3 style="margin-top: 0;"><?php _e('Manual Payout', 'wp-digital-download'); ?></h3>
|
||||||
|
<p><?php _e('Process a payout for off-site sales or manual adjustments.', 'wp-digital-download'); ?></p>
|
||||||
|
|
||||||
|
<form method="post" action="<?php echo admin_url('admin-post.php'); ?>">
|
||||||
|
<input type="hidden" name="action" value="wpdd_manual_payout">
|
||||||
|
<?php wp_nonce_field('wpdd_manual_payout', 'wpdd_nonce'); ?>
|
||||||
|
|
||||||
|
<table class="form-table" style="margin: 0;">
|
||||||
|
<tr>
|
||||||
|
<th><label for="creator_id"><?php _e('Creator', 'wp-digital-download'); ?></label></th>
|
||||||
|
<td>
|
||||||
|
<select name="creator_id" id="creator_id" required style="width: 100%;">
|
||||||
|
<option value=""><?php _e('Select Creator', 'wp-digital-download'); ?></option>
|
||||||
|
<?php
|
||||||
|
$all_creators = get_users(array('role' => 'wpdd_creator'));
|
||||||
|
foreach ($all_creators as $creator) :
|
||||||
|
$paypal_email = get_user_meta($creator->ID, 'wpdd_paypal_email', true);
|
||||||
|
?>
|
||||||
|
<option value="<?php echo esc_attr($creator->ID); ?>" <?php echo empty($paypal_email) ? 'disabled' : ''; ?>>
|
||||||
|
<?php echo esc_html($creator->display_name); ?>
|
||||||
|
<?php echo empty($paypal_email) ? ' (' . __('No PayPal email', 'wp-digital-download') . ')' : ' (' . esc_html($paypal_email) . ')'; ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th><label for="payout_amount"><?php _e('Amount', 'wp-digital-download'); ?></label></th>
|
||||||
|
<td>
|
||||||
|
<input type="number" name="payout_amount" id="payout_amount" step="0.01" min="0.01" required style="width: 150px;">
|
||||||
|
<span><?php echo esc_html($currency); ?></span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th><label for="payout_reason"><?php _e('Reason/Note', 'wp-digital-download'); ?></label></th>
|
||||||
|
<td>
|
||||||
|
<textarea name="payout_reason" id="payout_reason" rows="3" style="width: 100%;" placeholder="<?php _e('e.g., Off-site sale, manual adjustment, etc.', 'wp-digital-download'); ?>"></textarea>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<p class="submit" style="margin: 15px 0 0 0;">
|
||||||
|
<button type="submit" class="button button-primary">
|
||||||
|
<?php _e('Process Manual Payout', 'wp-digital-download'); ?>
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Balance Adjustment Form -->
|
||||||
|
<div style="background: white; border: 1px solid #ddd; border-radius: 4px; padding: 20px; flex: 1;">
|
||||||
|
<h3 style="margin-top: 0;"><?php _e('Adjust Creator Balance', 'wp-digital-download'); ?></h3>
|
||||||
|
<p><?php _e('Add or subtract funds from a creator\'s balance.', 'wp-digital-download'); ?></p>
|
||||||
|
|
||||||
|
<form method="post" action="<?php echo admin_url('admin-post.php'); ?>">
|
||||||
|
<input type="hidden" name="action" value="wpdd_adjust_balance">
|
||||||
|
<?php wp_nonce_field('wpdd_adjust_balance', 'wpdd_nonce'); ?>
|
||||||
|
|
||||||
|
<table class="form-table" style="margin: 0;">
|
||||||
|
<tr>
|
||||||
|
<th><label for="adj_creator_id"><?php _e('Creator', 'wp-digital-download'); ?></label></th>
|
||||||
|
<td>
|
||||||
|
<select name="creator_id" id="adj_creator_id" required style="width: 100%;">
|
||||||
|
<option value=""><?php _e('Select Creator', 'wp-digital-download'); ?></option>
|
||||||
|
<?php foreach ($all_creators as $creator) : ?>
|
||||||
|
<option value="<?php echo esc_attr($creator->ID); ?>">
|
||||||
|
<?php echo esc_html($creator->display_name); ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th><label for="adjustment_type"><?php _e('Type', 'wp-digital-download'); ?></label></th>
|
||||||
|
<td>
|
||||||
|
<select name="adjustment_type" id="adjustment_type" required>
|
||||||
|
<option value="add"><?php _e('Add Funds', 'wp-digital-download'); ?></option>
|
||||||
|
<option value="subtract"><?php _e('Subtract Funds', 'wp-digital-download'); ?></option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th><label for="adjustment_amount"><?php _e('Amount', 'wp-digital-download'); ?></label></th>
|
||||||
|
<td>
|
||||||
|
<input type="number" name="adjustment_amount" id="adjustment_amount" step="0.01" min="0.01" required style="width: 150px;">
|
||||||
|
<span><?php echo esc_html($currency); ?></span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th><label for="adjustment_reason"><?php _e('Reason/Note', 'wp-digital-download'); ?></label></th>
|
||||||
|
<td>
|
||||||
|
<textarea name="adjustment_reason" id="adjustment_reason" rows="3" style="width: 100%;" placeholder="<?php _e('e.g., Manual adjustment, refund, bonus, etc.', 'wp-digital-download'); ?>" required></textarea>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<p class="submit" style="margin: 15px 0 0 0;">
|
||||||
|
<button type="submit" class="button button-secondary">
|
||||||
|
<?php _e('Adjust Balance', 'wp-digital-download'); ?>
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="wpdd-payout-stats">
|
<div class="wpdd-payout-stats">
|
||||||
<h2><?php _e('Pending Payouts', 'wp-digital-download'); ?></h2>
|
<h2><?php _e('Pending Payouts', 'wp-digital-download'); ?></h2>
|
||||||
|
|
||||||
@@ -432,4 +608,241 @@ class WPDD_Admin_Payouts {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function process_payout_request() {
|
||||||
|
if (!current_user_can('manage_options')) {
|
||||||
|
wp_die(__('You do not have permission to perform this action.', 'wp-digital-download'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$payout_id = isset($_POST['payout_id']) ? intval($_POST['payout_id']) : 0;
|
||||||
|
|
||||||
|
if (!$payout_id || !wp_verify_nonce($_POST['wpdd_nonce'], 'wpdd_process_payout_request_' . $payout_id)) {
|
||||||
|
wp_redirect(admin_url('edit.php?post_type=wpdd_product&page=wpdd-payouts&message=error'));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
// Get the payout request
|
||||||
|
$payout = $wpdb->get_row($wpdb->prepare(
|
||||||
|
"SELECT * FROM {$wpdb->prefix}wpdd_payouts WHERE id = %d AND status = 'requested'",
|
||||||
|
$payout_id
|
||||||
|
));
|
||||||
|
|
||||||
|
if (!$payout) {
|
||||||
|
wp_redirect(admin_url('edit.php?post_type=wpdd_product&page=wpdd-payouts&message=error'));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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_by' => get_current_user_id(),
|
||||||
|
'processed_at' => current_time('mysql')
|
||||||
|
),
|
||||||
|
array('id' => $payout_id),
|
||||||
|
array('%s', '%s', '%d', '%s'),
|
||||||
|
array('%d')
|
||||||
|
);
|
||||||
|
|
||||||
|
wp_redirect(admin_url('edit.php?post_type=wpdd_product&page=wpdd-payouts&message=success'));
|
||||||
|
} 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')
|
||||||
|
);
|
||||||
|
|
||||||
|
wp_redirect(admin_url('edit.php?post_type=wpdd_product&page=wpdd-payouts&message=error'));
|
||||||
|
}
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function reject_payout_request() {
|
||||||
|
if (!current_user_can('manage_options')) {
|
||||||
|
wp_die(__('You do not have permission to perform this action.', 'wp-digital-download'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$payout_id = isset($_POST['payout_id']) ? intval($_POST['payout_id']) : 0;
|
||||||
|
|
||||||
|
if (!$payout_id || !wp_verify_nonce($_POST['wpdd_nonce'], 'wpdd_reject_payout_request_' . $payout_id)) {
|
||||||
|
wp_redirect(admin_url('edit.php?post_type=wpdd_product&page=wpdd-payouts&message=error'));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
// Get the payout request
|
||||||
|
$payout = $wpdb->get_row($wpdb->prepare(
|
||||||
|
"SELECT * FROM {$wpdb->prefix}wpdd_payouts WHERE id = %d AND status = 'requested'",
|
||||||
|
$payout_id
|
||||||
|
));
|
||||||
|
|
||||||
|
if (!$payout) {
|
||||||
|
wp_redirect(admin_url('edit.php?post_type=wpdd_product&page=wpdd-payouts&message=error'));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update status to failed/rejected
|
||||||
|
$wpdb->update(
|
||||||
|
$wpdb->prefix . 'wpdd_payouts',
|
||||||
|
array(
|
||||||
|
'status' => 'failed',
|
||||||
|
'notes' => 'Request rejected by administrator',
|
||||||
|
'processed_by' => get_current_user_id(),
|
||||||
|
'processed_at' => current_time('mysql')
|
||||||
|
),
|
||||||
|
array('id' => $payout_id),
|
||||||
|
array('%s', '%s', '%d', '%s'),
|
||||||
|
array('%d')
|
||||||
|
);
|
||||||
|
|
||||||
|
// Restore balance to creator
|
||||||
|
update_user_meta($payout->creator_id, 'wpdd_creator_balance', $payout->amount);
|
||||||
|
|
||||||
|
wp_redirect(admin_url('edit.php?post_type=wpdd_product&page=wpdd-payouts&message=success'));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function process_manual_payout() {
|
||||||
|
if (!current_user_can('manage_options') ||
|
||||||
|
!wp_verify_nonce($_POST['wpdd_nonce'], 'wpdd_manual_payout')) {
|
||||||
|
wp_die(__('Security check failed', 'wp-digital-download'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$creator_id = intval($_POST['creator_id']);
|
||||||
|
$amount = floatval($_POST['payout_amount']);
|
||||||
|
$reason = sanitize_textarea_field($_POST['payout_reason']);
|
||||||
|
|
||||||
|
if (!$creator_id || $amount <= 0) {
|
||||||
|
wp_redirect(add_query_arg('message', 'error', wp_get_referer()));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$creator = get_userdata($creator_id);
|
||||||
|
if (!$creator || !in_array('wpdd_creator', $creator->roles)) {
|
||||||
|
wp_redirect(add_query_arg('message', 'error', wp_get_referer()));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$paypal_email = get_user_meta($creator_id, 'wpdd_paypal_email', true);
|
||||||
|
if (empty($paypal_email)) {
|
||||||
|
wp_redirect(add_query_arg('message', 'error', wp_get_referer()));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
// Create the payout record
|
||||||
|
$result = $wpdb->insert(
|
||||||
|
$wpdb->prefix . 'wpdd_payouts',
|
||||||
|
array(
|
||||||
|
'creator_id' => $creator_id,
|
||||||
|
'amount' => $amount,
|
||||||
|
'currency' => get_option('wpdd_currency', 'USD'),
|
||||||
|
'paypal_email' => $paypal_email,
|
||||||
|
'status' => 'pending',
|
||||||
|
'payout_method' => 'manual',
|
||||||
|
'notes' => $reason,
|
||||||
|
'created_at' => current_time('mysql'),
|
||||||
|
'processed_by' => get_current_user_id()
|
||||||
|
),
|
||||||
|
array('%d', '%f', '%s', '%s', '%s', '%s', '%s', '%s', '%d')
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!$result) {
|
||||||
|
wp_redirect(add_query_arg('message', 'error', wp_get_referer()));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$payout_id = $wpdb->insert_id;
|
||||||
|
|
||||||
|
// Try to process via PayPal
|
||||||
|
if (class_exists('WPDD_PayPal_Payouts')) {
|
||||||
|
$paypal_result = WPDD_PayPal_Payouts::process_payout($payout_id);
|
||||||
|
|
||||||
|
if (!$paypal_result) {
|
||||||
|
// Update status to failed
|
||||||
|
$wpdb->update(
|
||||||
|
$wpdb->prefix . 'wpdd_payouts',
|
||||||
|
array('status' => 'failed'),
|
||||||
|
array('id' => $payout_id),
|
||||||
|
array('%s'),
|
||||||
|
array('%d')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wp_redirect(add_query_arg('message', 'success', wp_get_referer()));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function adjust_creator_balance() {
|
||||||
|
if (!current_user_can('manage_options') ||
|
||||||
|
!wp_verify_nonce($_POST['wpdd_nonce'], 'wpdd_adjust_balance')) {
|
||||||
|
wp_die(__('Security check failed', 'wp-digital-download'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$creator_id = intval($_POST['creator_id']);
|
||||||
|
$adjustment_type = sanitize_text_field($_POST['adjustment_type']);
|
||||||
|
$amount = floatval($_POST['adjustment_amount']);
|
||||||
|
$reason = sanitize_textarea_field($_POST['adjustment_reason']);
|
||||||
|
|
||||||
|
if (!$creator_id || $amount <= 0 || !in_array($adjustment_type, array('add', 'subtract'))) {
|
||||||
|
wp_redirect(add_query_arg('message', 'error', wp_get_referer()));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$creator = get_userdata($creator_id);
|
||||||
|
if (!$creator || !in_array('wpdd_creator', $creator->roles)) {
|
||||||
|
wp_redirect(add_query_arg('message', 'error', wp_get_referer()));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get current balance
|
||||||
|
$current_balance = floatval(get_user_meta($creator_id, 'wpdd_balance', true));
|
||||||
|
|
||||||
|
// Calculate new balance
|
||||||
|
if ($adjustment_type === 'add') {
|
||||||
|
$new_balance = $current_balance + $amount;
|
||||||
|
} else {
|
||||||
|
$new_balance = max(0, $current_balance - $amount); // Don't allow negative balance
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the balance
|
||||||
|
update_user_meta($creator_id, 'wpdd_balance', $new_balance);
|
||||||
|
|
||||||
|
// Create a record of this adjustment
|
||||||
|
global $wpdb;
|
||||||
|
$wpdb->insert(
|
||||||
|
$wpdb->prefix . 'wpdd_balance_adjustments',
|
||||||
|
array(
|
||||||
|
'creator_id' => $creator_id,
|
||||||
|
'adjustment_type' => $adjustment_type,
|
||||||
|
'amount' => $amount,
|
||||||
|
'previous_balance' => $current_balance,
|
||||||
|
'new_balance' => $new_balance,
|
||||||
|
'reason' => $reason,
|
||||||
|
'adjusted_by' => get_current_user_id(),
|
||||||
|
'created_at' => current_time('mysql')
|
||||||
|
),
|
||||||
|
array('%d', '%s', '%f', '%f', '%f', '%s', '%d', '%s')
|
||||||
|
);
|
||||||
|
|
||||||
|
wp_redirect(add_query_arg('message', 'balance_adjusted', wp_get_referer()));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
}
|
}
|
@@ -12,6 +12,7 @@ class WPDD_Admin {
|
|||||||
add_action('manage_wpdd_product_posts_custom_column', array(__CLASS__, 'render_product_columns'), 10, 2);
|
add_action('manage_wpdd_product_posts_custom_column', array(__CLASS__, 'render_product_columns'), 10, 2);
|
||||||
add_filter('manage_edit-wpdd_product_sortable_columns', array(__CLASS__, 'make_columns_sortable'));
|
add_filter('manage_edit-wpdd_product_sortable_columns', array(__CLASS__, 'make_columns_sortable'));
|
||||||
add_action('pre_get_posts', array(__CLASS__, 'sort_products_by_column'));
|
add_action('pre_get_posts', array(__CLASS__, 'sort_products_by_column'));
|
||||||
|
add_action('pre_get_posts', array(__CLASS__, 'filter_creator_products'));
|
||||||
add_action('admin_init', array(__CLASS__, 'handle_admin_actions'));
|
add_action('admin_init', array(__CLASS__, 'handle_admin_actions'));
|
||||||
|
|
||||||
// Initialize admin payouts
|
// Initialize admin payouts
|
||||||
@@ -21,41 +22,70 @@ class WPDD_Admin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static function add_admin_menus() {
|
public static function add_admin_menus() {
|
||||||
add_submenu_page(
|
// Show different menus based on user role
|
||||||
'edit.php?post_type=wpdd_product',
|
$user = wp_get_current_user();
|
||||||
__('Orders', 'wp-digital-download'),
|
$is_creator = in_array('wpdd_creator', (array) $user->roles);
|
||||||
__('Orders', 'wp-digital-download'),
|
$is_admin = current_user_can('manage_options');
|
||||||
'wpdd_manage_orders',
|
|
||||||
'wpdd-orders',
|
|
||||||
array(__CLASS__, 'render_orders_page')
|
|
||||||
);
|
|
||||||
|
|
||||||
add_submenu_page(
|
if ($is_admin) {
|
||||||
'edit.php?post_type=wpdd_product',
|
// Full admin menus
|
||||||
__('Reports', 'wp-digital-download'),
|
add_submenu_page(
|
||||||
__('Reports', 'wp-digital-download'),
|
'edit.php?post_type=wpdd_product',
|
||||||
'wpdd_view_reports',
|
__('Orders', 'wp-digital-download'),
|
||||||
'wpdd-reports',
|
__('Orders', 'wp-digital-download'),
|
||||||
array(__CLASS__, 'render_reports_page')
|
'wpdd_manage_orders',
|
||||||
);
|
'wpdd-orders',
|
||||||
|
array(__CLASS__, 'render_orders_page')
|
||||||
|
);
|
||||||
|
|
||||||
|
add_submenu_page(
|
||||||
|
'edit.php?post_type=wpdd_product',
|
||||||
|
__('Reports', 'wp-digital-download'),
|
||||||
|
__('Reports', 'wp-digital-download'),
|
||||||
|
'wpdd_view_reports',
|
||||||
|
'wpdd-reports',
|
||||||
|
array(__CLASS__, 'render_reports_page')
|
||||||
|
);
|
||||||
|
|
||||||
|
add_submenu_page(
|
||||||
|
'edit.php?post_type=wpdd_product',
|
||||||
|
__('Customers', 'wp-digital-download'),
|
||||||
|
__('Customers', 'wp-digital-download'),
|
||||||
|
'wpdd_manage_orders',
|
||||||
|
'wpdd-customers',
|
||||||
|
array(__CLASS__, 'render_customers_page')
|
||||||
|
);
|
||||||
|
|
||||||
|
add_submenu_page(
|
||||||
|
'edit.php?post_type=wpdd_product',
|
||||||
|
__('Shortcodes', 'wp-digital-download'),
|
||||||
|
__('Shortcodes', 'wp-digital-download'),
|
||||||
|
'edit_wpdd_products',
|
||||||
|
'wpdd-shortcodes',
|
||||||
|
array(__CLASS__, 'render_shortcodes_page')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
add_submenu_page(
|
if ($is_creator || $is_admin) {
|
||||||
'edit.php?post_type=wpdd_product',
|
// Creator-specific menus
|
||||||
__('Customers', 'wp-digital-download'),
|
add_submenu_page(
|
||||||
__('Customers', 'wp-digital-download'),
|
'edit.php?post_type=wpdd_product',
|
||||||
'wpdd_manage_orders',
|
__('My Sales', 'wp-digital-download'),
|
||||||
'wpdd-customers',
|
__('My Sales', 'wp-digital-download'),
|
||||||
array(__CLASS__, 'render_customers_page')
|
'wpdd_view_own_sales',
|
||||||
);
|
'wpdd-creator-sales',
|
||||||
|
array(__CLASS__, 'render_creator_sales_page')
|
||||||
add_submenu_page(
|
);
|
||||||
'edit.php?post_type=wpdd_product',
|
|
||||||
__('Shortcodes', 'wp-digital-download'),
|
add_submenu_page(
|
||||||
__('Shortcodes', 'wp-digital-download'),
|
'edit.php?post_type=wpdd_product',
|
||||||
'edit_wpdd_products',
|
__('My Payouts', 'wp-digital-download'),
|
||||||
'wpdd-shortcodes',
|
__('My Payouts', 'wp-digital-download'),
|
||||||
array(__CLASS__, 'render_shortcodes_page')
|
'wpdd_view_own_sales',
|
||||||
);
|
'wpdd-creator-payouts',
|
||||||
|
array(__CLASS__, 'render_creator_payouts_page')
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function add_product_columns($columns) {
|
public static function add_product_columns($columns) {
|
||||||
@@ -917,4 +947,289 @@ class WPDD_Admin {
|
|||||||
|
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function filter_creator_products($query) {
|
||||||
|
if (!is_admin() || !$query->is_main_query()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($_GET['post_type']) || $_GET['post_type'] !== 'wpdd_product') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = wp_get_current_user();
|
||||||
|
$is_creator = in_array('wpdd_creator', (array) $user->roles);
|
||||||
|
$is_admin = current_user_can('manage_options');
|
||||||
|
|
||||||
|
// Only filter for creators, not admins
|
||||||
|
if ($is_creator && !$is_admin) {
|
||||||
|
$query->set('author', get_current_user_id());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function render_creator_sales_page() {
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
$user_id = get_current_user_id();
|
||||||
|
$currency = get_option('wpdd_currency', 'USD');
|
||||||
|
$commission_rate = floatval(get_option('wpdd_commission_rate', 0));
|
||||||
|
|
||||||
|
// Get creator's sales data
|
||||||
|
$sales = $wpdb->get_results($wpdb->prepare(
|
||||||
|
"SELECT o.*, p.post_title as product_name,
|
||||||
|
(o.total * %f / 100) as platform_fee,
|
||||||
|
(o.total * (100 - %f) / 100) as creator_earning
|
||||||
|
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'
|
||||||
|
ORDER BY o.purchase_date DESC
|
||||||
|
LIMIT 100",
|
||||||
|
$commission_rate,
|
||||||
|
$commission_rate,
|
||||||
|
$user_id
|
||||||
|
));
|
||||||
|
|
||||||
|
// Get totals
|
||||||
|
$total_sales = $wpdb->get_var($wpdb->prepare(
|
||||||
|
"SELECT SUM(o.total)
|
||||||
|
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
|
||||||
|
));
|
||||||
|
|
||||||
|
$total_earnings = $total_sales * (1 - ($commission_rate / 100));
|
||||||
|
$current_balance = WPDD_Creator::get_creator_balance($user_id);
|
||||||
|
|
||||||
|
?>
|
||||||
|
<div class="wrap">
|
||||||
|
<h1><?php _e('My Sales Report', 'wp-digital-download'); ?></h1>
|
||||||
|
|
||||||
|
<div class="wpdd-stats-row" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin-bottom: 30px;">
|
||||||
|
<div class="wpdd-stat-card" style="background: #fff; padding: 20px; border: 1px solid #ccd0d4; border-radius: 4px;">
|
||||||
|
<h3 style="margin: 0 0 10px; color: #646970;"><?php _e('Total Sales', 'wp-digital-download'); ?></h3>
|
||||||
|
<div style="font-size: 24px; font-weight: bold; color: #1d2327;"><?php echo wpdd_format_price($total_sales, $currency); ?></div>
|
||||||
|
</div>
|
||||||
|
<div class="wpdd-stat-card" style="background: #fff; padding: 20px; border: 1px solid #ccd0d4; border-radius: 4px;">
|
||||||
|
<h3 style="margin: 0 0 10px; color: #646970;"><?php _e('Your Earnings', 'wp-digital-download'); ?></h3>
|
||||||
|
<div style="font-size: 24px; font-weight: bold; color: #1d2327;"><?php echo wpdd_format_price($total_earnings, $currency); ?></div>
|
||||||
|
<small style="color: #646970;"><?php printf(__('After %s%% platform fee', 'wp-digital-download'), $commission_rate); ?></small>
|
||||||
|
</div>
|
||||||
|
<div class="wpdd-stat-card" style="background: #fff; padding: 20px; border: 1px solid #ccd0d4; border-radius: 4px;">
|
||||||
|
<h3 style="margin: 0 0 10px; color: #646970;"><?php _e('Available Balance', 'wp-digital-download'); ?></h3>
|
||||||
|
<div style="font-size: 24px; font-weight: bold; color: #1d2327;"><?php echo wpdd_format_price($current_balance, $currency); ?></div>
|
||||||
|
<small style="color: #646970;"><?php _e('Ready for payout', 'wp-digital-download'); ?></small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if (!empty($sales)) : ?>
|
||||||
|
<div class="wpdd-sales-table">
|
||||||
|
<h2><?php _e('Recent Sales', 'wp-digital-download'); ?></h2>
|
||||||
|
<table class="wp-list-table widefat fixed striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th><?php _e('Date', 'wp-digital-download'); ?></th>
|
||||||
|
<th><?php _e('Product', 'wp-digital-download'); ?></th>
|
||||||
|
<th><?php _e('Customer', 'wp-digital-download'); ?></th>
|
||||||
|
<th><?php _e('Sale Amount', 'wp-digital-download'); ?></th>
|
||||||
|
<th><?php _e('Platform Fee', 'wp-digital-download'); ?></th>
|
||||||
|
<th><?php _e('Your Earning', 'wp-digital-download'); ?></th>
|
||||||
|
<th><?php _e('Status', 'wp-digital-download'); ?></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($sales as $sale) : ?>
|
||||||
|
<tr>
|
||||||
|
<td><?php echo esc_html(date_i18n(get_option('date_format'), strtotime($sale->purchase_date))); ?></td>
|
||||||
|
<td><?php echo esc_html($sale->product_name); ?></td>
|
||||||
|
<td><?php echo esc_html($sale->customer_name); ?></td>
|
||||||
|
<td><?php echo wpdd_format_price($sale->total, $currency); ?></td>
|
||||||
|
<td><?php echo wpdd_format_price($sale->platform_fee, $currency); ?></td>
|
||||||
|
<td><strong><?php echo wpdd_format_price($sale->creator_earning, $currency); ?></strong></td>
|
||||||
|
<td>
|
||||||
|
<span class="wpdd-status-<?php echo esc_attr($sale->status); ?>" style="padding: 2px 8px; border-radius: 3px; font-size: 12px; <?php echo $sale->status === 'completed' ? 'background: #d1e7dd; color: #0f5132;' : 'background: #f8d7da; color: #721c24;'; ?>">
|
||||||
|
<?php echo esc_html(ucfirst($sale->status)); ?>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<?php else : ?>
|
||||||
|
<div style="background: #fff; padding: 40px; border: 1px solid #ccd0d4; border-radius: 4px; text-align: center;">
|
||||||
|
<h3><?php _e('No sales yet', 'wp-digital-download'); ?></h3>
|
||||||
|
<p><?php _e('Once customers purchase your products, your sales data will appear here.', 'wp-digital-download'); ?></p>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function render_creator_payouts_page() {
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
if (isset($_POST['request_payout']) && wp_verify_nonce($_POST['wpdd_nonce'], 'wpdd_request_payout')) {
|
||||||
|
self::handle_payout_request();
|
||||||
|
}
|
||||||
|
|
||||||
|
$user_id = get_current_user_id();
|
||||||
|
$currency = get_option('wpdd_currency', 'USD');
|
||||||
|
$current_balance = WPDD_Creator::get_creator_balance($user_id);
|
||||||
|
$paypal_email = get_user_meta($user_id, 'wpdd_paypal_email', true);
|
||||||
|
$threshold = floatval(get_option('wpdd_payout_threshold', 0));
|
||||||
|
|
||||||
|
// Get payout history
|
||||||
|
$payouts = $wpdb->get_results($wpdb->prepare(
|
||||||
|
"SELECT * FROM {$wpdb->prefix}wpdd_payouts
|
||||||
|
WHERE creator_id = %d
|
||||||
|
ORDER BY created_at DESC
|
||||||
|
LIMIT 50",
|
||||||
|
$user_id
|
||||||
|
));
|
||||||
|
|
||||||
|
?>
|
||||||
|
<div class="wrap">
|
||||||
|
<h1><?php _e('My Payouts', 'wp-digital-download'); ?></h1>
|
||||||
|
|
||||||
|
<?php if (isset($_GET['message']) && $_GET['message'] === 'payout_requested') : ?>
|
||||||
|
<div class="notice notice-success is-dismissible">
|
||||||
|
<p><?php _e('Payout request submitted successfully!', 'wp-digital-download'); ?></p>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="wpdd-payout-request" style="background: #fff; padding: 20px; border: 1px solid #ccd0d4; border-radius: 4px; margin-bottom: 30px;">
|
||||||
|
<h2><?php _e('Request Payout', 'wp-digital-download'); ?></h2>
|
||||||
|
|
||||||
|
<div style="display: grid; grid-template-columns: 1fr 2fr; gap: 20px; align-items: start;">
|
||||||
|
<div>
|
||||||
|
<h3><?php _e('Current Balance', 'wp-digital-download'); ?></h3>
|
||||||
|
<div style="font-size: 32px; font-weight: bold; color: #1d2327; margin: 10px 0;">
|
||||||
|
<?php echo wpdd_format_price($current_balance, $currency); ?>
|
||||||
|
</div>
|
||||||
|
<?php if ($threshold > 0) : ?>
|
||||||
|
<p style="color: #646970; margin: 0;">
|
||||||
|
<?php printf(__('Minimum for automatic payout: %s', 'wp-digital-download'), wpdd_format_price($threshold, $currency)); ?>
|
||||||
|
</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<?php if (empty($paypal_email)) : ?>
|
||||||
|
<div class="notice notice-warning" style="margin: 0;">
|
||||||
|
<p><?php _e('Please add your PayPal email in your profile before requesting a payout.', 'wp-digital-download'); ?></p>
|
||||||
|
<p><a href="<?php echo esc_url(get_edit_profile_url($user_id)); ?>" class="button"><?php _e('Edit Profile', 'wp-digital-download'); ?></a></p>
|
||||||
|
</div>
|
||||||
|
<?php elseif ($current_balance <= 0) : ?>
|
||||||
|
<div class="notice notice-info" style="margin: 0;">
|
||||||
|
<p><?php _e('No balance available for payout.', 'wp-digital-download'); ?></p>
|
||||||
|
</div>
|
||||||
|
<?php else : ?>
|
||||||
|
<form method="post">
|
||||||
|
<?php wp_nonce_field('wpdd_request_payout', 'wpdd_nonce'); ?>
|
||||||
|
<p><?php _e('PayPal Email:', 'wp-digital-download'); ?> <strong><?php echo esc_html($paypal_email); ?></strong></p>
|
||||||
|
<p><?php _e('Requesting a payout will notify administrators to process your payment.', 'wp-digital-download'); ?></p>
|
||||||
|
<button type="submit" name="request_payout" class="button button-primary">
|
||||||
|
<?php printf(__('Request Payout of %s', 'wp-digital-download'), wpdd_format_price($current_balance, $currency)); ?>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if (!empty($payouts)) : ?>
|
||||||
|
<div class="wpdd-payout-history">
|
||||||
|
<h2><?php _e('Payout History', 'wp-digital-download'); ?></h2>
|
||||||
|
<table class="wp-list-table widefat fixed striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th><?php _e('Date Requested', 'wp-digital-download'); ?></th>
|
||||||
|
<th><?php _e('Amount', 'wp-digital-download'); ?></th>
|
||||||
|
<th><?php _e('PayPal Email', 'wp-digital-download'); ?></th>
|
||||||
|
<th><?php _e('Status', 'wp-digital-download'); ?></th>
|
||||||
|
<th><?php _e('Transaction ID', 'wp-digital-download'); ?></th>
|
||||||
|
<th><?php _e('Processed Date', 'wp-digital-download'); ?></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($payouts as $payout) : ?>
|
||||||
|
<tr>
|
||||||
|
<td><?php echo esc_html(date_i18n(get_option('date_format'), strtotime($payout->created_at))); ?></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
|
||||||
|
$status_colors = array(
|
||||||
|
'pending' => '#fef3c7; color: #92400e;',
|
||||||
|
'completed' => '#d1fae5; color: #065f46;',
|
||||||
|
'failed' => '#fee2e2; color: #991b1b;',
|
||||||
|
'requested' => '#dbeafe; color: #1e40af;'
|
||||||
|
);
|
||||||
|
$status_color = isset($status_colors[$payout->status]) ? $status_colors[$payout->status] : '#f3f4f6; color: #374151;';
|
||||||
|
?>
|
||||||
|
<span style="padding: 2px 8px; border-radius: 3px; font-size: 12px; background: <?php echo $status_color; ?>">
|
||||||
|
<?php echo esc_html(ucfirst($payout->status)); ?>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td><?php echo esc_html($payout->transaction_id ?: '-'); ?></td>
|
||||||
|
<td>
|
||||||
|
<?php
|
||||||
|
echo $payout->processed_at
|
||||||
|
? esc_html(date_i18n(get_option('date_format'), strtotime($payout->processed_at)))
|
||||||
|
: '-';
|
||||||
|
?>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<?php else : ?>
|
||||||
|
<div style="background: #fff; padding: 40px; border: 1px solid #ccd0d4; border-radius: 4px; text-align: center;">
|
||||||
|
<h3><?php _e('No payout history', 'wp-digital-download'); ?></h3>
|
||||||
|
<p><?php _e('Your payout requests will appear here once you make them.', 'wp-digital-download'); ?></p>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function handle_payout_request() {
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
$user_id = get_current_user_id();
|
||||||
|
$balance = WPDD_Creator::get_creator_balance($user_id);
|
||||||
|
$paypal_email = get_user_meta($user_id, 'wpdd_paypal_email', true);
|
||||||
|
|
||||||
|
if ($balance <= 0 || empty($paypal_email)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$currency = get_option('wpdd_currency', 'USD');
|
||||||
|
|
||||||
|
// Create payout request
|
||||||
|
$wpdb->insert(
|
||||||
|
$wpdb->prefix . 'wpdd_payouts',
|
||||||
|
array(
|
||||||
|
'creator_id' => $user_id,
|
||||||
|
'amount' => $balance,
|
||||||
|
'currency' => $currency,
|
||||||
|
'paypal_email' => $paypal_email,
|
||||||
|
'status' => 'requested',
|
||||||
|
'payout_method' => 'request',
|
||||||
|
'created_at' => current_time('mysql')
|
||||||
|
),
|
||||||
|
array('%d', '%f', '%s', '%s', '%s', '%s', '%s')
|
||||||
|
);
|
||||||
|
|
||||||
|
// Reset balance to 0 since it's now requested
|
||||||
|
update_user_meta($user_id, 'wpdd_creator_balance', 0);
|
||||||
|
|
||||||
|
// Redirect to avoid resubmission
|
||||||
|
wp_redirect(admin_url('edit.php?post_type=wpdd_product&page=wpdd-creator-payouts&message=payout_requested'));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
}
|
}
|
@@ -16,7 +16,7 @@ class WPDD_Settings {
|
|||||||
'edit.php?post_type=wpdd_product',
|
'edit.php?post_type=wpdd_product',
|
||||||
__('Settings', 'wp-digital-download'),
|
__('Settings', 'wp-digital-download'),
|
||||||
__('Settings', 'wp-digital-download'),
|
__('Settings', 'wp-digital-download'),
|
||||||
'wpdd_manage_settings',
|
'manage_options',
|
||||||
'wpdd-settings',
|
'wpdd-settings',
|
||||||
array(__CLASS__, 'render_settings_page')
|
array(__CLASS__, 'render_settings_page')
|
||||||
);
|
);
|
||||||
@@ -26,9 +26,16 @@ class WPDD_Settings {
|
|||||||
register_setting('wpdd_settings', 'wpdd_paypal_mode');
|
register_setting('wpdd_settings', 'wpdd_paypal_mode');
|
||||||
register_setting('wpdd_settings', 'wpdd_paypal_client_id');
|
register_setting('wpdd_settings', 'wpdd_paypal_client_id');
|
||||||
register_setting('wpdd_settings', 'wpdd_paypal_secret');
|
register_setting('wpdd_settings', 'wpdd_paypal_secret');
|
||||||
|
register_setting('wpdd_settings', 'wpdd_paypal_payout_email');
|
||||||
register_setting('wpdd_settings', 'wpdd_admin_email');
|
register_setting('wpdd_settings', 'wpdd_admin_email');
|
||||||
register_setting('wpdd_settings', 'wpdd_from_name');
|
register_setting('wpdd_settings', 'wpdd_from_name');
|
||||||
register_setting('wpdd_settings', 'wpdd_from_email');
|
register_setting('wpdd_settings', 'wpdd_from_email');
|
||||||
|
register_setting('wpdd_settings', 'wpdd_smtp_enabled');
|
||||||
|
register_setting('wpdd_settings', 'wpdd_smtp_host');
|
||||||
|
register_setting('wpdd_settings', 'wpdd_smtp_port');
|
||||||
|
register_setting('wpdd_settings', 'wpdd_smtp_username');
|
||||||
|
register_setting('wpdd_settings', 'wpdd_smtp_password');
|
||||||
|
register_setting('wpdd_settings', 'wpdd_smtp_encryption');
|
||||||
register_setting('wpdd_settings', 'wpdd_currency');
|
register_setting('wpdd_settings', 'wpdd_currency');
|
||||||
register_setting('wpdd_settings', 'wpdd_enable_guest_checkout');
|
register_setting('wpdd_settings', 'wpdd_enable_guest_checkout');
|
||||||
register_setting('wpdd_settings', 'wpdd_default_download_limit');
|
register_setting('wpdd_settings', 'wpdd_default_download_limit');
|
||||||
@@ -193,6 +200,18 @@ class WPDD_Settings {
|
|||||||
'wpdd_paypal_settings',
|
'wpdd_paypal_settings',
|
||||||
array('name' => 'wpdd_paypal_secret')
|
array('name' => 'wpdd_paypal_secret')
|
||||||
);
|
);
|
||||||
|
|
||||||
|
add_settings_field(
|
||||||
|
'wpdd_paypal_payout_email',
|
||||||
|
__('PayPal Payout Account Email', 'wp-digital-download'),
|
||||||
|
array(__CLASS__, 'email_field'),
|
||||||
|
'wpdd_settings',
|
||||||
|
'wpdd_paypal_settings',
|
||||||
|
array(
|
||||||
|
'name' => 'wpdd_paypal_payout_email',
|
||||||
|
'description' => __('PayPal account email that will send payouts to creators', 'wp-digital-download')
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function add_email_fields() {
|
private static function add_email_fields() {
|
||||||
@@ -231,6 +250,94 @@ class WPDD_Settings {
|
|||||||
'description' => __('Email address shown in email headers', 'wp-digital-download')
|
'description' => __('Email address shown in email headers', 'wp-digital-download')
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
add_settings_field(
|
||||||
|
'wpdd_smtp_enabled',
|
||||||
|
__('Enable SMTP', 'wp-digital-download'),
|
||||||
|
array(__CLASS__, 'checkbox_field'),
|
||||||
|
'wpdd_settings',
|
||||||
|
'wpdd_email_settings',
|
||||||
|
array(
|
||||||
|
'name' => 'wpdd_smtp_enabled',
|
||||||
|
'label' => __('Use SMTP for sending emails instead of PHP mail()', 'wp-digital-download')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
add_settings_field(
|
||||||
|
'wpdd_smtp_host',
|
||||||
|
__('SMTP Host', 'wp-digital-download'),
|
||||||
|
array(__CLASS__, 'text_field'),
|
||||||
|
'wpdd_settings',
|
||||||
|
'wpdd_email_settings',
|
||||||
|
array(
|
||||||
|
'name' => 'wpdd_smtp_host',
|
||||||
|
'description' => __('SMTP server hostname (e.g., smtp.gmail.com)', 'wp-digital-download')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
add_settings_field(
|
||||||
|
'wpdd_smtp_port',
|
||||||
|
__('SMTP Port', 'wp-digital-download'),
|
||||||
|
array(__CLASS__, 'number_field'),
|
||||||
|
'wpdd_settings',
|
||||||
|
'wpdd_email_settings',
|
||||||
|
array(
|
||||||
|
'name' => 'wpdd_smtp_port',
|
||||||
|
'description' => __('SMTP server port number (common ports: 25, 465, 587)', 'wp-digital-download'),
|
||||||
|
'min' => 1,
|
||||||
|
'max' => 65535
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
add_settings_field(
|
||||||
|
'wpdd_smtp_encryption',
|
||||||
|
__('SMTP Encryption', 'wp-digital-download'),
|
||||||
|
array(__CLASS__, 'select_field'),
|
||||||
|
'wpdd_settings',
|
||||||
|
'wpdd_email_settings',
|
||||||
|
array(
|
||||||
|
'name' => 'wpdd_smtp_encryption',
|
||||||
|
'options' => array(
|
||||||
|
'' => __('None', 'wp-digital-download'),
|
||||||
|
'tls' => __('TLS', 'wp-digital-download'),
|
||||||
|
'ssl' => __('SSL', 'wp-digital-download')
|
||||||
|
),
|
||||||
|
'description' => __('Select encryption method - TLS is recommended for most providers', 'wp-digital-download')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
add_settings_field(
|
||||||
|
'wpdd_smtp_autodetect',
|
||||||
|
__('Auto-Detect Settings', 'wp-digital-download'),
|
||||||
|
array(__CLASS__, 'smtp_autodetect_field'),
|
||||||
|
'wpdd_settings',
|
||||||
|
'wpdd_email_settings',
|
||||||
|
array()
|
||||||
|
);
|
||||||
|
|
||||||
|
add_settings_field(
|
||||||
|
'wpdd_smtp_username',
|
||||||
|
__('SMTP Username', 'wp-digital-download'),
|
||||||
|
array(__CLASS__, 'text_field'),
|
||||||
|
'wpdd_settings',
|
||||||
|
'wpdd_email_settings',
|
||||||
|
array(
|
||||||
|
'name' => 'wpdd_smtp_username',
|
||||||
|
'description' => __('SMTP authentication username', 'wp-digital-download')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
add_settings_field(
|
||||||
|
'wpdd_smtp_password',
|
||||||
|
__('SMTP Password', 'wp-digital-download'),
|
||||||
|
array(__CLASS__, 'password_field'),
|
||||||
|
'wpdd_settings',
|
||||||
|
'wpdd_email_settings',
|
||||||
|
array(
|
||||||
|
'name' => 'wpdd_smtp_password',
|
||||||
|
'description' => __('SMTP authentication password', 'wp-digital-download')
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function add_download_fields() {
|
private static function add_download_fields() {
|
||||||
@@ -305,59 +412,101 @@ class WPDD_Settings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static function render_settings_page() {
|
public static function render_settings_page() {
|
||||||
|
$active_tab = isset($_GET['tab']) ? $_GET['tab'] : 'general';
|
||||||
?>
|
?>
|
||||||
<div class="wrap">
|
<div class="wrap">
|
||||||
<h1><?php _e('WP Digital Download Settings', 'wp-digital-download'); ?></h1>
|
<h1><?php _e('WP Digital Download Settings', 'wp-digital-download'); ?></h1>
|
||||||
|
|
||||||
<div class="wpdd-settings-sidebar">
|
<h2 class="nav-tab-wrapper">
|
||||||
<div class="wpdd-settings-box">
|
<a href="<?php echo admin_url('edit.php?post_type=wpdd_product&page=wpdd-settings&tab=general'); ?>"
|
||||||
<h3><?php _e('Quick Setup', 'wp-digital-download'); ?></h3>
|
class="nav-tab <?php echo $active_tab == 'general' ? 'nav-tab-active' : ''; ?>"><?php _e('General', 'wp-digital-download'); ?></a>
|
||||||
<p><?php _e('To get started quickly:', 'wp-digital-download'); ?></p>
|
<a href="<?php echo admin_url('edit.php?post_type=wpdd_product&page=wpdd-settings&tab=paypal'); ?>"
|
||||||
<ol>
|
class="nav-tab <?php echo $active_tab == 'paypal' ? 'nav-tab-active' : ''; ?>"><?php _e('PayPal', 'wp-digital-download'); ?></a>
|
||||||
<li><?php _e('Configure PayPal settings above', 'wp-digital-download'); ?></li>
|
<a href="<?php echo admin_url('edit.php?post_type=wpdd_product&page=wpdd-settings&tab=email'); ?>"
|
||||||
<li><?php _e('Create your first product', 'wp-digital-download'); ?></li>
|
class="nav-tab <?php echo $active_tab == 'email' ? 'nav-tab-active' : ''; ?>"><?php _e('Email', 'wp-digital-download'); ?></a>
|
||||||
<li><?php _e('Add the shop shortcode [wpdd_shop] to a page', 'wp-digital-download'); ?></li>
|
<a href="<?php echo admin_url('edit.php?post_type=wpdd_product&page=wpdd-settings&tab=downloads'); ?>"
|
||||||
<li><?php _e('Test with a free product first', 'wp-digital-download'); ?></li>
|
class="nav-tab <?php echo $active_tab == 'downloads' ? 'nav-tab-active' : ''; ?>"><?php _e('Downloads', 'wp-digital-download'); ?></a>
|
||||||
</ol>
|
<a href="<?php echo admin_url('edit.php?post_type=wpdd_product&page=wpdd-settings&tab=watermark'); ?>"
|
||||||
|
class="nav-tab <?php echo $active_tab == 'watermark' ? 'nav-tab-active' : ''; ?>"><?php _e('Watermark', 'wp-digital-download'); ?></a>
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<div class="wpdd-settings-container">
|
||||||
|
<div class="wpdd-settings-content">
|
||||||
|
<form method="post" action="options.php" class="wpdd-settings-form">
|
||||||
|
<?php
|
||||||
|
settings_fields('wpdd_settings');
|
||||||
|
|
||||||
|
switch ($active_tab) {
|
||||||
|
case 'general':
|
||||||
|
self::render_general_tab();
|
||||||
|
break;
|
||||||
|
case 'paypal':
|
||||||
|
self::render_paypal_tab();
|
||||||
|
break;
|
||||||
|
case 'email':
|
||||||
|
self::render_email_tab();
|
||||||
|
break;
|
||||||
|
case 'downloads':
|
||||||
|
self::render_downloads_tab();
|
||||||
|
break;
|
||||||
|
case 'watermark':
|
||||||
|
self::render_watermark_tab();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
self::render_general_tab();
|
||||||
|
}
|
||||||
|
|
||||||
|
submit_button();
|
||||||
|
?>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="wpdd-settings-box">
|
<div class="wpdd-settings-sidebar">
|
||||||
<h3><?php _e('Available Shortcodes', 'wp-digital-download'); ?></h3>
|
<?php if ($active_tab == 'general') : ?>
|
||||||
<ul>
|
<div class="wpdd-settings-box">
|
||||||
<li><code>[wpdd_shop]</code> - <?php _e('Display product storefront', 'wp-digital-download'); ?></li>
|
<h3><?php _e('Quick Setup', 'wp-digital-download'); ?></h3>
|
||||||
<li><code>[wpdd_customer_purchases]</code> - <?php _e('Customer purchase history', 'wp-digital-download'); ?></li>
|
<p><?php _e('To get started quickly:', 'wp-digital-download'); ?></p>
|
||||||
<li><code>[wpdd_checkout]</code> - <?php _e('Checkout page', 'wp-digital-download'); ?></li>
|
<ol>
|
||||||
<li><code>[wpdd_thank_you]</code> - <?php _e('Thank you page', 'wp-digital-download'); ?></li>
|
<li><?php _e('Configure PayPal settings', 'wp-digital-download'); ?></li>
|
||||||
<li><code>[wpdd_product id="123"]</code> - <?php _e('Single product display', 'wp-digital-download'); ?></li>
|
<li><?php _e('Create your first product', 'wp-digital-download'); ?></li>
|
||||||
</ul>
|
<li><?php _e('Add the shop shortcode [wpdd_shop] to a page', 'wp-digital-download'); ?></li>
|
||||||
</div>
|
<li><?php _e('Test with a free product first', 'wp-digital-download'); ?></li>
|
||||||
|
</ol>
|
||||||
<div class="wpdd-settings-box">
|
</div>
|
||||||
<h3><?php _e('System Status', 'wp-digital-download'); ?></h3>
|
|
||||||
<?php self::system_status(); ?>
|
<div class="wpdd-settings-box">
|
||||||
|
<h3><?php _e('Available Shortcodes', 'wp-digital-download'); ?></h3>
|
||||||
|
<ul>
|
||||||
|
<li><code>[wpdd_shop]</code> - <?php _e('Display product storefront', 'wp-digital-download'); ?></li>
|
||||||
|
<li><code>[wpdd_customer_purchases]</code> - <?php _e('Customer purchase history', 'wp-digital-download'); ?></li>
|
||||||
|
<li><code>[wpdd_checkout]</code> - <?php _e('Checkout page', 'wp-digital-download'); ?></li>
|
||||||
|
<li><code>[wpdd_thank_you]</code> - <?php _e('Thank you page', 'wp-digital-download'); ?></li>
|
||||||
|
<li><code>[wpdd_product id="123"]</code> - <?php _e('Single product display', 'wp-digital-download'); ?></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="wpdd-settings-box">
|
||||||
|
<h3><?php _e('System Status', 'wp-digital-download'); ?></h3>
|
||||||
|
<?php self::system_status(); ?>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form method="post" action="options.php" class="wpdd-settings-form">
|
|
||||||
<?php
|
|
||||||
settings_fields('wpdd_settings');
|
|
||||||
do_settings_sections('wpdd_settings');
|
|
||||||
submit_button();
|
|
||||||
?>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.wpdd-settings-sidebar {
|
.wpdd-settings-container {
|
||||||
float: right;
|
display: flex;
|
||||||
width: 300px;
|
gap: 20px;
|
||||||
margin-left: 20px;
|
margin-top: 20px;
|
||||||
position: relative;
|
|
||||||
z-index: 10;
|
|
||||||
}
|
}
|
||||||
.wpdd-settings-form {
|
.wpdd-settings-content {
|
||||||
overflow: hidden;
|
flex: 1;
|
||||||
margin-right: 340px;
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.wpdd-settings-sidebar {
|
||||||
|
width: 300px;
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
.wpdd-settings-box {
|
.wpdd-settings-box {
|
||||||
background: white;
|
background: white;
|
||||||
@@ -377,15 +526,21 @@ class WPDD_Settings {
|
|||||||
.wpdd-status-good { color: #46b450; }
|
.wpdd-status-good { color: #46b450; }
|
||||||
.wpdd-status-warning { color: #ffb900; }
|
.wpdd-status-warning { color: #ffb900; }
|
||||||
.wpdd-status-error { color: #dc3232; }
|
.wpdd-status-error { color: #dc3232; }
|
||||||
|
|
||||||
|
.wpdd-tab-content {
|
||||||
|
background: white;
|
||||||
|
border: 1px solid #ccd0d4;
|
||||||
|
padding: 20px;
|
||||||
|
margin-top: -1px;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 1200px) {
|
@media (max-width: 1200px) {
|
||||||
.wpdd-settings-sidebar {
|
.wpdd-settings-container {
|
||||||
float: none;
|
flex-direction: column;
|
||||||
width: 100%;
|
|
||||||
margin-left: 0;
|
|
||||||
margin-top: 30px;
|
|
||||||
}
|
}
|
||||||
.wpdd-settings-form {
|
.wpdd-settings-sidebar {
|
||||||
margin-right: 0;
|
width: 100%;
|
||||||
|
order: 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -584,9 +739,98 @@ class WPDD_Settings {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function smtp_autodetect_field($args) {
|
||||||
|
?>
|
||||||
|
<button type="button" id="wpdd-smtp-autodetect" class="button button-secondary">
|
||||||
|
<?php _e('Auto-Detect SMTP Settings', 'wp-digital-download'); ?>
|
||||||
|
</button>
|
||||||
|
<span id="wpdd-smtp-autodetect-status" style="margin-left: 10px;"></span>
|
||||||
|
<p class="description">
|
||||||
|
<?php _e('Automatically detect port and encryption settings based on the SMTP host.', 'wp-digital-download'); ?>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
jQuery(document).ready(function($) {
|
||||||
|
$('#wpdd-smtp-autodetect').on('click', function() {
|
||||||
|
var $button = $(this);
|
||||||
|
var $status = $('#wpdd-smtp-autodetect-status');
|
||||||
|
var host = $('#wpdd_smtp_host').val();
|
||||||
|
|
||||||
|
if (!host) {
|
||||||
|
$status.html('<span style="color: #dc3232;"><?php _e('Please enter SMTP host first.', 'wp-digital-download'); ?></span>');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$button.prop('disabled', true).text('<?php _e('Detecting...', 'wp-digital-download'); ?>');
|
||||||
|
$status.html('<span style="color: #0073aa;"><?php _e('Testing connection...', 'wp-digital-download'); ?></span>');
|
||||||
|
|
||||||
|
// Test common SMTP configurations
|
||||||
|
var configs = [
|
||||||
|
{ port: 587, encryption: 'tls' },
|
||||||
|
{ port: 465, encryption: 'ssl' },
|
||||||
|
{ port: 25, encryption: 'tls' },
|
||||||
|
{ port: 25, encryption: '' }
|
||||||
|
];
|
||||||
|
|
||||||
|
// Detect common providers
|
||||||
|
var detectedConfig = null;
|
||||||
|
var hostname = host.toLowerCase();
|
||||||
|
|
||||||
|
if (hostname.includes('gmail.com') || hostname.includes('google.com')) {
|
||||||
|
detectedConfig = { port: 587, encryption: 'tls' };
|
||||||
|
} else if (hostname.includes('outlook.com') || hostname.includes('hotmail.com') || hostname.includes('live.com')) {
|
||||||
|
detectedConfig = { port: 587, encryption: 'tls' };
|
||||||
|
} else if (hostname.includes('yahoo.com')) {
|
||||||
|
detectedConfig = { port: 587, encryption: 'tls' };
|
||||||
|
} else if (hostname.includes('smtp.') && hostname.includes('.com')) {
|
||||||
|
detectedConfig = { port: 587, encryption: 'tls' };
|
||||||
|
} else {
|
||||||
|
// Default to most common configuration
|
||||||
|
detectedConfig = { port: 587, encryption: 'tls' };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply detected settings
|
||||||
|
setTimeout(function() {
|
||||||
|
$('#wpdd_smtp_port').val(detectedConfig.port);
|
||||||
|
$('#wpdd_smtp_encryption').val(detectedConfig.encryption);
|
||||||
|
|
||||||
|
$button.prop('disabled', false).text('<?php _e('Auto-Detect SMTP Settings', 'wp-digital-download'); ?>');
|
||||||
|
$status.html('<span style="color: #46b450;"><?php _e('Settings detected and applied!', 'wp-digital-download'); ?></span>');
|
||||||
|
|
||||||
|
// Clear status after 5 seconds
|
||||||
|
setTimeout(function() {
|
||||||
|
$status.html('');
|
||||||
|
}, 5000);
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
private static function system_status() {
|
private static function system_status() {
|
||||||
$status = array();
|
$status = array();
|
||||||
|
|
||||||
|
// Check if WPDD_UPLOADS_DIR is defined to prevent fatal errors
|
||||||
|
if (!defined('WPDD_UPLOADS_DIR')) {
|
||||||
|
$status[] = array(
|
||||||
|
'label' => __('Plugin Constants', 'wp-digital-download'),
|
||||||
|
'value' => __('Not Loaded', 'wp-digital-download'),
|
||||||
|
'class' => 'wpdd-status-error'
|
||||||
|
);
|
||||||
|
echo '<ul>';
|
||||||
|
foreach ($status as $item) {
|
||||||
|
printf(
|
||||||
|
'<li>%s: <span class="%s">%s</span></li>',
|
||||||
|
esc_html($item['label']),
|
||||||
|
esc_attr($item['class']),
|
||||||
|
esc_html($item['value'])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
echo '</ul>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$upload_dir = wp_upload_dir();
|
$upload_dir = wp_upload_dir();
|
||||||
$protected_dir = trailingslashit($upload_dir['basedir']) . WPDD_UPLOADS_DIR;
|
$protected_dir = trailingslashit($upload_dir['basedir']) . WPDD_UPLOADS_DIR;
|
||||||
|
|
||||||
@@ -659,6 +903,73 @@ class WPDD_Settings {
|
|||||||
echo '</ul>';
|
echo '</ul>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function render_general_tab() {
|
||||||
|
?>
|
||||||
|
<div class="wpdd-tab-content">
|
||||||
|
<h2><?php _e('General Settings', 'wp-digital-download'); ?></h2>
|
||||||
|
<?php self::do_settings_sections_for_tab('wpdd_general_settings'); ?>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function render_paypal_tab() {
|
||||||
|
?>
|
||||||
|
<div class="wpdd-tab-content">
|
||||||
|
<h2><?php _e('PayPal Settings', 'wp-digital-download'); ?></h2>
|
||||||
|
<?php self::do_settings_sections_for_tab('wpdd_paypal_settings'); ?>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function render_email_tab() {
|
||||||
|
?>
|
||||||
|
<div class="wpdd-tab-content">
|
||||||
|
<h2><?php _e('Email Settings', 'wp-digital-download'); ?></h2>
|
||||||
|
<?php self::do_settings_sections_for_tab('wpdd_email_settings'); ?>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function render_downloads_tab() {
|
||||||
|
?>
|
||||||
|
<div class="wpdd-tab-content">
|
||||||
|
<h2><?php _e('Download Settings', 'wp-digital-download'); ?></h2>
|
||||||
|
<?php self::do_settings_sections_for_tab('wpdd_download_settings'); ?>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function render_watermark_tab() {
|
||||||
|
?>
|
||||||
|
<div class="wpdd-tab-content">
|
||||||
|
<h2><?php _e('Watermark Settings', 'wp-digital-download'); ?></h2>
|
||||||
|
<?php self::do_settings_sections_for_tab('wpdd_watermark_settings'); ?>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function do_settings_sections_for_tab($section_id) {
|
||||||
|
global $wp_settings_sections, $wp_settings_fields;
|
||||||
|
|
||||||
|
if (!isset($wp_settings_sections['wpdd_settings'][$section_id])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$section = $wp_settings_sections['wpdd_settings'][$section_id];
|
||||||
|
|
||||||
|
if (isset($section['callback']) && $section['callback']) {
|
||||||
|
call_user_func($section['callback'], $section);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($wp_settings_fields['wpdd_settings'][$section_id])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo '<table class="form-table" role="presentation">';
|
||||||
|
do_settings_fields('wpdd_settings', $section_id);
|
||||||
|
echo '</table>';
|
||||||
|
}
|
||||||
|
|
||||||
public static function sanitize_commission_rate($input) {
|
public static function sanitize_commission_rate($input) {
|
||||||
$value = floatval($input);
|
$value = floatval($input);
|
||||||
if ($value < 0) {
|
if ($value < 0) {
|
||||||
|
261
admin/views/order-details.php
Normal file
261
admin/views/order-details.php
Normal file
@@ -0,0 +1,261 @@
|
|||||||
|
<?php
|
||||||
|
if (!defined('ABSPATH')) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// $order variable is passed from the calling function
|
||||||
|
?>
|
||||||
|
<div class="wrap">
|
||||||
|
<h1><?php _e('Order Details', 'wp-digital-download'); ?></h1>
|
||||||
|
|
||||||
|
<div class="order-details-container" style="max-width: 800px;">
|
||||||
|
|
||||||
|
<div class="order-summary" style="background: #fff; border: 1px solid #ccd0d4; padding: 20px; margin-bottom: 20px;">
|
||||||
|
<h2><?php _e('Order Summary', 'wp-digital-download'); ?></h2>
|
||||||
|
|
||||||
|
<table class="widefat fixed">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style="width: 200px;"><strong><?php _e('Order Number', 'wp-digital-download'); ?></strong></td>
|
||||||
|
<td><?php echo esc_html($order->order_number); ?></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong><?php _e('Status', 'wp-digital-download'); ?></strong></td>
|
||||||
|
<td>
|
||||||
|
<span class="order-status status-<?php echo esc_attr($order->status); ?>" style="padding: 4px 8px; border-radius: 3px; font-size: 12px; <?php
|
||||||
|
echo $order->status === 'completed' ? 'background: #d1e7dd; color: #0f5132;' :
|
||||||
|
($order->status === 'pending' ? 'background: #fff3cd; color: #856404;' :
|
||||||
|
'background: #f8d7da; color: #721c24;');
|
||||||
|
?>">
|
||||||
|
<?php echo esc_html(ucfirst($order->status)); ?>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong><?php _e('Purchase Date', 'wp-digital-download'); ?></strong></td>
|
||||||
|
<td><?php echo esc_html(date_i18n(get_option('date_format') . ' ' . get_option('time_format'), strtotime($order->purchase_date))); ?></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong><?php _e('Payment Method', 'wp-digital-download'); ?></strong></td>
|
||||||
|
<td>
|
||||||
|
<?php
|
||||||
|
$payment_methods = array(
|
||||||
|
'paypal' => 'PayPal',
|
||||||
|
'free' => 'Free Download',
|
||||||
|
'manual' => 'Manual Payment'
|
||||||
|
);
|
||||||
|
echo esc_html($payment_methods[$order->payment_method] ?? ucfirst($order->payment_method));
|
||||||
|
?>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php if (!empty($order->transaction_id)) : ?>
|
||||||
|
<tr>
|
||||||
|
<td><strong><?php _e('Transaction ID', 'wp-digital-download'); ?></strong></td>
|
||||||
|
<td><code><?php echo esc_html($order->transaction_id); ?></code></td>
|
||||||
|
</tr>
|
||||||
|
<?php endif; ?>
|
||||||
|
<tr>
|
||||||
|
<td><strong><?php _e('Amount', 'wp-digital-download'); ?></strong></td>
|
||||||
|
<td>
|
||||||
|
<strong style="font-size: 16px;">
|
||||||
|
<?php echo wpdd_format_price($order->amount, $order->currency); ?>
|
||||||
|
</strong>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="customer-details" style="background: #fff; border: 1px solid #ccd0d4; padding: 20px; margin-bottom: 20px;">
|
||||||
|
<h2><?php _e('Customer Information', 'wp-digital-download'); ?></h2>
|
||||||
|
|
||||||
|
<table class="widefat fixed">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style="width: 200px;"><strong><?php _e('Customer Name', 'wp-digital-download'); ?></strong></td>
|
||||||
|
<td><?php echo esc_html($order->customer_name); ?></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong><?php _e('Email Address', 'wp-digital-download'); ?></strong></td>
|
||||||
|
<td>
|
||||||
|
<a href="mailto:<?php echo esc_attr($order->customer_email); ?>">
|
||||||
|
<?php echo esc_html($order->customer_email); ?>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php if ($order->customer_id > 0) : ?>
|
||||||
|
<tr>
|
||||||
|
<td><strong><?php _e('WordPress User', 'wp-digital-download'); ?></strong></td>
|
||||||
|
<td>
|
||||||
|
<a href="<?php echo admin_url('user-edit.php?user_id=' . $order->customer_id); ?>">
|
||||||
|
<?php _e('View User Profile', 'wp-digital-download'); ?>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endif; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="product-details" style="background: #fff; border: 1px solid #ccd0d4; padding: 20px; margin-bottom: 20px;">
|
||||||
|
<h2><?php _e('Product Information', 'wp-digital-download'); ?></h2>
|
||||||
|
|
||||||
|
<table class="widefat fixed">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style="width: 200px;"><strong><?php _e('Product', 'wp-digital-download'); ?></strong></td>
|
||||||
|
<td>
|
||||||
|
<a href="<?php echo admin_url('post.php?post=' . $order->product_id . '&action=edit'); ?>">
|
||||||
|
<?php echo esc_html($order->product_name); ?>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong><?php _e('Product ID', 'wp-digital-download'); ?></strong></td>
|
||||||
|
<td><?php echo esc_html($order->product_id); ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php if ($order->creator_id > 0) : ?>
|
||||||
|
<tr>
|
||||||
|
<td><strong><?php _e('Creator', 'wp-digital-download'); ?></strong></td>
|
||||||
|
<td>
|
||||||
|
<?php
|
||||||
|
$creator = get_userdata($order->creator_id);
|
||||||
|
if ($creator) : ?>
|
||||||
|
<a href="<?php echo admin_url('user-edit.php?user_id=' . $order->creator_id); ?>">
|
||||||
|
<?php echo esc_html($creator->display_name); ?>
|
||||||
|
</a>
|
||||||
|
<?php else : ?>
|
||||||
|
<?php _e('Creator not found', 'wp-digital-download'); ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endif; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
// Get download links for this order
|
||||||
|
global $wpdb;
|
||||||
|
$download_links = $wpdb->get_results($wpdb->prepare(
|
||||||
|
"SELECT * FROM {$wpdb->prefix}wpdd_download_links WHERE order_id = %d ORDER BY created_at DESC",
|
||||||
|
$order->id
|
||||||
|
));
|
||||||
|
?>
|
||||||
|
|
||||||
|
<?php if (!empty($download_links)) : ?>
|
||||||
|
<div class="download-links" style="background: #fff; border: 1px solid #ccd0d4; padding: 20px; margin-bottom: 20px;">
|
||||||
|
<h2><?php _e('Download Links', 'wp-digital-download'); ?></h2>
|
||||||
|
|
||||||
|
<table class="wp-list-table widefat fixed striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th><?php _e('Download Token', 'wp-digital-download'); ?></th>
|
||||||
|
<th><?php _e('Downloads', 'wp-digital-download'); ?></th>
|
||||||
|
<th><?php _e('Expires', 'wp-digital-download'); ?></th>
|
||||||
|
<th><?php _e('Created', 'wp-digital-download'); ?></th>
|
||||||
|
<th><?php _e('Status', 'wp-digital-download'); ?></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($download_links as $link) :
|
||||||
|
$is_expired = strtotime($link->expires_at) < current_time('timestamp');
|
||||||
|
$is_used_up = $link->download_count >= $link->max_downloads;
|
||||||
|
?>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<code style="font-size: 11px;"><?php echo esc_html(substr($link->token, 0, 20)) . '...'; ?></code>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<?php echo esc_html($link->download_count); ?> / <?php echo esc_html($link->max_downloads); ?>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<?php
|
||||||
|
if ($is_expired) : ?>
|
||||||
|
<span style="color: #d63384;"><?php _e('Expired', 'wp-digital-download'); ?></span>
|
||||||
|
<?php else : ?>
|
||||||
|
<?php echo esc_html(date_i18n(get_option('date_format'), strtotime($link->expires_at))); ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
</td>
|
||||||
|
<td><?php echo esc_html(date_i18n(get_option('date_format'), strtotime($link->created_at))); ?></td>
|
||||||
|
<td>
|
||||||
|
<?php if ($is_expired) : ?>
|
||||||
|
<span style="color: #d63384;"><?php _e('Expired', 'wp-digital-download'); ?></span>
|
||||||
|
<?php elseif ($is_used_up) : ?>
|
||||||
|
<span style="color: #fd7e14;"><?php _e('Used Up', 'wp-digital-download'); ?></span>
|
||||||
|
<?php else : ?>
|
||||||
|
<span style="color: #198754;"><?php _e('Active', 'wp-digital-download'); ?></span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
// Get download history for this order
|
||||||
|
$downloads = $wpdb->get_results($wpdb->prepare(
|
||||||
|
"SELECT * FROM {$wpdb->prefix}wpdd_downloads WHERE order_id = %d ORDER BY download_date DESC LIMIT 20",
|
||||||
|
$order->id
|
||||||
|
));
|
||||||
|
?>
|
||||||
|
|
||||||
|
<?php if (!empty($downloads)) : ?>
|
||||||
|
<div class="download-history" style="background: #fff; border: 1px solid #ccd0d4; padding: 20px; margin-bottom: 20px;">
|
||||||
|
<h2><?php _e('Download History', 'wp-digital-download'); ?>
|
||||||
|
<small>(<?php printf(__('Last %d downloads', 'wp-digital-download'), count($downloads)); ?>)</small>
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<table class="wp-list-table widefat fixed striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th><?php _e('Date', 'wp-digital-download'); ?></th>
|
||||||
|
<th><?php _e('IP Address', 'wp-digital-download'); ?></th>
|
||||||
|
<th><?php _e('User Agent', 'wp-digital-download'); ?></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($downloads as $download) : ?>
|
||||||
|
<tr>
|
||||||
|
<td><?php echo esc_html(date_i18n(get_option('date_format') . ' ' . get_option('time_format'), strtotime($download->download_date))); ?></td>
|
||||||
|
<td><code><?php echo esc_html($download->ip_address); ?></code></td>
|
||||||
|
<td style="font-size: 11px;">
|
||||||
|
<?php echo esc_html(wp_trim_words($download->user_agent, 10)); ?>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="order-actions" style="margin-top: 30px;">
|
||||||
|
<a href="<?php echo admin_url('edit.php?post_type=wpdd_product&page=wpdd-orders'); ?>" class="button">
|
||||||
|
<?php _e('← Back to Orders', 'wp-digital-download'); ?>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<?php if ($order->status === 'completed') : ?>
|
||||||
|
<a href="mailto:<?php echo esc_attr($order->customer_email); ?>?subject=<?php echo urlencode('Your Order: ' . $order->order_number); ?>" class="button button-secondary">
|
||||||
|
<?php _e('Email Customer', 'wp-digital-download'); ?>
|
||||||
|
</a>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.order-details-container table.widefat td {
|
||||||
|
padding: 12px;
|
||||||
|
border-bottom: 1px solid #f0f0f1;
|
||||||
|
}
|
||||||
|
.order-details-container table.widefat td:first-child {
|
||||||
|
background-color: #f6f7f7;
|
||||||
|
}
|
||||||
|
.order-status {
|
||||||
|
font-weight: bold;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
</style>
|
124
assets/js/admin-payouts.js
Normal file
124
assets/js/admin-payouts.js
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
jQuery(document).ready(function($) {
|
||||||
|
// Handle individual payout processing
|
||||||
|
$('.wpdd-process-payout').on('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
var $button = $(this);
|
||||||
|
var payoutId = $button.data('payout-id');
|
||||||
|
var originalText = $button.text();
|
||||||
|
|
||||||
|
// Disable button and show loading
|
||||||
|
$button.prop('disabled', true).text('Processing...');
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: ajaxurl,
|
||||||
|
type: 'POST',
|
||||||
|
data: {
|
||||||
|
action: 'wpdd_process_payout',
|
||||||
|
payout_id: payoutId,
|
||||||
|
nonce: wpdd_admin_nonce
|
||||||
|
},
|
||||||
|
success: function(response) {
|
||||||
|
if (response.success) {
|
||||||
|
$button.closest('tr').find('.payout-status').text('Processing');
|
||||||
|
$button.text('Processed').removeClass('wpdd-process-payout').addClass('button-disabled');
|
||||||
|
} else {
|
||||||
|
alert('Error: ' + (response.data || 'Unknown error occurred'));
|
||||||
|
$button.prop('disabled', false).text(originalText);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function() {
|
||||||
|
alert('Ajax request failed');
|
||||||
|
$button.prop('disabled', false).text(originalText);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle payout request approval
|
||||||
|
$('.wpdd-approve-request').on('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
var $button = $(this);
|
||||||
|
var requestId = $button.data('request-id');
|
||||||
|
var originalText = $button.text();
|
||||||
|
|
||||||
|
// Disable button and show loading
|
||||||
|
$button.prop('disabled', true).text('Processing...');
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: ajaxurl,
|
||||||
|
type: 'POST',
|
||||||
|
data: {
|
||||||
|
action: 'wpdd_approve_payout_request',
|
||||||
|
request_id: requestId,
|
||||||
|
nonce: wpdd_admin_nonce
|
||||||
|
},
|
||||||
|
success: function(response) {
|
||||||
|
if (response.success) {
|
||||||
|
location.reload(); // Reload to show updated status
|
||||||
|
} else {
|
||||||
|
alert('Error: ' + (response.data || 'Unknown error occurred'));
|
||||||
|
$button.prop('disabled', false).text(originalText);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function() {
|
||||||
|
alert('Ajax request failed');
|
||||||
|
$button.prop('disabled', false).text(originalText);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle bulk payout processing
|
||||||
|
$('#wpdd-process-bulk-payouts').on('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
var checkedPayouts = $('.wpdd-payout-checkbox:checked');
|
||||||
|
|
||||||
|
if (checkedPayouts.length === 0) {
|
||||||
|
alert('Please select at least one payout to process.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!confirm('Are you sure you want to process ' + checkedPayouts.length + ' payout(s)?')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var $button = $(this);
|
||||||
|
var originalText = $button.text();
|
||||||
|
var payoutIds = [];
|
||||||
|
|
||||||
|
checkedPayouts.each(function() {
|
||||||
|
payoutIds.push($(this).val());
|
||||||
|
});
|
||||||
|
|
||||||
|
// Disable button and show loading
|
||||||
|
$button.prop('disabled', true).text('Processing...');
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: ajaxurl,
|
||||||
|
type: 'POST',
|
||||||
|
data: {
|
||||||
|
action: 'wpdd_process_bulk_payouts',
|
||||||
|
payout_ids: payoutIds,
|
||||||
|
nonce: wpdd_admin_nonce
|
||||||
|
},
|
||||||
|
success: function(response) {
|
||||||
|
if (response.success) {
|
||||||
|
location.reload(); // Reload to show updated status
|
||||||
|
} else {
|
||||||
|
alert('Error: ' + (response.data || 'Unknown error occurred'));
|
||||||
|
$button.prop('disabled', false).text(originalText);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function() {
|
||||||
|
alert('Ajax request failed');
|
||||||
|
$button.prop('disabled', false).text(originalText);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle select all checkboxes
|
||||||
|
$('#wpdd-select-all-payouts').on('change', function() {
|
||||||
|
$('.wpdd-payout-checkbox').prop('checked', $(this).prop('checked'));
|
||||||
|
});
|
||||||
|
});
|
420
docs/developer-integration-guide.md
Normal file
420
docs/developer-integration-guide.md
Normal file
@@ -0,0 +1,420 @@
|
|||||||
|
# WP Digital Download - Software Licensing Integration Guide
|
||||||
|
|
||||||
|
This guide shows developers how to integrate their WordPress plugins with the WP Digital Download licensing and update system.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### 1. Download the Integration Library
|
||||||
|
|
||||||
|
Download `wpdd-plugin-updater.php` from your product page and include it in your plugin.
|
||||||
|
|
||||||
|
### 2. Basic Integration
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Plugin Name: My Awesome Plugin
|
||||||
|
* Version: 1.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Include the WPDD updater library
|
||||||
|
require_once plugin_dir_path(__FILE__) . 'includes/wpdd-plugin-updater.php';
|
||||||
|
|
||||||
|
class My_Awesome_Plugin {
|
||||||
|
|
||||||
|
private $updater;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->init_updater();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function init_updater() {
|
||||||
|
// Only initialize updater in admin area
|
||||||
|
if (!is_admin()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$license_key = get_option('my_plugin_license_key', '');
|
||||||
|
|
||||||
|
$this->updater = new WPDD_Plugin_Updater(
|
||||||
|
__FILE__, // Main plugin file
|
||||||
|
$license_key, // License key from user
|
||||||
|
'https://your-store.com', // Your store URL
|
||||||
|
array(
|
||||||
|
'add_settings_page' => true // Add license settings page
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
new My_Awesome_Plugin();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Advanced Integration
|
||||||
|
|
||||||
|
### Custom License Settings Page
|
||||||
|
|
||||||
|
If you want to integrate license management into your existing settings:
|
||||||
|
|
||||||
|
```php
|
||||||
|
class My_Plugin_Settings {
|
||||||
|
|
||||||
|
private $updater;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->init_updater();
|
||||||
|
add_action('admin_menu', array($this, 'add_settings_page'));
|
||||||
|
add_action('admin_init', array($this, 'handle_license_actions'));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function init_updater() {
|
||||||
|
$license_key = get_option('my_plugin_license_key', '');
|
||||||
|
|
||||||
|
$this->updater = new WPDD_Plugin_Updater(
|
||||||
|
MY_PLUGIN_FILE,
|
||||||
|
$license_key,
|
||||||
|
'https://your-store.com',
|
||||||
|
array('add_settings_page' => false) // We'll handle settings ourselves
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle_license_actions() {
|
||||||
|
if (isset($_POST['activate_license'])) {
|
||||||
|
$license_key = sanitize_text_field($_POST['license_key']);
|
||||||
|
$result = $this->updater->activate_license($license_key);
|
||||||
|
|
||||||
|
if ($result['success']) {
|
||||||
|
update_option('my_plugin_license_key', $license_key);
|
||||||
|
add_settings_error('my_plugin', 'activated', 'License activated!', 'updated');
|
||||||
|
} else {
|
||||||
|
add_settings_error('my_plugin', 'error', $result['message'], 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_POST['deactivate_license'])) {
|
||||||
|
$result = $this->updater->deactivate_license();
|
||||||
|
if ($result['success']) {
|
||||||
|
delete_option('my_plugin_license_key');
|
||||||
|
add_settings_error('my_plugin', 'deactivated', 'License deactivated!', 'updated');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render_license_section() {
|
||||||
|
$license_key = get_option('my_plugin_license_key', '');
|
||||||
|
$is_valid = $this->updater->validate_license();
|
||||||
|
|
||||||
|
?>
|
||||||
|
<h3>License Settings</h3>
|
||||||
|
<table class="form-table">
|
||||||
|
<tr>
|
||||||
|
<th><label for="license_key">License Key</label></th>
|
||||||
|
<td>
|
||||||
|
<input type="text" id="license_key" name="license_key"
|
||||||
|
value="<?php echo esc_attr($license_key); ?>" class="regular-text" />
|
||||||
|
|
||||||
|
<?php if ($is_valid): ?>
|
||||||
|
<span style="color: green;">✓ Active</span>
|
||||||
|
<?php elseif (!empty($license_key)): ?>
|
||||||
|
<span style="color: red;">✗ Invalid</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<p class="description">
|
||||||
|
Enter your license key to receive automatic updates.
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<?php if (empty($license_key) || !$is_valid): ?>
|
||||||
|
<p>
|
||||||
|
<input type="submit" name="activate_license" class="button-primary" value="Activate License" />
|
||||||
|
</p>
|
||||||
|
<?php else: ?>
|
||||||
|
<p>
|
||||||
|
<input type="submit" name="deactivate_license" class="button-secondary" value="Deactivate License" />
|
||||||
|
</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Manual License Validation
|
||||||
|
|
||||||
|
For premium features or activation checks:
|
||||||
|
|
||||||
|
```php
|
||||||
|
class My_Premium_Feature {
|
||||||
|
|
||||||
|
private $updater;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$license_key = get_option('my_plugin_license_key', '');
|
||||||
|
|
||||||
|
$this->updater = new WPDD_Plugin_Updater(
|
||||||
|
MY_PLUGIN_FILE,
|
||||||
|
$license_key,
|
||||||
|
'https://your-store.com'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Only enable premium features if license is valid
|
||||||
|
if ($this->is_license_valid()) {
|
||||||
|
$this->enable_premium_features();
|
||||||
|
} else {
|
||||||
|
$this->show_license_notice();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function is_license_valid() {
|
||||||
|
return $this->updater->validate_license();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function enable_premium_features() {
|
||||||
|
// Add your premium functionality here
|
||||||
|
add_action('init', array($this, 'init_premium_features'));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function show_license_notice() {
|
||||||
|
add_action('admin_notices', function() {
|
||||||
|
?>
|
||||||
|
<div class="notice notice-warning">
|
||||||
|
<p>
|
||||||
|
<strong>My Awesome Plugin:</strong>
|
||||||
|
Please <a href="<?php echo admin_url('options-general.php?page=my-plugin-settings'); ?>">
|
||||||
|
activate your license</a> to access premium features and receive updates.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## API Reference
|
||||||
|
|
||||||
|
### WPDD_Plugin_Updater Class
|
||||||
|
|
||||||
|
#### Constructor Parameters
|
||||||
|
|
||||||
|
```php
|
||||||
|
new WPDD_Plugin_Updater($plugin_file, $license_key, $update_server, $args);
|
||||||
|
```
|
||||||
|
|
||||||
|
- **$plugin_file** (string) - Full path to your main plugin file
|
||||||
|
- **$license_key** (string) - The user's license key
|
||||||
|
- **$update_server** (string) - URL to your store (e.g., 'https://your-store.com')
|
||||||
|
- **$args** (array) - Optional arguments:
|
||||||
|
- `add_settings_page` (bool) - Auto-create license settings page (default: false)
|
||||||
|
|
||||||
|
#### Methods
|
||||||
|
|
||||||
|
##### validate_license()
|
||||||
|
Validates the current license with the server.
|
||||||
|
```php
|
||||||
|
$is_valid = $updater->validate_license();
|
||||||
|
// Returns: boolean
|
||||||
|
```
|
||||||
|
|
||||||
|
##### activate_license($license_key)
|
||||||
|
Activates a license key for the current site.
|
||||||
|
```php
|
||||||
|
$result = $updater->activate_license('XXXX-XXXX-XXXX-XXXX');
|
||||||
|
// Returns: array with 'success', 'message', and additional data
|
||||||
|
```
|
||||||
|
|
||||||
|
##### deactivate_license()
|
||||||
|
Deactivates the current license from this site.
|
||||||
|
```php
|
||||||
|
$result = $updater->deactivate_license();
|
||||||
|
// Returns: array with 'success' and 'message'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Repository Setup (For Store Owners)
|
||||||
|
|
||||||
|
### 1. Create Software Product
|
||||||
|
|
||||||
|
1. Go to your WordPress admin → Digital Products → Add New Product
|
||||||
|
2. Select "Software License" as product type
|
||||||
|
3. Fill in the software licensing fields:
|
||||||
|
- Git Repository URL
|
||||||
|
- License settings (max activations, duration)
|
||||||
|
- Version information
|
||||||
|
|
||||||
|
### 2. Configure Git Webhook
|
||||||
|
|
||||||
|
Add the generated webhook URL to your repository settings. The system receives webhook notifications FROM your Git platform when releases are published:
|
||||||
|
|
||||||
|
**Gitea:**
|
||||||
|
1. Go to Settings → Webhooks
|
||||||
|
2. Add webhook with the URL from your product page
|
||||||
|
3. Set Content-Type to `application/json`
|
||||||
|
4. Select "Release events" as the trigger
|
||||||
|
5. Ensure webhook is active
|
||||||
|
|
||||||
|
**GitHub:**
|
||||||
|
1. Go to Settings → Webhooks
|
||||||
|
2. Add webhook with the URL from your product page
|
||||||
|
3. Set Content-Type to `application/json`
|
||||||
|
4. Select "Releases" events (or "Just the push event" for tag-based releases)
|
||||||
|
|
||||||
|
**GitLab:**
|
||||||
|
1. Go to Settings → Webhooks
|
||||||
|
2. Add the webhook URL
|
||||||
|
3. Select "Tag push events" or "Releases events"
|
||||||
|
|
||||||
|
### 3. Release Process
|
||||||
|
|
||||||
|
**Option 1: Using Git Platform Releases (Recommended for Gitea/GitHub)**
|
||||||
|
1. Create a release through your Git platform's web interface:
|
||||||
|
- Navigate to Releases section
|
||||||
|
- Click "Create Release" or "New Release"
|
||||||
|
- Set tag name (e.g., `v1.2.0`)
|
||||||
|
- Add release notes in the description
|
||||||
|
- Publish the release
|
||||||
|
|
||||||
|
2. The webhook automatically receives the release notification and:
|
||||||
|
- Detects the new version from the release
|
||||||
|
- Clones the repository at the specific tag
|
||||||
|
- Creates distribution packages (removes dev files, creates ZIP)
|
||||||
|
- Stores version info and changelog in the database
|
||||||
|
- Makes update available to customers with active licenses
|
||||||
|
|
||||||
|
**Option 2: Using Git Tags (Alternative)**
|
||||||
|
1. Create and push a git tag:
|
||||||
|
```bash
|
||||||
|
git tag -a v1.2.0 -m "Version 1.2.0"
|
||||||
|
git push origin v1.2.0
|
||||||
|
```
|
||||||
|
|
||||||
|
2. The webhook receives the tag push notification and processes the release similarly
|
||||||
|
|
||||||
|
## API Endpoints
|
||||||
|
|
||||||
|
### License Validation
|
||||||
|
```
|
||||||
|
POST /wp-json/wpdd/v1/validate-license
|
||||||
|
Body: {
|
||||||
|
"license_key": "XXXX-XXXX-XXXX-XXXX",
|
||||||
|
"product_slug": "my-plugin",
|
||||||
|
"site_url": "https://example.com"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Update Check
|
||||||
|
```
|
||||||
|
GET /wp-json/wpdd/v1/check-update/my-plugin?license_key=XXXX&version=1.0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
### Download Update
|
||||||
|
```
|
||||||
|
GET /wp-json/wpdd/v1/download-update/my-plugin?license_key=XXXX
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing Your Integration
|
||||||
|
|
||||||
|
### 1. Local Testing
|
||||||
|
|
||||||
|
```php
|
||||||
|
// Add this to your plugin for testing
|
||||||
|
if (defined('WP_DEBUG') && WP_DEBUG) {
|
||||||
|
add_action('admin_notices', function() {
|
||||||
|
$license_key = get_option('my_plugin_license_key', '');
|
||||||
|
$updater = new WPDD_Plugin_Updater(__FILE__, $license_key, 'https://your-store.com');
|
||||||
|
$is_valid = $updater->validate_license();
|
||||||
|
|
||||||
|
echo '<div class="notice notice-info">';
|
||||||
|
echo '<p>License Status: ' . ($is_valid ? 'Valid' : 'Invalid') . '</p>';
|
||||||
|
echo '</div>';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Force Update Check
|
||||||
|
|
||||||
|
```php
|
||||||
|
// Add this temporarily to force update check
|
||||||
|
add_action('admin_init', function() {
|
||||||
|
if (isset($_GET['force_update_check'])) {
|
||||||
|
delete_transient('wpdd_update_my-plugin');
|
||||||
|
delete_site_transient('update_plugins');
|
||||||
|
wp_redirect(admin_url('plugins.php'));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Then visit: `wp-admin/plugins.php?force_update_check=1`
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
### 1. Error Handling
|
||||||
|
Always handle API failures gracefully:
|
||||||
|
|
||||||
|
```php
|
||||||
|
$result = $updater->validate_license();
|
||||||
|
if ($result === false) {
|
||||||
|
// Network error or server down - allow functionality to continue
|
||||||
|
// but maybe show a notice
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Caching
|
||||||
|
The updater automatically caches responses. Don't call validation on every page load:
|
||||||
|
|
||||||
|
```php
|
||||||
|
// Good - check once per day
|
||||||
|
$last_check = get_option('my_plugin_license_check', 0);
|
||||||
|
if (time() - $last_check > DAY_IN_SECONDS) {
|
||||||
|
$is_valid = $updater->validate_license();
|
||||||
|
update_option('my_plugin_license_check', time());
|
||||||
|
update_option('my_plugin_license_valid', $is_valid);
|
||||||
|
} else {
|
||||||
|
$is_valid = get_option('my_plugin_license_valid', false);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Graceful Degradation
|
||||||
|
Design your plugin to work without a valid license, but with reduced functionality:
|
||||||
|
|
||||||
|
```php
|
||||||
|
if ($this->is_license_valid()) {
|
||||||
|
// Full functionality
|
||||||
|
$this->enable_all_features();
|
||||||
|
} else {
|
||||||
|
// Basic functionality only
|
||||||
|
$this->enable_basic_features();
|
||||||
|
$this->show_upgrade_notice();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
1. **Updates not showing:** Check that the plugin slug matches the product slug in your store
|
||||||
|
2. **License validation fails:** Ensure the update server URL is correct and accessible
|
||||||
|
3. **Download fails:** Verify the license is activated and not expired
|
||||||
|
|
||||||
|
### Debug Mode
|
||||||
|
|
||||||
|
Enable WordPress debug logging and check for WPDD Updater messages:
|
||||||
|
|
||||||
|
```php
|
||||||
|
// wp-config.php
|
||||||
|
define('WP_DEBUG', true);
|
||||||
|
define('WP_DEBUG_LOG', true);
|
||||||
|
```
|
||||||
|
|
||||||
|
Check `/wp-content/debug.log` for error messages.
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
For integration support:
|
||||||
|
- Check the troubleshooting section above
|
||||||
|
- Enable debug logging and check for error messages
|
||||||
|
- Contact support with your store URL and plugin details
|
||||||
|
|
||||||
|
## Example Files
|
||||||
|
|
||||||
|
Complete example plugins are available in the `/examples/` directory of this package.
|
785
includes/class-wpdd-api.php
Normal file
785
includes/class-wpdd-api.php
Normal file
@@ -0,0 +1,785 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
if (!defined('ABSPATH')) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
class WPDD_API {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the API endpoints
|
||||||
|
*/
|
||||||
|
public static function init() {
|
||||||
|
add_action('rest_api_init', array(__CLASS__, 'register_routes'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register REST API routes
|
||||||
|
*/
|
||||||
|
public static function register_routes() {
|
||||||
|
// License validation
|
||||||
|
register_rest_route('wpdd/v1', '/validate-license', array(
|
||||||
|
'methods' => 'POST',
|
||||||
|
'callback' => array(__CLASS__, 'validate_license'),
|
||||||
|
'permission_callback' => '__return_true',
|
||||||
|
'args' => array(
|
||||||
|
'license_key' => array(
|
||||||
|
'required' => true,
|
||||||
|
'sanitize_callback' => 'sanitize_text_field'
|
||||||
|
),
|
||||||
|
'product_slug' => array(
|
||||||
|
'required' => false,
|
||||||
|
'sanitize_callback' => 'sanitize_text_field'
|
||||||
|
),
|
||||||
|
'site_url' => array(
|
||||||
|
'required' => false,
|
||||||
|
'sanitize_callback' => 'esc_url_raw'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
));
|
||||||
|
|
||||||
|
// License activation
|
||||||
|
register_rest_route('wpdd/v1', '/activate-license', array(
|
||||||
|
'methods' => 'POST',
|
||||||
|
'callback' => array(__CLASS__, 'activate_license'),
|
||||||
|
'permission_callback' => '__return_true',
|
||||||
|
'args' => array(
|
||||||
|
'license_key' => array(
|
||||||
|
'required' => true,
|
||||||
|
'sanitize_callback' => 'sanitize_text_field'
|
||||||
|
),
|
||||||
|
'site_url' => array(
|
||||||
|
'required' => true,
|
||||||
|
'sanitize_callback' => 'esc_url_raw'
|
||||||
|
),
|
||||||
|
'site_name' => array(
|
||||||
|
'required' => false,
|
||||||
|
'sanitize_callback' => 'sanitize_text_field'
|
||||||
|
),
|
||||||
|
'wp_version' => array(
|
||||||
|
'required' => false,
|
||||||
|
'sanitize_callback' => 'sanitize_text_field'
|
||||||
|
),
|
||||||
|
'php_version' => array(
|
||||||
|
'required' => false,
|
||||||
|
'sanitize_callback' => 'sanitize_text_field'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
));
|
||||||
|
|
||||||
|
// License deactivation
|
||||||
|
register_rest_route('wpdd/v1', '/deactivate-license', array(
|
||||||
|
'methods' => 'POST',
|
||||||
|
'callback' => array(__CLASS__, 'deactivate_license'),
|
||||||
|
'permission_callback' => '__return_true',
|
||||||
|
'args' => array(
|
||||||
|
'license_key' => array(
|
||||||
|
'required' => true,
|
||||||
|
'sanitize_callback' => 'sanitize_text_field'
|
||||||
|
),
|
||||||
|
'site_url' => array(
|
||||||
|
'required' => true,
|
||||||
|
'sanitize_callback' => 'esc_url_raw'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
));
|
||||||
|
|
||||||
|
// Check for updates
|
||||||
|
register_rest_route('wpdd/v1', '/check-update/(?P<product_slug>[a-zA-Z0-9-]+)', array(
|
||||||
|
'methods' => 'GET',
|
||||||
|
'callback' => array(__CLASS__, 'check_update'),
|
||||||
|
'permission_callback' => '__return_true',
|
||||||
|
'args' => array(
|
||||||
|
'product_slug' => array(
|
||||||
|
'required' => true,
|
||||||
|
'sanitize_callback' => 'sanitize_text_field'
|
||||||
|
),
|
||||||
|
'license_key' => array(
|
||||||
|
'required' => true,
|
||||||
|
'sanitize_callback' => 'sanitize_text_field'
|
||||||
|
),
|
||||||
|
'version' => array(
|
||||||
|
'required' => true,
|
||||||
|
'sanitize_callback' => 'sanitize_text_field'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
));
|
||||||
|
|
||||||
|
// Download update
|
||||||
|
register_rest_route('wpdd/v1', '/download-update/(?P<product_slug>[a-zA-Z0-9-]+)', array(
|
||||||
|
'methods' => 'GET',
|
||||||
|
'callback' => array(__CLASS__, 'download_update'),
|
||||||
|
'permission_callback' => '__return_true',
|
||||||
|
'args' => array(
|
||||||
|
'product_slug' => array(
|
||||||
|
'required' => true,
|
||||||
|
'sanitize_callback' => 'sanitize_text_field'
|
||||||
|
),
|
||||||
|
'license_key' => array(
|
||||||
|
'required' => true,
|
||||||
|
'sanitize_callback' => 'sanitize_text_field'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
));
|
||||||
|
|
||||||
|
// Webhook endpoint with secure passcode
|
||||||
|
register_rest_route('wpdd/v1', '/webhook/(?P<product_id>\d+)/(?P<passcode>[a-zA-Z0-9]+)', array(
|
||||||
|
'methods' => 'POST',
|
||||||
|
'callback' => array(__CLASS__, 'handle_webhook'),
|
||||||
|
'permission_callback' => '__return_true',
|
||||||
|
'args' => array(
|
||||||
|
'product_id' => array(
|
||||||
|
'required' => true,
|
||||||
|
'sanitize_callback' => 'absint'
|
||||||
|
),
|
||||||
|
'passcode' => array(
|
||||||
|
'required' => true,
|
||||||
|
'sanitize_callback' => 'sanitize_text_field'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate license endpoint
|
||||||
|
*/
|
||||||
|
public static function validate_license($request) {
|
||||||
|
$license_key = $request->get_param('license_key');
|
||||||
|
$product_slug = $request->get_param('product_slug');
|
||||||
|
$site_url = $request->get_param('site_url');
|
||||||
|
|
||||||
|
if (!class_exists('WPDD_License_Manager')) {
|
||||||
|
require_once WPDD_PLUGIN_PATH . 'includes/class-wpdd-license-manager.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = WPDD_License_Manager::validate_license($license_key, $product_slug, $site_url);
|
||||||
|
|
||||||
|
if ($result['valid']) {
|
||||||
|
return new WP_REST_Response(array(
|
||||||
|
'success' => true,
|
||||||
|
'message' => $result['message'],
|
||||||
|
'license' => array(
|
||||||
|
'status' => $result['license']->status,
|
||||||
|
'expires_at' => $result['license']->expires_at,
|
||||||
|
'activations' => $result['license']->activations_count,
|
||||||
|
'max_activations' => $result['license']->max_activations
|
||||||
|
)
|
||||||
|
), 200);
|
||||||
|
} else {
|
||||||
|
return new WP_REST_Response(array(
|
||||||
|
'success' => false,
|
||||||
|
'error' => $result['error'],
|
||||||
|
'message' => $result['message']
|
||||||
|
), 400);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Activate license endpoint
|
||||||
|
*/
|
||||||
|
public static function activate_license($request) {
|
||||||
|
$license_key = $request->get_param('license_key');
|
||||||
|
$site_url = $request->get_param('site_url');
|
||||||
|
$site_name = $request->get_param('site_name');
|
||||||
|
$wp_version = $request->get_param('wp_version');
|
||||||
|
$php_version = $request->get_param('php_version');
|
||||||
|
|
||||||
|
if (!class_exists('WPDD_License_Manager')) {
|
||||||
|
require_once WPDD_PLUGIN_PATH . 'includes/class-wpdd-license-manager.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = WPDD_License_Manager::activate_license($license_key, $site_url, $site_name, $wp_version, $php_version);
|
||||||
|
|
||||||
|
if ($result['success']) {
|
||||||
|
return new WP_REST_Response($result, 200);
|
||||||
|
} else {
|
||||||
|
return new WP_REST_Response($result, 400);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deactivate license endpoint
|
||||||
|
*/
|
||||||
|
public static function deactivate_license($request) {
|
||||||
|
$license_key = $request->get_param('license_key');
|
||||||
|
$site_url = $request->get_param('site_url');
|
||||||
|
|
||||||
|
if (!class_exists('WPDD_License_Manager')) {
|
||||||
|
require_once WPDD_PLUGIN_PATH . 'includes/class-wpdd-license-manager.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = WPDD_License_Manager::deactivate_license($license_key, $site_url);
|
||||||
|
|
||||||
|
if ($result['success']) {
|
||||||
|
return new WP_REST_Response($result, 200);
|
||||||
|
} else {
|
||||||
|
return new WP_REST_Response($result, 400);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check for updates endpoint
|
||||||
|
*/
|
||||||
|
public static function check_update($request) {
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
$product_slug = $request->get_param('product_slug');
|
||||||
|
$license_key = $request->get_param('license_key');
|
||||||
|
$current_version = $request->get_param('version');
|
||||||
|
|
||||||
|
// Validate license first
|
||||||
|
if (!class_exists('WPDD_License_Manager')) {
|
||||||
|
require_once WPDD_PLUGIN_PATH . 'includes/class-wpdd-license-manager.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
$validation = WPDD_License_Manager::validate_license($license_key, $product_slug);
|
||||||
|
if (!$validation['valid']) {
|
||||||
|
return new WP_REST_Response(array(
|
||||||
|
'success' => false,
|
||||||
|
'error' => $validation['error'],
|
||||||
|
'message' => $validation['message']
|
||||||
|
), 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get product by slug
|
||||||
|
$product = get_page_by_path($product_slug, OBJECT, 'wpdd_product');
|
||||||
|
if (!$product) {
|
||||||
|
return new WP_REST_Response(array(
|
||||||
|
'success' => false,
|
||||||
|
'message' => __('Product not found.', 'wp-digital-download')
|
||||||
|
), 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get latest version
|
||||||
|
$latest_version = $wpdb->get_row($wpdb->prepare(
|
||||||
|
"SELECT * FROM {$wpdb->prefix}wpdd_software_versions
|
||||||
|
WHERE product_id = %d
|
||||||
|
ORDER BY release_date DESC
|
||||||
|
LIMIT 1",
|
||||||
|
$product->ID
|
||||||
|
));
|
||||||
|
|
||||||
|
if (!$latest_version) {
|
||||||
|
return new WP_REST_Response(array(
|
||||||
|
'success' => true,
|
||||||
|
'update_available' => false,
|
||||||
|
'message' => __('No updates available.', 'wp-digital-download')
|
||||||
|
), 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare versions
|
||||||
|
if (version_compare($latest_version->version, $current_version, '>')) {
|
||||||
|
// Update available
|
||||||
|
$update_data = array(
|
||||||
|
'success' => true,
|
||||||
|
'update_available' => true,
|
||||||
|
'version' => $latest_version->version,
|
||||||
|
'download_url' => home_url("/wp-json/wpdd/v1/download-update/{$product_slug}?license_key={$license_key}"),
|
||||||
|
'package' => home_url("/wp-json/wpdd/v1/download-update/{$product_slug}?license_key={$license_key}"),
|
||||||
|
'url' => get_permalink($product->ID),
|
||||||
|
'tested' => $latest_version->tested_wp_version ?: get_bloginfo('version'),
|
||||||
|
'requires' => $latest_version->min_wp_version ?: '5.0',
|
||||||
|
'requires_php' => $latest_version->min_php_version ?: '7.0',
|
||||||
|
'new_version' => $latest_version->version,
|
||||||
|
'slug' => $product_slug,
|
||||||
|
'plugin' => $product_slug . '/' . $product_slug . '.php', // Adjust based on your naming convention
|
||||||
|
'changelog' => $latest_version->changelog,
|
||||||
|
'release_notes' => $latest_version->release_notes
|
||||||
|
);
|
||||||
|
|
||||||
|
return new WP_REST_Response($update_data, 200);
|
||||||
|
} else {
|
||||||
|
return new WP_REST_Response(array(
|
||||||
|
'success' => true,
|
||||||
|
'update_available' => false,
|
||||||
|
'message' => __('You have the latest version.', 'wp-digital-download')
|
||||||
|
), 200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Download update endpoint
|
||||||
|
*/
|
||||||
|
public static function download_update($request) {
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
$product_slug = $request->get_param('product_slug');
|
||||||
|
$license_key = $request->get_param('license_key');
|
||||||
|
|
||||||
|
// Validate license
|
||||||
|
if (!class_exists('WPDD_License_Manager')) {
|
||||||
|
require_once WPDD_PLUGIN_PATH . 'includes/class-wpdd-license-manager.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
$validation = WPDD_License_Manager::validate_license($license_key, $product_slug);
|
||||||
|
if (!$validation['valid']) {
|
||||||
|
return new WP_REST_Response(array(
|
||||||
|
'success' => false,
|
||||||
|
'error' => $validation['error'],
|
||||||
|
'message' => $validation['message']
|
||||||
|
), 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get product
|
||||||
|
$product = get_page_by_path($product_slug, OBJECT, 'wpdd_product');
|
||||||
|
if (!$product) {
|
||||||
|
return new WP_REST_Response(array(
|
||||||
|
'success' => false,
|
||||||
|
'message' => __('Product not found.', 'wp-digital-download')
|
||||||
|
), 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get latest version
|
||||||
|
$latest_version = $wpdb->get_row($wpdb->prepare(
|
||||||
|
"SELECT * FROM {$wpdb->prefix}wpdd_software_versions
|
||||||
|
WHERE product_id = %d
|
||||||
|
ORDER BY release_date DESC
|
||||||
|
LIMIT 1",
|
||||||
|
$product->ID
|
||||||
|
));
|
||||||
|
|
||||||
|
if (!$latest_version || !$latest_version->package_url) {
|
||||||
|
return new WP_REST_Response(array(
|
||||||
|
'success' => false,
|
||||||
|
'message' => __('Update package not available.', 'wp-digital-download')
|
||||||
|
), 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get package file path
|
||||||
|
$upload_dir = wp_upload_dir();
|
||||||
|
$package_path = str_replace($upload_dir['baseurl'], $upload_dir['basedir'], $latest_version->package_url);
|
||||||
|
|
||||||
|
if (!file_exists($package_path)) {
|
||||||
|
return new WP_REST_Response(array(
|
||||||
|
'success' => false,
|
||||||
|
'message' => __('Update package file not found.', 'wp-digital-download')
|
||||||
|
), 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log download
|
||||||
|
$wpdb->insert(
|
||||||
|
$wpdb->prefix . 'wpdd_downloads',
|
||||||
|
array(
|
||||||
|
'order_id' => $validation['license']->order_id,
|
||||||
|
'product_id' => $product->ID,
|
||||||
|
'customer_id' => $validation['license']->customer_id,
|
||||||
|
'file_id' => $latest_version->version,
|
||||||
|
'download_date' => current_time('mysql'),
|
||||||
|
'ip_address' => $_SERVER['REMOTE_ADDR'],
|
||||||
|
'user_agent' => $_SERVER['HTTP_USER_AGENT']
|
||||||
|
),
|
||||||
|
array('%d', '%d', '%d', '%s', '%s', '%s', '%s')
|
||||||
|
);
|
||||||
|
|
||||||
|
// Serve file
|
||||||
|
$filename = basename($package_path);
|
||||||
|
header('Content-Type: application/zip');
|
||||||
|
header('Content-Disposition: attachment; filename="' . $filename . '"');
|
||||||
|
header('Content-Length: ' . filesize($package_path));
|
||||||
|
header('Pragma: no-cache');
|
||||||
|
header('Expires: 0');
|
||||||
|
readfile($package_path);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle Git webhook for new releases (receives notifications FROM Git platforms like Gitea)
|
||||||
|
*/
|
||||||
|
public static function handle_webhook($request) {
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
$product_id = $request->get_param('product_id');
|
||||||
|
$passcode = $request->get_param('passcode');
|
||||||
|
|
||||||
|
// Validate passcode
|
||||||
|
$stored_passcode = get_post_meta($product_id, '_wpdd_webhook_passcode', true);
|
||||||
|
if (!$stored_passcode || $stored_passcode !== $passcode) {
|
||||||
|
return new WP_REST_Response(array(
|
||||||
|
'success' => false,
|
||||||
|
'message' => __('Invalid webhook passcode.', 'wp-digital-download')
|
||||||
|
), 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get payload from Git platform (Gitea, GitHub, GitLab, etc.)
|
||||||
|
$payload = $request->get_body();
|
||||||
|
$data = json_decode($payload, true);
|
||||||
|
|
||||||
|
if (!$data) {
|
||||||
|
return new WP_REST_Response(array(
|
||||||
|
'success' => false,
|
||||||
|
'message' => __('Invalid JSON payload.', 'wp-digital-download')
|
||||||
|
), 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine event type based on payload structure
|
||||||
|
$event_type = 'unknown';
|
||||||
|
$is_release = false;
|
||||||
|
|
||||||
|
// Gitea release webhook
|
||||||
|
if (isset($data['action']) && isset($data['release'])) {
|
||||||
|
$event_type = 'release';
|
||||||
|
$is_release = ($data['action'] === 'published' || $data['action'] === 'created');
|
||||||
|
}
|
||||||
|
// GitHub/GitLab push with tags
|
||||||
|
elseif (isset($data['ref']) && strpos($data['ref'], 'refs/tags/') === 0) {
|
||||||
|
$event_type = 'tag_push';
|
||||||
|
$is_release = true;
|
||||||
|
}
|
||||||
|
// GitHub release webhook
|
||||||
|
elseif (isset($data['action']) && isset($data['release']) && $data['action'] === 'published') {
|
||||||
|
$event_type = 'github_release';
|
||||||
|
$is_release = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log webhook event
|
||||||
|
$wpdb->insert(
|
||||||
|
$wpdb->prefix . 'wpdd_webhook_events',
|
||||||
|
array(
|
||||||
|
'product_id' => $product_id,
|
||||||
|
'event_type' => $event_type,
|
||||||
|
'payload' => $payload,
|
||||||
|
'processed' => 'pending',
|
||||||
|
'received_at' => current_time('mysql')
|
||||||
|
),
|
||||||
|
array('%d', '%s', '%s', '%s', '%s')
|
||||||
|
);
|
||||||
|
|
||||||
|
$event_id = $wpdb->insert_id;
|
||||||
|
|
||||||
|
if (!$is_release) {
|
||||||
|
// Mark as ignored - not a release event
|
||||||
|
$wpdb->update(
|
||||||
|
$wpdb->prefix . 'wpdd_webhook_events',
|
||||||
|
array(
|
||||||
|
'processed' => 'ignored',
|
||||||
|
'processed_at' => current_time('mysql'),
|
||||||
|
'error_message' => 'Not a release event'
|
||||||
|
),
|
||||||
|
array('id' => $event_id),
|
||||||
|
array('%s', '%s', '%s'),
|
||||||
|
array('%d')
|
||||||
|
);
|
||||||
|
|
||||||
|
return new WP_REST_Response(array(
|
||||||
|
'success' => true,
|
||||||
|
'message' => __('Webhook received but not a release event.', 'wp-digital-download')
|
||||||
|
), 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract version information based on platform
|
||||||
|
$version = '';
|
||||||
|
$tag = '';
|
||||||
|
|
||||||
|
if ($event_type === 'release' || $event_type === 'github_release') {
|
||||||
|
// Gitea or GitHub release
|
||||||
|
$tag = $data['release']['tag_name'] ?? '';
|
||||||
|
$version = ltrim($tag, 'v');
|
||||||
|
} elseif ($event_type === 'tag_push') {
|
||||||
|
// Git tag push
|
||||||
|
$tag = str_replace('refs/tags/', '', $data['ref']);
|
||||||
|
$version = ltrim($tag, 'v');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($version)) {
|
||||||
|
// Mark as error
|
||||||
|
$wpdb->update(
|
||||||
|
$wpdb->prefix . 'wpdd_webhook_events',
|
||||||
|
array(
|
||||||
|
'processed' => 'error',
|
||||||
|
'processed_at' => current_time('mysql'),
|
||||||
|
'error_message' => 'Could not extract version from payload'
|
||||||
|
),
|
||||||
|
array('id' => $event_id),
|
||||||
|
array('%s', '%s', '%s'),
|
||||||
|
array('%d')
|
||||||
|
);
|
||||||
|
|
||||||
|
return new WP_REST_Response(array(
|
||||||
|
'success' => false,
|
||||||
|
'message' => __('Could not extract version from webhook payload.', 'wp-digital-download')
|
||||||
|
), 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this version already exists
|
||||||
|
$existing = $wpdb->get_var($wpdb->prepare(
|
||||||
|
"SELECT id FROM {$wpdb->prefix}wpdd_software_versions
|
||||||
|
WHERE product_id = %d AND version = %s",
|
||||||
|
$product_id,
|
||||||
|
$version
|
||||||
|
));
|
||||||
|
|
||||||
|
if (!$existing) {
|
||||||
|
// Process new release
|
||||||
|
$success = self::process_new_release($product_id, $version, $tag, $data);
|
||||||
|
|
||||||
|
if ($success) {
|
||||||
|
// Mark webhook as processed
|
||||||
|
$wpdb->update(
|
||||||
|
$wpdb->prefix . 'wpdd_webhook_events',
|
||||||
|
array(
|
||||||
|
'processed' => 'completed',
|
||||||
|
'processed_at' => current_time('mysql')
|
||||||
|
),
|
||||||
|
array('id' => $event_id),
|
||||||
|
array('%s', '%s'),
|
||||||
|
array('%d')
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Mark as error
|
||||||
|
$wpdb->update(
|
||||||
|
$wpdb->prefix . 'wpdd_webhook_events',
|
||||||
|
array(
|
||||||
|
'processed' => 'error',
|
||||||
|
'processed_at' => current_time('mysql'),
|
||||||
|
'error_message' => 'Failed to process release'
|
||||||
|
),
|
||||||
|
array('id' => $event_id),
|
||||||
|
array('%s', '%s', '%s'),
|
||||||
|
array('%d')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Mark as duplicate
|
||||||
|
$wpdb->update(
|
||||||
|
$wpdb->prefix . 'wpdd_webhook_events',
|
||||||
|
array(
|
||||||
|
'processed' => 'duplicate',
|
||||||
|
'processed_at' => current_time('mysql'),
|
||||||
|
'error_message' => 'Version already exists'
|
||||||
|
),
|
||||||
|
array('id' => $event_id),
|
||||||
|
array('%s', '%s', '%s'),
|
||||||
|
array('%d')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new WP_REST_Response(array(
|
||||||
|
'success' => true,
|
||||||
|
'message' => __('Webhook received and processed.', 'wp-digital-download')
|
||||||
|
), 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process new release from webhook (receives data FROM Git platforms like Gitea)
|
||||||
|
*/
|
||||||
|
private static function process_new_release($product_id, $version, $tag, $webhook_data) {
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
// Get Git repository settings
|
||||||
|
$git_url = get_post_meta($product_id, '_wpdd_git_repository', true);
|
||||||
|
$git_username = get_post_meta($product_id, '_wpdd_git_username', true);
|
||||||
|
$git_token = get_post_meta($product_id, '_wpdd_git_token', true);
|
||||||
|
|
||||||
|
if (!$git_url) {
|
||||||
|
error_log('WPDD: No Git URL configured for product ' . $product_id);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build package from Git repository at the specific tag
|
||||||
|
$package_url = self::build_package_from_git($product_id, $git_url, $tag, $git_username, $git_token);
|
||||||
|
|
||||||
|
if (!$package_url) {
|
||||||
|
error_log('WPDD: Failed to build package for product ' . $product_id . ' version ' . $version);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract changelog based on webhook source
|
||||||
|
$changelog = '';
|
||||||
|
$git_commit = null;
|
||||||
|
|
||||||
|
// Gitea/GitHub release with description
|
||||||
|
if (isset($webhook_data['release'])) {
|
||||||
|
$changelog = $webhook_data['release']['body'] ?? $webhook_data['release']['note'] ?? '';
|
||||||
|
$git_commit = $webhook_data['release']['target_commitish'] ?? null;
|
||||||
|
}
|
||||||
|
// Git push webhook - use commit messages
|
||||||
|
elseif (isset($webhook_data['commits']) && is_array($webhook_data['commits'])) {
|
||||||
|
$messages = array();
|
||||||
|
foreach ($webhook_data['commits'] as $commit) {
|
||||||
|
if (isset($commit['message'])) {
|
||||||
|
$messages[] = '- ' . $commit['message'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$changelog = implode("\n", $messages);
|
||||||
|
$git_commit = $webhook_data['after'] ?? $webhook_data['head_commit']['id'] ?? null;
|
||||||
|
}
|
||||||
|
// Fallback - try to get from head commit
|
||||||
|
elseif (isset($webhook_data['head_commit']['message'])) {
|
||||||
|
$changelog = '- ' . $webhook_data['head_commit']['message'];
|
||||||
|
$git_commit = $webhook_data['head_commit']['id'] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert new version
|
||||||
|
$result = $wpdb->insert(
|
||||||
|
$wpdb->prefix . 'wpdd_software_versions',
|
||||||
|
array(
|
||||||
|
'product_id' => $product_id,
|
||||||
|
'version' => $version,
|
||||||
|
'changelog' => $changelog,
|
||||||
|
'package_url' => $package_url,
|
||||||
|
'git_tag' => $tag,
|
||||||
|
'git_commit' => $git_commit,
|
||||||
|
'release_date' => current_time('mysql')
|
||||||
|
),
|
||||||
|
array('%d', '%s', '%s', '%s', '%s', '%s', '%s')
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($result === false) {
|
||||||
|
error_log('WPDD: Failed to insert version record for product ' . $product_id . ' version ' . $version);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update product version meta
|
||||||
|
update_post_meta($product_id, '_wpdd_current_version', $version);
|
||||||
|
|
||||||
|
// Notify customers about update (optional)
|
||||||
|
self::notify_customers_about_update($product_id, $version);
|
||||||
|
|
||||||
|
error_log('WPDD: Successfully processed new release for product ' . $product_id . ' version ' . $version);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build package from Git repository at specific tag
|
||||||
|
*/
|
||||||
|
private static function build_package_from_git($product_id, $git_url, $tag, $username = null, $token = null) {
|
||||||
|
$upload_dir = wp_upload_dir();
|
||||||
|
$package_dir = trailingslashit($upload_dir['basedir']) . 'wpdd-packages/' . $product_id;
|
||||||
|
|
||||||
|
if (!file_exists($package_dir)) {
|
||||||
|
wp_mkdir_p($package_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
$package_filename = sanitize_file_name("package-{$tag}.zip");
|
||||||
|
$package_path = trailingslashit($package_dir) . $package_filename;
|
||||||
|
$package_url = trailingslashit($upload_dir['baseurl']) . 'wpdd-packages/' . $product_id . '/' . $package_filename;
|
||||||
|
|
||||||
|
// Skip if package already exists
|
||||||
|
if (file_exists($package_path)) {
|
||||||
|
return $package_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create temporary directory for cloning
|
||||||
|
$temp_dir = trailingslashit(sys_get_temp_dir()) . 'wpdd-build-' . $product_id . '-' . uniqid();
|
||||||
|
|
||||||
|
// Build authentication URL if credentials provided
|
||||||
|
$auth_url = $git_url;
|
||||||
|
if ($username && $token) {
|
||||||
|
$parsed_url = parse_url($git_url);
|
||||||
|
if ($parsed_url) {
|
||||||
|
$auth_url = $parsed_url['scheme'] . '://' . urlencode($username) . ':' . urlencode($token) . '@' . $parsed_url['host'];
|
||||||
|
if (isset($parsed_url['port'])) {
|
||||||
|
$auth_url .= ':' . $parsed_url['port'];
|
||||||
|
}
|
||||||
|
$auth_url .= $parsed_url['path'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clone repository at specific tag
|
||||||
|
$clone_cmd = sprintf(
|
||||||
|
'git clone --depth 1 --branch %s %s %s 2>&1',
|
||||||
|
escapeshellarg($tag),
|
||||||
|
escapeshellarg($auth_url),
|
||||||
|
escapeshellarg($temp_dir)
|
||||||
|
);
|
||||||
|
|
||||||
|
$output = array();
|
||||||
|
$return_code = 0;
|
||||||
|
exec($clone_cmd, $output, $return_code);
|
||||||
|
|
||||||
|
if ($return_code !== 0) {
|
||||||
|
error_log('WPDD: Git clone failed for ' . $git_url . ' tag ' . $tag . ': ' . implode(' ', $output));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove .git directory and other development files
|
||||||
|
$cleanup_files = array('.git', '.gitignore', '.gitattributes', 'tests', 'test', '.phpunit.xml', 'composer.json', 'package.json');
|
||||||
|
foreach ($cleanup_files as $cleanup_file) {
|
||||||
|
$cleanup_path = trailingslashit($temp_dir) . $cleanup_file;
|
||||||
|
if (file_exists($cleanup_path)) {
|
||||||
|
if (is_dir($cleanup_path)) {
|
||||||
|
self::remove_directory($cleanup_path);
|
||||||
|
} else {
|
||||||
|
unlink($cleanup_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create ZIP package
|
||||||
|
if (class_exists('ZipArchive')) {
|
||||||
|
$zip = new ZipArchive();
|
||||||
|
if ($zip->open($package_path, ZipArchive::CREATE | ZipArchive::OVERWRITE) === TRUE) {
|
||||||
|
self::add_directory_to_zip($zip, $temp_dir, '');
|
||||||
|
$zip->close();
|
||||||
|
|
||||||
|
// Clean up temporary directory
|
||||||
|
self::remove_directory($temp_dir);
|
||||||
|
|
||||||
|
return $package_url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: tar command if ZipArchive not available
|
||||||
|
$tar_cmd = sprintf(
|
||||||
|
'cd %s && tar -czf %s . 2>&1',
|
||||||
|
escapeshellarg($temp_dir),
|
||||||
|
escapeshellarg($package_path . '.tar.gz')
|
||||||
|
);
|
||||||
|
|
||||||
|
exec($tar_cmd, $output, $return_code);
|
||||||
|
self::remove_directory($temp_dir);
|
||||||
|
|
||||||
|
if ($return_code === 0 && file_exists($package_path . '.tar.gz')) {
|
||||||
|
return $package_url . '.tar.gz';
|
||||||
|
}
|
||||||
|
|
||||||
|
error_log('WPDD: Failed to create package for ' . $git_url . ' tag ' . $tag);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively add directory to ZIP archive
|
||||||
|
*/
|
||||||
|
private static function add_directory_to_zip($zip, $dir, $base_path) {
|
||||||
|
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir));
|
||||||
|
foreach ($iterator as $file) {
|
||||||
|
if ($file->isFile()) {
|
||||||
|
$file_path = $file->getPathname();
|
||||||
|
$relative_path = $base_path . substr($file_path, strlen($dir) + 1);
|
||||||
|
$zip->addFile($file_path, $relative_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively remove directory
|
||||||
|
*/
|
||||||
|
private static function remove_directory($dir) {
|
||||||
|
if (!is_dir($dir)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$iterator = new RecursiveIteratorIterator(
|
||||||
|
new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS),
|
||||||
|
RecursiveIteratorIterator::CHILD_FIRST
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($iterator as $file) {
|
||||||
|
if ($file->isDir()) {
|
||||||
|
rmdir($file->getPathname());
|
||||||
|
} else {
|
||||||
|
unlink($file->getPathname());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rmdir($dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify customers about new update
|
||||||
|
*/
|
||||||
|
private static function notify_customers_about_update($product_id, $version) {
|
||||||
|
// Optional: Send email notifications to customers with active licenses
|
||||||
|
// This could be a separate scheduled job to avoid timeout issues
|
||||||
|
}
|
||||||
|
}
|
@@ -20,9 +20,13 @@ class WPDD_Install {
|
|||||||
WPDD_Post_Types::register_taxonomies();
|
WPDD_Post_Types::register_taxonomies();
|
||||||
|
|
||||||
self::create_tables();
|
self::create_tables();
|
||||||
self::create_pages();
|
|
||||||
self::create_upload_protection();
|
self::create_upload_protection();
|
||||||
|
|
||||||
|
// Set flag to show setup notice instead of creating pages automatically
|
||||||
|
if (!get_option('wpdd_setup_completed')) {
|
||||||
|
add_option('wpdd_show_setup_notice', true);
|
||||||
|
}
|
||||||
|
|
||||||
WPDD_Roles::create_roles();
|
WPDD_Roles::create_roles();
|
||||||
|
|
||||||
// Flush rewrite rules after post types are registered
|
// Flush rewrite rules after post types are registered
|
||||||
@@ -126,6 +130,96 @@ class WPDD_Install {
|
|||||||
KEY transaction_id (transaction_id)
|
KEY transaction_id (transaction_id)
|
||||||
) $charset_collate;";
|
) $charset_collate;";
|
||||||
|
|
||||||
|
$sql[] = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}wpdd_balance_adjustments (
|
||||||
|
id bigint(20) NOT NULL AUTO_INCREMENT,
|
||||||
|
creator_id bigint(20) NOT NULL,
|
||||||
|
adjustment_type varchar(20) NOT NULL,
|
||||||
|
amount decimal(10,2) NOT NULL,
|
||||||
|
previous_balance decimal(10,2) NOT NULL,
|
||||||
|
new_balance decimal(10,2) NOT NULL,
|
||||||
|
reason text NOT NULL,
|
||||||
|
adjusted_by bigint(20) NOT NULL,
|
||||||
|
created_at datetime DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (id),
|
||||||
|
KEY creator_id (creator_id),
|
||||||
|
KEY adjusted_by (adjusted_by)
|
||||||
|
) $charset_collate;";
|
||||||
|
|
||||||
|
// Software Licensing Tables
|
||||||
|
$sql[] = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}wpdd_licenses (
|
||||||
|
id bigint(20) NOT NULL AUTO_INCREMENT,
|
||||||
|
license_key varchar(64) NOT NULL,
|
||||||
|
product_id bigint(20) NOT NULL,
|
||||||
|
order_id bigint(20) NOT NULL,
|
||||||
|
customer_id bigint(20) NOT NULL,
|
||||||
|
customer_email varchar(100) NOT NULL,
|
||||||
|
status varchar(20) NOT NULL DEFAULT 'active',
|
||||||
|
activations_count int(11) DEFAULT 0,
|
||||||
|
max_activations int(11) DEFAULT 1,
|
||||||
|
expires_at datetime DEFAULT NULL,
|
||||||
|
created_at datetime DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
last_checked datetime DEFAULT NULL,
|
||||||
|
PRIMARY KEY (id),
|
||||||
|
UNIQUE KEY license_key (license_key),
|
||||||
|
KEY product_id (product_id),
|
||||||
|
KEY order_id (order_id),
|
||||||
|
KEY customer_id (customer_id),
|
||||||
|
KEY status (status)
|
||||||
|
) $charset_collate;";
|
||||||
|
|
||||||
|
$sql[] = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}wpdd_license_activations (
|
||||||
|
id bigint(20) NOT NULL AUTO_INCREMENT,
|
||||||
|
license_id bigint(20) NOT NULL,
|
||||||
|
license_key varchar(64) NOT NULL,
|
||||||
|
site_url varchar(255) NOT NULL,
|
||||||
|
site_name varchar(255) DEFAULT NULL,
|
||||||
|
activated_at datetime DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
last_checked datetime DEFAULT NULL,
|
||||||
|
wp_version varchar(20) DEFAULT NULL,
|
||||||
|
php_version varchar(20) DEFAULT NULL,
|
||||||
|
status varchar(20) NOT NULL DEFAULT 'active',
|
||||||
|
PRIMARY KEY (id),
|
||||||
|
KEY license_id (license_id),
|
||||||
|
KEY license_key (license_key),
|
||||||
|
KEY site_url (site_url),
|
||||||
|
KEY status (status)
|
||||||
|
) $charset_collate;";
|
||||||
|
|
||||||
|
$sql[] = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}wpdd_software_versions (
|
||||||
|
id bigint(20) NOT NULL AUTO_INCREMENT,
|
||||||
|
product_id bigint(20) NOT NULL,
|
||||||
|
version varchar(20) NOT NULL,
|
||||||
|
changelog text DEFAULT NULL,
|
||||||
|
release_notes text DEFAULT NULL,
|
||||||
|
download_url text DEFAULT NULL,
|
||||||
|
package_url text DEFAULT NULL,
|
||||||
|
min_wp_version varchar(20) DEFAULT NULL,
|
||||||
|
tested_wp_version varchar(20) DEFAULT NULL,
|
||||||
|
min_php_version varchar(20) DEFAULT NULL,
|
||||||
|
release_date datetime DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
git_tag varchar(100) DEFAULT NULL,
|
||||||
|
git_commit varchar(100) DEFAULT NULL,
|
||||||
|
PRIMARY KEY (id),
|
||||||
|
KEY product_id (product_id),
|
||||||
|
KEY version (version),
|
||||||
|
KEY release_date (release_date)
|
||||||
|
) $charset_collate;";
|
||||||
|
|
||||||
|
$sql[] = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}wpdd_webhook_events (
|
||||||
|
id bigint(20) NOT NULL AUTO_INCREMENT,
|
||||||
|
product_id bigint(20) NOT NULL,
|
||||||
|
event_type varchar(50) NOT NULL,
|
||||||
|
payload text DEFAULT NULL,
|
||||||
|
processed varchar(20) NOT NULL DEFAULT 'pending',
|
||||||
|
error_message text DEFAULT NULL,
|
||||||
|
received_at datetime DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
processed_at datetime DEFAULT NULL,
|
||||||
|
PRIMARY KEY (id),
|
||||||
|
KEY product_id (product_id),
|
||||||
|
KEY processed (processed),
|
||||||
|
KEY received_at (received_at)
|
||||||
|
) $charset_collate;";
|
||||||
|
|
||||||
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
|
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
|
||||||
|
|
||||||
foreach ($sql as $query) {
|
foreach ($sql as $query) {
|
||||||
@@ -135,6 +229,10 @@ class WPDD_Install {
|
|||||||
update_option('wpdd_db_version', WPDD_VERSION);
|
update_option('wpdd_db_version', WPDD_VERSION);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function create_pages_optional() {
|
||||||
|
return self::create_pages();
|
||||||
|
}
|
||||||
|
|
||||||
private static function create_pages() {
|
private static function create_pages() {
|
||||||
$pages = array(
|
$pages = array(
|
||||||
'shop' => array(
|
'shop' => array(
|
||||||
@@ -159,6 +257,8 @@ class WPDD_Install {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$created_pages = array();
|
||||||
|
|
||||||
foreach ($pages as $slug => $page) {
|
foreach ($pages as $slug => $page) {
|
||||||
// Check if page already exists
|
// Check if page already exists
|
||||||
$existing_page_id = get_option($page['option']);
|
$existing_page_id = get_option($page['option']);
|
||||||
@@ -184,8 +284,11 @@ class WPDD_Install {
|
|||||||
|
|
||||||
if ($page_id && !is_wp_error($page_id)) {
|
if ($page_id && !is_wp_error($page_id)) {
|
||||||
update_option($page['option'], $page_id);
|
update_option($page['option'], $page_id);
|
||||||
|
$created_pages[] = $page_id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return $created_pages;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function create_upload_protection() {
|
private static function create_upload_protection() {
|
||||||
|
399
includes/class-wpdd-license-manager.php
Normal file
399
includes/class-wpdd-license-manager.php
Normal file
@@ -0,0 +1,399 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
if (!defined('ABSPATH')) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
class WPDD_License_Manager {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the license manager
|
||||||
|
*/
|
||||||
|
public static function init() {
|
||||||
|
add_action('wpdd_order_completed', array(__CLASS__, 'generate_license_for_order'), 10, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a unique license key
|
||||||
|
* Format: XXXX-XXXX-XXXX-XXXX
|
||||||
|
*/
|
||||||
|
public static function generate_license_key() {
|
||||||
|
$segments = array();
|
||||||
|
for ($i = 0; $i < 4; $i++) {
|
||||||
|
$segments[] = strtoupper(substr(md5(uniqid(mt_rand(), true)), 0, 4));
|
||||||
|
}
|
||||||
|
return implode('-', $segments);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate license for completed order
|
||||||
|
*/
|
||||||
|
public static function generate_license_for_order($order_id, $order) {
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
// Check if product is software license type
|
||||||
|
$product_type = get_post_meta($order->product_id, '_wpdd_product_type', true);
|
||||||
|
if ($product_type !== 'software_license') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if license already exists for this order
|
||||||
|
$existing = $wpdb->get_var($wpdb->prepare(
|
||||||
|
"SELECT id FROM {$wpdb->prefix}wpdd_licenses WHERE order_id = %d",
|
||||||
|
$order_id
|
||||||
|
));
|
||||||
|
|
||||||
|
if ($existing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate unique license key
|
||||||
|
$license_key = self::generate_license_key();
|
||||||
|
|
||||||
|
// Ensure it's unique
|
||||||
|
while (self::license_key_exists($license_key)) {
|
||||||
|
$license_key = self::generate_license_key();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get license settings from product
|
||||||
|
$max_activations = get_post_meta($order->product_id, '_wpdd_max_activations', true) ?: 1;
|
||||||
|
$license_duration = get_post_meta($order->product_id, '_wpdd_license_duration', true); // in days
|
||||||
|
|
||||||
|
$expires_at = null;
|
||||||
|
if ($license_duration && $license_duration > 0) {
|
||||||
|
$expires_at = date('Y-m-d H:i:s', strtotime("+{$license_duration} days"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert license
|
||||||
|
$wpdb->insert(
|
||||||
|
$wpdb->prefix . 'wpdd_licenses',
|
||||||
|
array(
|
||||||
|
'license_key' => $license_key,
|
||||||
|
'product_id' => $order->product_id,
|
||||||
|
'order_id' => $order_id,
|
||||||
|
'customer_id' => $order->customer_id,
|
||||||
|
'customer_email' => $order->customer_email,
|
||||||
|
'status' => 'active',
|
||||||
|
'max_activations' => $max_activations,
|
||||||
|
'expires_at' => $expires_at,
|
||||||
|
'created_at' => current_time('mysql')
|
||||||
|
),
|
||||||
|
array('%s', '%d', '%d', '%d', '%s', '%s', '%d', '%s', '%s')
|
||||||
|
);
|
||||||
|
|
||||||
|
// Send license key to customer
|
||||||
|
self::send_license_email($order, $license_key);
|
||||||
|
|
||||||
|
return $license_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if license key exists
|
||||||
|
*/
|
||||||
|
public static function license_key_exists($license_key) {
|
||||||
|
global $wpdb;
|
||||||
|
return $wpdb->get_var($wpdb->prepare(
|
||||||
|
"SELECT COUNT(*) FROM {$wpdb->prefix}wpdd_licenses WHERE license_key = %s",
|
||||||
|
$license_key
|
||||||
|
)) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate license key
|
||||||
|
*/
|
||||||
|
public static function validate_license($license_key, $product_slug = null, $site_url = null) {
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
// Get license details
|
||||||
|
$license = $wpdb->get_row($wpdb->prepare(
|
||||||
|
"SELECT l.*, p.post_name as product_slug
|
||||||
|
FROM {$wpdb->prefix}wpdd_licenses l
|
||||||
|
LEFT JOIN {$wpdb->prefix}posts p ON l.product_id = p.ID
|
||||||
|
WHERE l.license_key = %s",
|
||||||
|
$license_key
|
||||||
|
));
|
||||||
|
|
||||||
|
if (!$license) {
|
||||||
|
return array(
|
||||||
|
'valid' => false,
|
||||||
|
'error' => 'invalid_license',
|
||||||
|
'message' => __('Invalid license key.', 'wp-digital-download')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check product match
|
||||||
|
if ($product_slug && $license->product_slug !== $product_slug) {
|
||||||
|
return array(
|
||||||
|
'valid' => false,
|
||||||
|
'error' => 'product_mismatch',
|
||||||
|
'message' => __('License key is not valid for this product.', 'wp-digital-download')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check status
|
||||||
|
if ($license->status !== 'active') {
|
||||||
|
return array(
|
||||||
|
'valid' => false,
|
||||||
|
'error' => 'license_' . $license->status,
|
||||||
|
'message' => sprintf(__('License is %s.', 'wp-digital-download'), $license->status)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check expiration
|
||||||
|
if ($license->expires_at && strtotime($license->expires_at) < time()) {
|
||||||
|
// Update status to expired
|
||||||
|
$wpdb->update(
|
||||||
|
$wpdb->prefix . 'wpdd_licenses',
|
||||||
|
array('status' => 'expired'),
|
||||||
|
array('id' => $license->id),
|
||||||
|
array('%s'),
|
||||||
|
array('%d')
|
||||||
|
);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'valid' => false,
|
||||||
|
'error' => 'license_expired',
|
||||||
|
'message' => __('License has expired.', 'wp-digital-download'),
|
||||||
|
'expired_at' => $license->expires_at
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check activation limit if site_url provided
|
||||||
|
if ($site_url) {
|
||||||
|
$activation_count = $wpdb->get_var($wpdb->prepare(
|
||||||
|
"SELECT COUNT(*) FROM {$wpdb->prefix}wpdd_license_activations
|
||||||
|
WHERE license_id = %d AND status = 'active'",
|
||||||
|
$license->id
|
||||||
|
));
|
||||||
|
|
||||||
|
$is_activated = $wpdb->get_var($wpdb->prepare(
|
||||||
|
"SELECT COUNT(*) FROM {$wpdb->prefix}wpdd_license_activations
|
||||||
|
WHERE license_id = %d AND site_url = %s AND status = 'active'",
|
||||||
|
$license->id,
|
||||||
|
$site_url
|
||||||
|
));
|
||||||
|
|
||||||
|
if (!$is_activated && $activation_count >= $license->max_activations) {
|
||||||
|
return array(
|
||||||
|
'valid' => false,
|
||||||
|
'error' => 'activation_limit',
|
||||||
|
'message' => sprintf(__('License activation limit reached (%d/%d).', 'wp-digital-download'),
|
||||||
|
$activation_count, $license->max_activations),
|
||||||
|
'activations' => $activation_count,
|
||||||
|
'max_activations' => $license->max_activations
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update last checked
|
||||||
|
$wpdb->update(
|
||||||
|
$wpdb->prefix . 'wpdd_licenses',
|
||||||
|
array('last_checked' => current_time('mysql')),
|
||||||
|
array('id' => $license->id),
|
||||||
|
array('%s'),
|
||||||
|
array('%d')
|
||||||
|
);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'valid' => true,
|
||||||
|
'license' => $license,
|
||||||
|
'message' => __('License is valid.', 'wp-digital-download')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Activate license for a site
|
||||||
|
*/
|
||||||
|
public static function activate_license($license_key, $site_url, $site_name = null, $wp_version = null, $php_version = null) {
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
// Validate license first
|
||||||
|
$validation = self::validate_license($license_key, null, $site_url);
|
||||||
|
if (!$validation['valid']) {
|
||||||
|
return $validation;
|
||||||
|
}
|
||||||
|
|
||||||
|
$license = $validation['license'];
|
||||||
|
|
||||||
|
// Check if already activated for this site
|
||||||
|
$existing = $wpdb->get_row($wpdb->prepare(
|
||||||
|
"SELECT * FROM {$wpdb->prefix}wpdd_license_activations
|
||||||
|
WHERE license_id = %d AND site_url = %s",
|
||||||
|
$license->id,
|
||||||
|
$site_url
|
||||||
|
));
|
||||||
|
|
||||||
|
if ($existing && $existing->status === 'active') {
|
||||||
|
return array(
|
||||||
|
'success' => true,
|
||||||
|
'already_active' => true,
|
||||||
|
'message' => __('License already activated for this site.', 'wp-digital-download')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($existing) {
|
||||||
|
// Reactivate
|
||||||
|
$wpdb->update(
|
||||||
|
$wpdb->prefix . 'wpdd_license_activations',
|
||||||
|
array(
|
||||||
|
'status' => 'active',
|
||||||
|
'activated_at' => current_time('mysql'),
|
||||||
|
'last_checked' => current_time('mysql'),
|
||||||
|
'wp_version' => $wp_version,
|
||||||
|
'php_version' => $php_version,
|
||||||
|
'site_name' => $site_name
|
||||||
|
),
|
||||||
|
array('id' => $existing->id),
|
||||||
|
array('%s', '%s', '%s', '%s', '%s', '%s'),
|
||||||
|
array('%d')
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// New activation
|
||||||
|
$wpdb->insert(
|
||||||
|
$wpdb->prefix . 'wpdd_license_activations',
|
||||||
|
array(
|
||||||
|
'license_id' => $license->id,
|
||||||
|
'license_key' => $license_key,
|
||||||
|
'site_url' => $site_url,
|
||||||
|
'site_name' => $site_name,
|
||||||
|
'activated_at' => current_time('mysql'),
|
||||||
|
'last_checked' => current_time('mysql'),
|
||||||
|
'wp_version' => $wp_version,
|
||||||
|
'php_version' => $php_version,
|
||||||
|
'status' => 'active'
|
||||||
|
),
|
||||||
|
array('%d', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update activation count
|
||||||
|
$count = $wpdb->get_var($wpdb->prepare(
|
||||||
|
"SELECT COUNT(*) FROM {$wpdb->prefix}wpdd_license_activations
|
||||||
|
WHERE license_id = %d AND status = 'active'",
|
||||||
|
$license->id
|
||||||
|
));
|
||||||
|
|
||||||
|
$wpdb->update(
|
||||||
|
$wpdb->prefix . 'wpdd_licenses',
|
||||||
|
array('activations_count' => $count),
|
||||||
|
array('id' => $license->id),
|
||||||
|
array('%d'),
|
||||||
|
array('%d')
|
||||||
|
);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'success' => true,
|
||||||
|
'message' => __('License activated successfully.', 'wp-digital-download'),
|
||||||
|
'activations' => $count,
|
||||||
|
'max_activations' => $license->max_activations
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deactivate license for a site
|
||||||
|
*/
|
||||||
|
public static function deactivate_license($license_key, $site_url) {
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
// Get license
|
||||||
|
$license = $wpdb->get_row($wpdb->prepare(
|
||||||
|
"SELECT * FROM {$wpdb->prefix}wpdd_licenses WHERE license_key = %s",
|
||||||
|
$license_key
|
||||||
|
));
|
||||||
|
|
||||||
|
if (!$license) {
|
||||||
|
return array(
|
||||||
|
'success' => false,
|
||||||
|
'error' => 'invalid_license',
|
||||||
|
'message' => __('Invalid license key.', 'wp-digital-download')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deactivate
|
||||||
|
$updated = $wpdb->update(
|
||||||
|
$wpdb->prefix . 'wpdd_license_activations',
|
||||||
|
array('status' => 'deactivated'),
|
||||||
|
array(
|
||||||
|
'license_id' => $license->id,
|
||||||
|
'site_url' => $site_url
|
||||||
|
),
|
||||||
|
array('%s'),
|
||||||
|
array('%d', '%s')
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!$updated) {
|
||||||
|
return array(
|
||||||
|
'success' => false,
|
||||||
|
'error' => 'not_activated',
|
||||||
|
'message' => __('License not activated for this site.', 'wp-digital-download')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update activation count
|
||||||
|
$count = $wpdb->get_var($wpdb->prepare(
|
||||||
|
"SELECT COUNT(*) FROM {$wpdb->prefix}wpdd_license_activations
|
||||||
|
WHERE license_id = %d AND status = 'active'",
|
||||||
|
$license->id
|
||||||
|
));
|
||||||
|
|
||||||
|
$wpdb->update(
|
||||||
|
$wpdb->prefix . 'wpdd_licenses',
|
||||||
|
array('activations_count' => $count),
|
||||||
|
array('id' => $license->id),
|
||||||
|
array('%d'),
|
||||||
|
array('%d')
|
||||||
|
);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'success' => true,
|
||||||
|
'message' => __('License deactivated successfully.', 'wp-digital-download')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send license email to customer
|
||||||
|
*/
|
||||||
|
private static function send_license_email($order, $license_key) {
|
||||||
|
$product = get_post($order->product_id);
|
||||||
|
$subject = sprintf(__('Your License Key for %s', 'wp-digital-download'), $product->post_title);
|
||||||
|
|
||||||
|
$message = sprintf(
|
||||||
|
__("Hi %s,\n\nThank you for your purchase!\n\nHere is your license key for %s:\n\n%s\n\nPlease keep this key safe. You will need it to activate and receive updates for your software.\n\nBest regards,\n%s", 'wp-digital-download'),
|
||||||
|
$order->customer_name ?: $order->customer_email,
|
||||||
|
$product->post_title,
|
||||||
|
$license_key,
|
||||||
|
get_bloginfo('name')
|
||||||
|
);
|
||||||
|
|
||||||
|
wp_mail($order->customer_email, $subject, $message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get license details for admin
|
||||||
|
*/
|
||||||
|
public static function get_license_details($license_key) {
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
$license = $wpdb->get_row($wpdb->prepare(
|
||||||
|
"SELECT l.*, p.post_title as product_name
|
||||||
|
FROM {$wpdb->prefix}wpdd_licenses l
|
||||||
|
LEFT JOIN {$wpdb->prefix}posts p ON l.product_id = p.ID
|
||||||
|
WHERE l.license_key = %s",
|
||||||
|
$license_key
|
||||||
|
));
|
||||||
|
|
||||||
|
if (!$license) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get activations
|
||||||
|
$license->activations = $wpdb->get_results($wpdb->prepare(
|
||||||
|
"SELECT * FROM {$wpdb->prefix}wpdd_license_activations
|
||||||
|
WHERE license_id = %d
|
||||||
|
ORDER BY activated_at DESC",
|
||||||
|
$license->id
|
||||||
|
));
|
||||||
|
|
||||||
|
return $license;
|
||||||
|
}
|
||||||
|
}
|
@@ -12,6 +12,15 @@ class WPDD_Metaboxes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static function add_meta_boxes() {
|
public static function add_meta_boxes() {
|
||||||
|
add_meta_box(
|
||||||
|
'wpdd_product_type',
|
||||||
|
__('Product Type', 'wp-digital-download'),
|
||||||
|
array(__CLASS__, 'render_product_type_metabox'),
|
||||||
|
'wpdd_product',
|
||||||
|
'side',
|
||||||
|
'high'
|
||||||
|
);
|
||||||
|
|
||||||
add_meta_box(
|
add_meta_box(
|
||||||
'wpdd_product_pricing',
|
'wpdd_product_pricing',
|
||||||
__('Product Pricing', 'wp-digital-download'),
|
__('Product Pricing', 'wp-digital-download'),
|
||||||
@@ -30,6 +39,15 @@ class WPDD_Metaboxes {
|
|||||||
'high'
|
'high'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
add_meta_box(
|
||||||
|
'wpdd_software_licensing',
|
||||||
|
__('Software Licensing', 'wp-digital-download'),
|
||||||
|
array(__CLASS__, 'render_software_licensing_metabox'),
|
||||||
|
'wpdd_product',
|
||||||
|
'normal',
|
||||||
|
'high'
|
||||||
|
);
|
||||||
|
|
||||||
add_meta_box(
|
add_meta_box(
|
||||||
'wpdd_product_settings',
|
'wpdd_product_settings',
|
||||||
__('Product Settings', 'wp-digital-download'),
|
__('Product Settings', 'wp-digital-download'),
|
||||||
@@ -78,6 +96,46 @@ class WPDD_Metaboxes {
|
|||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function render_product_type_metabox($post) {
|
||||||
|
$product_type = get_post_meta($post->ID, '_wpdd_product_type', true) ?: 'digital_download';
|
||||||
|
?>
|
||||||
|
<p>
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="wpdd_product_type" value="digital_download" <?php checked($product_type, 'digital_download'); ?> />
|
||||||
|
<?php _e('Digital Download', 'wp-digital-download'); ?>
|
||||||
|
</label>
|
||||||
|
<br>
|
||||||
|
<span class="description"><?php _e('Standard downloadable file (PDF, images, etc.)', 'wp-digital-download'); ?></span>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="wpdd_product_type" value="software_license" <?php checked($product_type, 'software_license'); ?> />
|
||||||
|
<?php _e('Software License', 'wp-digital-download'); ?>
|
||||||
|
</label>
|
||||||
|
<br>
|
||||||
|
<span class="description"><?php _e('WordPress plugin/theme with license key and automatic updates', 'wp-digital-download'); ?></span>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
jQuery(document).ready(function($) {
|
||||||
|
function toggleMetaboxes() {
|
||||||
|
var productType = $('input[name="wpdd_product_type"]:checked').val();
|
||||||
|
if (productType === 'software_license') {
|
||||||
|
$('#wpdd_software_licensing').show();
|
||||||
|
$('#wpdd_product_files').hide();
|
||||||
|
} else {
|
||||||
|
$('#wpdd_software_licensing').hide();
|
||||||
|
$('#wpdd_product_files').show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$('input[name="wpdd_product_type"]').on('change', toggleMetaboxes);
|
||||||
|
toggleMetaboxes();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
public static function render_files_metabox($post) {
|
public static function render_files_metabox($post) {
|
||||||
$files = get_post_meta($post->ID, '_wpdd_files', true);
|
$files = get_post_meta($post->ID, '_wpdd_files', true);
|
||||||
if (!is_array($files)) {
|
if (!is_array($files)) {
|
||||||
@@ -148,6 +206,101 @@ class WPDD_Metaboxes {
|
|||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function render_software_licensing_metabox($post) {
|
||||||
|
$git_repository = get_post_meta($post->ID, '_wpdd_git_repository', true);
|
||||||
|
$git_username = get_post_meta($post->ID, '_wpdd_git_username', true);
|
||||||
|
$git_token = get_post_meta($post->ID, '_wpdd_git_token', true);
|
||||||
|
$webhook_passcode = get_post_meta($post->ID, '_wpdd_webhook_passcode', true);
|
||||||
|
$max_activations = get_post_meta($post->ID, '_wpdd_max_activations', true) ?: 1;
|
||||||
|
$license_duration = get_post_meta($post->ID, '_wpdd_license_duration', true);
|
||||||
|
$current_version = get_post_meta($post->ID, '_wpdd_current_version', true);
|
||||||
|
$min_wp_version = get_post_meta($post->ID, '_wpdd_min_wp_version', true);
|
||||||
|
$tested_wp_version = get_post_meta($post->ID, '_wpdd_tested_wp_version', true);
|
||||||
|
|
||||||
|
// Generate webhook passcode if not set
|
||||||
|
if (!$webhook_passcode) {
|
||||||
|
$webhook_passcode = wp_generate_password(32, false);
|
||||||
|
update_post_meta($post->ID, '_wpdd_webhook_passcode', $webhook_passcode);
|
||||||
|
}
|
||||||
|
|
||||||
|
$webhook_url = home_url("/wp-json/wpdd/v1/webhook/{$post->ID}/{$webhook_passcode}");
|
||||||
|
?>
|
||||||
|
<div class="wpdd-metabox-content">
|
||||||
|
<h4><?php _e('Repository Settings', 'wp-digital-download'); ?></h4>
|
||||||
|
<p>
|
||||||
|
<label for="wpdd_git_repository"><?php _e('Git Repository URL', 'wp-digital-download'); ?></label><br>
|
||||||
|
<input type="url" id="wpdd_git_repository" name="wpdd_git_repository" value="<?php echo esc_attr($git_repository); ?>" class="widefat" placeholder="https://github.com/username/repository" />
|
||||||
|
<span class="description"><?php _e('Full URL to your Git repository', 'wp-digital-download'); ?></span>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<label for="wpdd_git_username"><?php _e('Git Username (optional)', 'wp-digital-download'); ?></label><br>
|
||||||
|
<input type="text" id="wpdd_git_username" name="wpdd_git_username" value="<?php echo esc_attr($git_username); ?>" class="widefat" />
|
||||||
|
<span class="description"><?php _e('For private repositories', 'wp-digital-download'); ?></span>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<label for="wpdd_git_token"><?php _e('Git Access Token (optional)', 'wp-digital-download'); ?></label><br>
|
||||||
|
<input type="password" id="wpdd_git_token" name="wpdd_git_token" value="<?php echo esc_attr($git_token); ?>" class="widefat" />
|
||||||
|
<span class="description"><?php _e('Personal access token for private repositories', 'wp-digital-download'); ?></span>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h4><?php _e('Webhook Configuration', 'wp-digital-download'); ?></h4>
|
||||||
|
<p>
|
||||||
|
<label><?php _e('Webhook URL', 'wp-digital-download'); ?></label><br>
|
||||||
|
<input type="text" value="<?php echo esc_attr($webhook_url); ?>" class="widefat" readonly onclick="this.select();" />
|
||||||
|
<span class="description"><?php _e('Add this URL to your repository webhook settings (GitHub, GitLab, etc.) to receive release notifications', 'wp-digital-download'); ?></span>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<button type="button" class="button" onclick="wpddRegenerateWebhookPasscode(<?php echo $post->ID; ?>)">
|
||||||
|
<?php _e('Regenerate Webhook URL', 'wp-digital-download'); ?>
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h4><?php _e('License Settings', 'wp-digital-download'); ?></h4>
|
||||||
|
<p>
|
||||||
|
<label for="wpdd_max_activations"><?php _e('Max Activations per License', 'wp-digital-download'); ?></label><br>
|
||||||
|
<input type="number" id="wpdd_max_activations" name="wpdd_max_activations" value="<?php echo esc_attr($max_activations); ?>" min="1" />
|
||||||
|
<span class="description"><?php _e('Number of sites where the license can be activated', 'wp-digital-download'); ?></span>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<label for="wpdd_license_duration"><?php _e('License Duration (days)', 'wp-digital-download'); ?></label><br>
|
||||||
|
<input type="number" id="wpdd_license_duration" name="wpdd_license_duration" value="<?php echo esc_attr($license_duration); ?>" min="0" />
|
||||||
|
<span class="description"><?php _e('Leave blank or 0 for lifetime licenses', 'wp-digital-download'); ?></span>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h4><?php _e('Version Information', 'wp-digital-download'); ?></h4>
|
||||||
|
<p>
|
||||||
|
<label for="wpdd_current_version"><?php _e('Current Version', 'wp-digital-download'); ?></label><br>
|
||||||
|
<input type="text" id="wpdd_current_version" name="wpdd_current_version" value="<?php echo esc_attr($current_version); ?>" placeholder="1.0.0" />
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<label for="wpdd_min_wp_version"><?php _e('Minimum WordPress Version', 'wp-digital-download'); ?></label><br>
|
||||||
|
<input type="text" id="wpdd_min_wp_version" name="wpdd_min_wp_version" value="<?php echo esc_attr($min_wp_version); ?>" placeholder="5.0" />
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<label for="wpdd_tested_wp_version"><?php _e('Tested up to WordPress Version', 'wp-digital-download'); ?></label><br>
|
||||||
|
<input type="text" id="wpdd_tested_wp_version" name="wpdd_tested_wp_version" value="<?php echo esc_attr($tested_wp_version); ?>" placeholder="6.4" />
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
function wpddRegenerateWebhookPasscode(productId) {
|
||||||
|
if (confirm('<?php _e('Are you sure? This will invalidate the existing webhook URL.', 'wp-digital-download'); ?>')) {
|
||||||
|
var newPasscode = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
|
||||||
|
jQuery('#wpdd_webhook_passcode').val(newPasscode);
|
||||||
|
var newUrl = '<?php echo home_url("/wp-json/wpdd/v1/webhook/"); ?>' + productId + '/' + newPasscode;
|
||||||
|
jQuery('input[readonly]').val(newUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
public static function render_settings_metabox($post) {
|
public static function render_settings_metabox($post) {
|
||||||
$download_limit = get_post_meta($post->ID, '_wpdd_download_limit', true);
|
$download_limit = get_post_meta($post->ID, '_wpdd_download_limit', true);
|
||||||
$download_expiry = get_post_meta($post->ID, '_wpdd_download_expiry', true);
|
$download_expiry = get_post_meta($post->ID, '_wpdd_download_expiry', true);
|
||||||
@@ -304,5 +457,52 @@ class WPDD_Metaboxes {
|
|||||||
update_post_meta($post_id, '_wpdd_watermark_text',
|
update_post_meta($post_id, '_wpdd_watermark_text',
|
||||||
sanitize_text_field($_POST['wpdd_watermark_text']));
|
sanitize_text_field($_POST['wpdd_watermark_text']));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Product type
|
||||||
|
if (isset($_POST['wpdd_product_type'])) {
|
||||||
|
update_post_meta($post_id, '_wpdd_product_type',
|
||||||
|
sanitize_text_field($_POST['wpdd_product_type']));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Software licensing fields
|
||||||
|
if (isset($_POST['wpdd_git_repository'])) {
|
||||||
|
update_post_meta($post_id, '_wpdd_git_repository',
|
||||||
|
esc_url_raw($_POST['wpdd_git_repository']));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_POST['wpdd_git_username'])) {
|
||||||
|
update_post_meta($post_id, '_wpdd_git_username',
|
||||||
|
sanitize_text_field($_POST['wpdd_git_username']));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_POST['wpdd_git_token'])) {
|
||||||
|
update_post_meta($post_id, '_wpdd_git_token',
|
||||||
|
sanitize_text_field($_POST['wpdd_git_token']));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_POST['wpdd_max_activations'])) {
|
||||||
|
update_post_meta($post_id, '_wpdd_max_activations',
|
||||||
|
intval($_POST['wpdd_max_activations']));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_POST['wpdd_license_duration'])) {
|
||||||
|
update_post_meta($post_id, '_wpdd_license_duration',
|
||||||
|
intval($_POST['wpdd_license_duration']));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_POST['wpdd_current_version'])) {
|
||||||
|
update_post_meta($post_id, '_wpdd_current_version',
|
||||||
|
sanitize_text_field($_POST['wpdd_current_version']));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_POST['wpdd_min_wp_version'])) {
|
||||||
|
update_post_meta($post_id, '_wpdd_min_wp_version',
|
||||||
|
sanitize_text_field($_POST['wpdd_min_wp_version']));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_POST['wpdd_tested_wp_version'])) {
|
||||||
|
update_post_meta($post_id, '_wpdd_tested_wp_version',
|
||||||
|
sanitize_text_field($_POST['wpdd_tested_wp_version']));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -15,7 +15,26 @@ class WPDD_PayPal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static function enqueue_paypal_sdk() {
|
public static function enqueue_paypal_sdk() {
|
||||||
if (!is_page(get_option('wpdd_checkout_page_id'))) {
|
// Check if we're on a page with the checkout shortcode or if there's a product_id in the URL (checkout page)
|
||||||
|
global $post;
|
||||||
|
$is_checkout = false;
|
||||||
|
|
||||||
|
// Check if we're on the configured checkout page
|
||||||
|
if (is_page(get_option('wpdd_checkout_page_id'))) {
|
||||||
|
$is_checkout = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also check if the current page has the checkout shortcode
|
||||||
|
if ($post && has_shortcode($post->post_content, 'wpdd_checkout')) {
|
||||||
|
$is_checkout = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also check if there's a product_id parameter (checkout flow)
|
||||||
|
if (isset($_GET['product_id']) || isset($_GET['wpdd_action'])) {
|
||||||
|
$is_checkout = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$is_checkout) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,6 +202,9 @@ class WPDD_PayPal {
|
|||||||
|
|
||||||
$order_id = $wpdb->insert_id;
|
$order_id = $wpdb->insert_id;
|
||||||
|
|
||||||
|
// Trigger the order completed hook for balance tracking
|
||||||
|
do_action('wpdd_order_completed', $order_id);
|
||||||
|
|
||||||
self::generate_download_link($order_id);
|
self::generate_download_link($order_id);
|
||||||
|
|
||||||
self::send_purchase_email($order_id);
|
self::send_purchase_email($order_id);
|
||||||
|
169
includes/class-wpdd-setup.php
Normal file
169
includes/class-wpdd-setup.php
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
if (!defined('ABSPATH')) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
class WPDD_Setup {
|
||||||
|
|
||||||
|
public static function init() {
|
||||||
|
add_action('admin_notices', array(__CLASS__, 'show_setup_notice'));
|
||||||
|
add_action('wp_ajax_wpdd_dismiss_setup', array(__CLASS__, 'dismiss_setup_notice'));
|
||||||
|
add_action('wp_ajax_wpdd_create_pages', array(__CLASS__, 'create_default_pages'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function show_setup_notice() {
|
||||||
|
if (!get_option('wpdd_show_setup_notice') || get_option('wpdd_setup_completed')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!current_user_can('manage_options')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$screen = get_current_screen();
|
||||||
|
if ($screen && strpos($screen->id, 'wpdd_product') === false && $screen->id !== 'dashboard') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
<div class="notice notice-info wpdd-setup-notice" style="position: relative;">
|
||||||
|
<h3><?php _e('🎉 WP Digital Download Setup', 'wp-digital-download'); ?></h3>
|
||||||
|
<p><?php _e('Thank you for installing WP Digital Download! To get started, you can create the default pages or set them up manually.', 'wp-digital-download'); ?></p>
|
||||||
|
|
||||||
|
<div class="wpdd-setup-actions" style="margin-bottom: 10px;">
|
||||||
|
<button type="button" class="button button-primary" id="wpdd-create-pages">
|
||||||
|
<?php _e('Create Default Pages', 'wp-digital-download'); ?>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button type="button" class="button button-secondary" id="wpdd-skip-setup">
|
||||||
|
<?php _e('Skip - I\'ll Set Up Manually', 'wp-digital-download'); ?>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<a href="<?php echo admin_url('edit.php?post_type=wpdd_product&page=wpdd-settings'); ?>" class="button">
|
||||||
|
<?php _e('Go to Settings', 'wp-digital-download'); ?>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="wpdd-pages-preview" style="background: #f9f9f9; padding: 15px; margin: 10px 0; border-radius: 4px;">
|
||||||
|
<h4><?php _e('Default Pages to be Created:', 'wp-digital-download'); ?></h4>
|
||||||
|
<ul style="columns: 2; margin: 0;">
|
||||||
|
<li><strong><?php _e('Shop', 'wp-digital-download'); ?></strong> - <?php _e('Product catalog with filtering', 'wp-digital-download'); ?></li>
|
||||||
|
<li><strong><?php _e('My Purchases', 'wp-digital-download'); ?></strong> - <?php _e('Customer purchase history', 'wp-digital-download'); ?></li>
|
||||||
|
<li><strong><?php _e('Checkout', 'wp-digital-download'); ?></strong> - <?php _e('Payment and order form', 'wp-digital-download'); ?></li>
|
||||||
|
<li><strong><?php _e('Thank You', 'wp-digital-download'); ?></strong> - <?php _e('Order confirmation page', 'wp-digital-download'); ?></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="wpdd-setup-status" id="wpdd-setup-status" style="display: none; padding: 10px; margin: 10px 0; border-radius: 4px;">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
jQuery(document).ready(function($) {
|
||||||
|
$('#wpdd-create-pages').on('click', function() {
|
||||||
|
var $button = $(this);
|
||||||
|
var $status = $('#wpdd-setup-status');
|
||||||
|
|
||||||
|
$button.prop('disabled', true).text('<?php _e('Creating Pages...', 'wp-digital-download'); ?>');
|
||||||
|
$status.show().removeClass().addClass('notice notice-info').html('<p><?php _e('Creating default pages...', 'wp-digital-download'); ?></p>');
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: ajaxurl,
|
||||||
|
type: 'POST',
|
||||||
|
data: {
|
||||||
|
action: 'wpdd_create_pages',
|
||||||
|
nonce: '<?php echo wp_create_nonce('wpdd_setup_nonce'); ?>'
|
||||||
|
},
|
||||||
|
success: function(response) {
|
||||||
|
if (response.success) {
|
||||||
|
$status.removeClass().addClass('notice notice-success').html(
|
||||||
|
'<p><strong><?php _e('Success!', 'wp-digital-download'); ?></strong> ' + response.data.message + '</p>' +
|
||||||
|
'<ul>' + response.data.pages.map(function(page) {
|
||||||
|
return '<li><a href="' + page.edit_url + '">' + page.title + '</a> - <a href="' + page.view_url + '" target="_blank"><?php _e('View', 'wp-digital-download'); ?></a></li>';
|
||||||
|
}).join('') + '</ul>'
|
||||||
|
);
|
||||||
|
$('.wpdd-setup-actions').hide();
|
||||||
|
setTimeout(function() {
|
||||||
|
$('.wpdd-setup-notice').fadeOut();
|
||||||
|
}, 5000);
|
||||||
|
} else {
|
||||||
|
$status.removeClass().addClass('notice notice-error').html('<p><strong><?php _e('Error:', 'wp-digital-download'); ?></strong> ' + (response.data || '<?php _e('Unknown error occurred', 'wp-digital-download'); ?>') + '</p>');
|
||||||
|
$button.prop('disabled', false).text('<?php _e('Create Default Pages', 'wp-digital-download'); ?>');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function() {
|
||||||
|
$status.removeClass().addClass('notice notice-error').html('<p><strong><?php _e('Error:', 'wp-digital-download'); ?></strong> <?php _e('Failed to create pages. Please try again.', 'wp-digital-download'); ?></p>');
|
||||||
|
$button.prop('disabled', false).text('<?php _e('Create Default Pages', 'wp-digital-download'); ?>');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#wpdd-skip-setup').on('click', function() {
|
||||||
|
var $button = $(this);
|
||||||
|
$button.prop('disabled', true).text('<?php _e('Dismissing...', 'wp-digital-download'); ?>');
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: ajaxurl,
|
||||||
|
type: 'POST',
|
||||||
|
data: {
|
||||||
|
action: 'wpdd_dismiss_setup',
|
||||||
|
nonce: '<?php echo wp_create_nonce('wpdd_setup_nonce'); ?>'
|
||||||
|
},
|
||||||
|
success: function(response) {
|
||||||
|
$('.wpdd-setup-notice').fadeOut();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function create_default_pages() {
|
||||||
|
check_ajax_referer('wpdd_setup_nonce', 'nonce');
|
||||||
|
|
||||||
|
if (!current_user_can('manage_options')) {
|
||||||
|
wp_send_json_error(__('Permission denied.', 'wp-digital-download'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$created_pages = WPDD_Install::create_pages_optional();
|
||||||
|
|
||||||
|
if (!empty($created_pages)) {
|
||||||
|
$pages_info = array();
|
||||||
|
foreach ($created_pages as $page_id) {
|
||||||
|
$page = get_post($page_id);
|
||||||
|
if ($page) {
|
||||||
|
$pages_info[] = array(
|
||||||
|
'title' => $page->post_title,
|
||||||
|
'edit_url' => admin_url('post.php?post=' . $page_id . '&action=edit'),
|
||||||
|
'view_url' => get_permalink($page_id)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update_option('wpdd_setup_completed', true);
|
||||||
|
delete_option('wpdd_show_setup_notice');
|
||||||
|
|
||||||
|
wp_send_json_success(array(
|
||||||
|
'message' => sprintf(__('%d pages created successfully!', 'wp-digital-download'), count($pages_info)),
|
||||||
|
'pages' => $pages_info
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
wp_send_json_error(__('No pages were created. They may already exist.', 'wp-digital-download'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function dismiss_setup_notice() {
|
||||||
|
check_ajax_referer('wpdd_setup_nonce', 'nonce');
|
||||||
|
|
||||||
|
if (!current_user_can('manage_options')) {
|
||||||
|
wp_send_json_error(__('Permission denied.', 'wp-digital-download'));
|
||||||
|
}
|
||||||
|
|
||||||
|
update_option('wpdd_setup_completed', true);
|
||||||
|
delete_option('wpdd_show_setup_notice');
|
||||||
|
|
||||||
|
wp_send_json_success();
|
||||||
|
}
|
||||||
|
}
|
471
includes/wpdd-plugin-updater.php
Normal file
471
includes/wpdd-plugin-updater.php
Normal file
@@ -0,0 +1,471 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* WPDD Plugin Updater Library
|
||||||
|
*
|
||||||
|
* Include this file in your WordPress plugin to enable automatic updates
|
||||||
|
* and license validation through the WP Digital Download licensing system.
|
||||||
|
*
|
||||||
|
* @version 1.0.0
|
||||||
|
* @author WP Digital Download
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('ABSPATH')) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!class_exists('WPDD_Plugin_Updater')) {
|
||||||
|
|
||||||
|
class WPDD_Plugin_Updater {
|
||||||
|
|
||||||
|
private $plugin_slug;
|
||||||
|
private $plugin_file;
|
||||||
|
private $version;
|
||||||
|
private $license_key;
|
||||||
|
private $update_server;
|
||||||
|
private $transient_key;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the updater
|
||||||
|
*
|
||||||
|
* @param string $plugin_file Full path to the main plugin file
|
||||||
|
* @param string $license_key Your license key
|
||||||
|
* @param string $update_server URL to your update server
|
||||||
|
* @param array $args Additional arguments
|
||||||
|
*/
|
||||||
|
public function __construct($plugin_file, $license_key, $update_server, $args = array()) {
|
||||||
|
$this->plugin_file = $plugin_file;
|
||||||
|
$this->plugin_slug = basename($plugin_file, '.php');
|
||||||
|
$this->license_key = $license_key;
|
||||||
|
$this->update_server = trailingslashit($update_server);
|
||||||
|
|
||||||
|
// Get plugin version from header
|
||||||
|
if (!function_exists('get_plugin_data')) {
|
||||||
|
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
||||||
|
}
|
||||||
|
$plugin_data = get_plugin_data($plugin_file);
|
||||||
|
$this->version = $plugin_data['Version'];
|
||||||
|
|
||||||
|
$this->transient_key = 'wpdd_update_' . $this->plugin_slug;
|
||||||
|
|
||||||
|
// Initialize hooks
|
||||||
|
$this->init_hooks();
|
||||||
|
|
||||||
|
// Add settings page if requested
|
||||||
|
if (isset($args['add_settings_page']) && $args['add_settings_page']) {
|
||||||
|
$this->add_settings_page();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize WordPress hooks
|
||||||
|
*/
|
||||||
|
private function init_hooks() {
|
||||||
|
add_filter('pre_set_site_transient_update_plugins', array($this, 'check_for_update'));
|
||||||
|
add_filter('plugins_api', array($this, 'plugin_info'), 10, 3);
|
||||||
|
add_filter('upgrader_pre_download', array($this, 'maybe_download_package'), 10, 3);
|
||||||
|
|
||||||
|
// Clean up transients on plugin activation/deactivation
|
||||||
|
register_activation_hook($this->plugin_file, array($this, 'delete_transients'));
|
||||||
|
register_deactivation_hook($this->plugin_file, array($this, 'delete_transients'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check for plugin updates
|
||||||
|
*/
|
||||||
|
public function check_for_update($transient) {
|
||||||
|
if (empty($transient->checked)) {
|
||||||
|
return $transient;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get cached update info
|
||||||
|
$update_cache = get_transient($this->transient_key);
|
||||||
|
if ($update_cache !== false) {
|
||||||
|
if (isset($update_cache->update_available) && $update_cache->update_available) {
|
||||||
|
$transient->response[$this->plugin_file] = $update_cache;
|
||||||
|
}
|
||||||
|
return $transient;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for update from server
|
||||||
|
$update_info = $this->request_update_info();
|
||||||
|
|
||||||
|
if ($update_info && isset($update_info['update_available']) && $update_info['update_available']) {
|
||||||
|
$plugin_data = array(
|
||||||
|
'slug' => $this->plugin_slug,
|
||||||
|
'plugin' => $this->plugin_file,
|
||||||
|
'new_version' => $update_info['version'],
|
||||||
|
'url' => $update_info['url'],
|
||||||
|
'package' => $update_info['package'],
|
||||||
|
'tested' => $update_info['tested'],
|
||||||
|
'requires' => $update_info['requires'],
|
||||||
|
'requires_php' => $update_info['requires_php'],
|
||||||
|
'compatibility' => new stdClass()
|
||||||
|
);
|
||||||
|
|
||||||
|
$update_cache = (object) $plugin_data;
|
||||||
|
$update_cache->update_available = true;
|
||||||
|
|
||||||
|
// Cache for 12 hours
|
||||||
|
set_transient($this->transient_key, $update_cache, 12 * HOUR_IN_SECONDS);
|
||||||
|
|
||||||
|
$transient->response[$this->plugin_file] = $update_cache;
|
||||||
|
} else {
|
||||||
|
// No update available - cache negative result for 12 hours
|
||||||
|
$update_cache = new stdClass();
|
||||||
|
$update_cache->update_available = false;
|
||||||
|
set_transient($this->transient_key, $update_cache, 12 * HOUR_IN_SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $transient;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide plugin information for the update screen
|
||||||
|
*/
|
||||||
|
public function plugin_info($false, $action, $args) {
|
||||||
|
if ($action !== 'plugin_information' || $args->slug !== $this->plugin_slug) {
|
||||||
|
return $false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$update_info = $this->request_update_info();
|
||||||
|
|
||||||
|
if (!$update_info) {
|
||||||
|
return $false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (object) array(
|
||||||
|
'slug' => $this->plugin_slug,
|
||||||
|
'name' => $update_info['name'] ?? $this->plugin_slug,
|
||||||
|
'version' => $update_info['version'] ?? $this->version,
|
||||||
|
'author' => $update_info['author'] ?? '',
|
||||||
|
'homepage' => $update_info['url'] ?? '',
|
||||||
|
'requires' => $update_info['requires'] ?? '5.0',
|
||||||
|
'tested' => $update_info['tested'] ?? get_bloginfo('version'),
|
||||||
|
'requires_php' => $update_info['requires_php'] ?? '7.0',
|
||||||
|
'download_link' => $update_info['package'] ?? '',
|
||||||
|
'sections' => array(
|
||||||
|
'changelog' => $update_info['changelog'] ?? '',
|
||||||
|
'description' => $update_info['description'] ?? ''
|
||||||
|
),
|
||||||
|
'banners' => array(),
|
||||||
|
'icons' => array()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle package download with license validation
|
||||||
|
*/
|
||||||
|
public function maybe_download_package($reply, $package, $upgrader) {
|
||||||
|
// Check if this is our plugin's package
|
||||||
|
if (strpos($package, $this->update_server) === false || strpos($package, $this->plugin_slug) === false) {
|
||||||
|
return $reply;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate license before download
|
||||||
|
$license_valid = $this->validate_license();
|
||||||
|
if (!$license_valid) {
|
||||||
|
return new WP_Error('license_invalid', __('Your license key is invalid or expired. Please update your license key.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $reply;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request update information from server
|
||||||
|
*/
|
||||||
|
private function request_update_info() {
|
||||||
|
$url = $this->update_server . "wp-json/wpdd/v1/check-update/{$this->plugin_slug}";
|
||||||
|
$url = add_query_arg(array(
|
||||||
|
'license_key' => $this->license_key,
|
||||||
|
'version' => $this->version,
|
||||||
|
'site_url' => home_url()
|
||||||
|
), $url);
|
||||||
|
|
||||||
|
$response = wp_remote_get($url, array(
|
||||||
|
'timeout' => 15,
|
||||||
|
'headers' => array(
|
||||||
|
'User-Agent' => 'WPDD-Updater/' . $this->version . '; ' . home_url()
|
||||||
|
)
|
||||||
|
));
|
||||||
|
|
||||||
|
if (is_wp_error($response)) {
|
||||||
|
error_log('WPDD Updater: Failed to check for updates - ' . $response->get_error_message());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$body = wp_remote_retrieve_body($response);
|
||||||
|
$data = json_decode($body, true);
|
||||||
|
|
||||||
|
if (!$data || !isset($data['success'])) {
|
||||||
|
error_log('WPDD Updater: Invalid response from update server');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$data['success']) {
|
||||||
|
if (isset($data['error'])) {
|
||||||
|
error_log('WPDD Updater: ' . $data['error'] . ' - ' . ($data['message'] ?? ''));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate license with server
|
||||||
|
*/
|
||||||
|
public function validate_license() {
|
||||||
|
if (empty($this->license_key)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$url = $this->update_server . 'wp-json/wpdd/v1/validate-license';
|
||||||
|
|
||||||
|
$response = wp_remote_post($url, array(
|
||||||
|
'timeout' => 15,
|
||||||
|
'body' => array(
|
||||||
|
'license_key' => $this->license_key,
|
||||||
|
'product_slug' => $this->plugin_slug,
|
||||||
|
'site_url' => home_url()
|
||||||
|
),
|
||||||
|
'headers' => array(
|
||||||
|
'User-Agent' => 'WPDD-Updater/' . $this->version . '; ' . home_url()
|
||||||
|
)
|
||||||
|
));
|
||||||
|
|
||||||
|
if (is_wp_error($response)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$body = wp_remote_retrieve_body($response);
|
||||||
|
$data = json_decode($body, true);
|
||||||
|
|
||||||
|
return $data && isset($data['success']) && $data['success'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Activate license
|
||||||
|
*/
|
||||||
|
public function activate_license($license_key = null) {
|
||||||
|
if ($license_key) {
|
||||||
|
$this->license_key = $license_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($this->license_key)) {
|
||||||
|
return array(
|
||||||
|
'success' => false,
|
||||||
|
'message' => __('Please enter a license key.')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$url = $this->update_server . 'wp-json/wpdd/v1/activate-license';
|
||||||
|
|
||||||
|
$response = wp_remote_post($url, array(
|
||||||
|
'timeout' => 15,
|
||||||
|
'body' => array(
|
||||||
|
'license_key' => $this->license_key,
|
||||||
|
'site_url' => home_url(),
|
||||||
|
'site_name' => get_bloginfo('name'),
|
||||||
|
'wp_version' => get_bloginfo('version'),
|
||||||
|
'php_version' => PHP_VERSION
|
||||||
|
),
|
||||||
|
'headers' => array(
|
||||||
|
'User-Agent' => 'WPDD-Updater/' . $this->version . '; ' . home_url()
|
||||||
|
)
|
||||||
|
));
|
||||||
|
|
||||||
|
if (is_wp_error($response)) {
|
||||||
|
return array(
|
||||||
|
'success' => false,
|
||||||
|
'message' => $response->get_error_message()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$body = wp_remote_retrieve_body($response);
|
||||||
|
$data = json_decode($body, true);
|
||||||
|
|
||||||
|
if (!$data) {
|
||||||
|
return array(
|
||||||
|
'success' => false,
|
||||||
|
'message' => __('Invalid response from server.')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deactivate license
|
||||||
|
*/
|
||||||
|
public function deactivate_license() {
|
||||||
|
if (empty($this->license_key)) {
|
||||||
|
return array(
|
||||||
|
'success' => false,
|
||||||
|
'message' => __('No license key to deactivate.')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$url = $this->update_server . 'wp-json/wpdd/v1/deactivate-license';
|
||||||
|
|
||||||
|
$response = wp_remote_post($url, array(
|
||||||
|
'timeout' => 15,
|
||||||
|
'body' => array(
|
||||||
|
'license_key' => $this->license_key,
|
||||||
|
'site_url' => home_url()
|
||||||
|
),
|
||||||
|
'headers' => array(
|
||||||
|
'User-Agent' => 'WPDD-Updater/' . $this->version . '; ' . home_url()
|
||||||
|
)
|
||||||
|
));
|
||||||
|
|
||||||
|
if (is_wp_error($response)) {
|
||||||
|
return array(
|
||||||
|
'success' => false,
|
||||||
|
'message' => $response->get_error_message()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$body = wp_remote_retrieve_body($response);
|
||||||
|
$data = json_decode($body, true);
|
||||||
|
|
||||||
|
return $data ?: array(
|
||||||
|
'success' => false,
|
||||||
|
'message' => __('Invalid response from server.')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a simple settings page for license management
|
||||||
|
*/
|
||||||
|
private function add_settings_page() {
|
||||||
|
add_action('admin_menu', array($this, 'add_license_menu'));
|
||||||
|
add_action('admin_init', array($this, 'handle_license_actions'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add license menu page
|
||||||
|
*/
|
||||||
|
public function add_license_menu() {
|
||||||
|
add_options_page(
|
||||||
|
sprintf(__('%s License', 'default'), $this->plugin_slug),
|
||||||
|
sprintf(__('%s License', 'default'), $this->plugin_slug),
|
||||||
|
'manage_options',
|
||||||
|
$this->plugin_slug . '-license',
|
||||||
|
array($this, 'render_license_page')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle license activation/deactivation
|
||||||
|
*/
|
||||||
|
public function handle_license_actions() {
|
||||||
|
if (!current_user_can('manage_options')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$option_key = $this->plugin_slug . '_license_key';
|
||||||
|
|
||||||
|
if (isset($_POST['activate_license'])) {
|
||||||
|
if (!wp_verify_nonce($_POST['license_nonce'], 'wpdd_license_nonce')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$license_key = sanitize_text_field($_POST['license_key']);
|
||||||
|
$result = $this->activate_license($license_key);
|
||||||
|
|
||||||
|
if ($result['success']) {
|
||||||
|
update_option($option_key, $license_key);
|
||||||
|
$this->license_key = $license_key;
|
||||||
|
add_settings_error('wpdd_license', 'activated', $result['message'], 'updated');
|
||||||
|
} else {
|
||||||
|
add_settings_error('wpdd_license', 'activation_failed', $result['message'], 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_POST['deactivate_license'])) {
|
||||||
|
if (!wp_verify_nonce($_POST['license_nonce'], 'wpdd_license_nonce')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $this->deactivate_license();
|
||||||
|
|
||||||
|
if ($result['success']) {
|
||||||
|
delete_option($option_key);
|
||||||
|
$this->license_key = '';
|
||||||
|
add_settings_error('wpdd_license', 'deactivated', $result['message'], 'updated');
|
||||||
|
} else {
|
||||||
|
add_settings_error('wpdd_license', 'deactivation_failed', $result['message'], 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render license management page
|
||||||
|
*/
|
||||||
|
public function render_license_page() {
|
||||||
|
$option_key = $this->plugin_slug . '_license_key';
|
||||||
|
$license_key = get_option($option_key, '');
|
||||||
|
$license_status = $this->validate_license();
|
||||||
|
|
||||||
|
?>
|
||||||
|
<div class="wrap">
|
||||||
|
<h1><?php printf(__('%s License Settings', 'default'), esc_html($this->plugin_slug)); ?></h1>
|
||||||
|
|
||||||
|
<?php settings_errors('wpdd_license'); ?>
|
||||||
|
|
||||||
|
<form method="post" action="">
|
||||||
|
<?php wp_nonce_field('wpdd_license_nonce', 'license_nonce'); ?>
|
||||||
|
|
||||||
|
<table class="form-table">
|
||||||
|
<tr>
|
||||||
|
<th scope="row">
|
||||||
|
<label for="license_key"><?php _e('License Key', 'default'); ?></label>
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
<input type="text" id="license_key" name="license_key"
|
||||||
|
value="<?php echo esc_attr($license_key); ?>" class="regular-text" />
|
||||||
|
|
||||||
|
<?php if ($license_status): ?>
|
||||||
|
<span class="dashicons dashicons-yes-alt" style="color: green;"></span>
|
||||||
|
<span style="color: green;"><?php _e('Active', 'default'); ?></span>
|
||||||
|
<?php elseif (!empty($license_key)): ?>
|
||||||
|
<span class="dashicons dashicons-dismiss" style="color: red;"></span>
|
||||||
|
<span style="color: red;"><?php _e('Invalid/Expired', 'default'); ?></span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<?php if (empty($license_key) || !$license_status): ?>
|
||||||
|
<p class="submit">
|
||||||
|
<input type="submit" name="activate_license" class="button-primary"
|
||||||
|
value="<?php _e('Activate License', 'default'); ?>" />
|
||||||
|
</p>
|
||||||
|
<?php else: ?>
|
||||||
|
<p class="submit">
|
||||||
|
<input type="submit" name="deactivate_license" class="button-secondary"
|
||||||
|
value="<?php _e('Deactivate License', 'default'); ?>" />
|
||||||
|
</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<h2><?php _e('Instructions', 'default'); ?></h2>
|
||||||
|
<ol>
|
||||||
|
<li><?php _e('Enter your license key above and click "Activate License"', 'default'); ?></li>
|
||||||
|
<li><?php _e('Once activated, you will receive automatic updates for this plugin', 'default'); ?></li>
|
||||||
|
<li><?php _e('You can deactivate the license if you want to use it on a different site', 'default'); ?></li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete update transients
|
||||||
|
*/
|
||||||
|
public function delete_transients() {
|
||||||
|
delete_transient($this->transient_key);
|
||||||
|
delete_site_transient('update_plugins');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End class exists check
|
@@ -39,6 +39,7 @@ final class WP_Digital_Download {
|
|||||||
private function load_dependencies() {
|
private function load_dependencies() {
|
||||||
$files = array(
|
$files = array(
|
||||||
'includes/class-wpdd-install.php',
|
'includes/class-wpdd-install.php',
|
||||||
|
'includes/class-wpdd-setup.php',
|
||||||
'includes/class-wpdd-post-types.php',
|
'includes/class-wpdd-post-types.php',
|
||||||
'includes/class-wpdd-roles.php',
|
'includes/class-wpdd-roles.php',
|
||||||
'includes/class-wpdd-metaboxes.php',
|
'includes/class-wpdd-metaboxes.php',
|
||||||
@@ -46,6 +47,8 @@ final class WP_Digital_Download {
|
|||||||
'includes/class-wpdd-paypal.php',
|
'includes/class-wpdd-paypal.php',
|
||||||
'includes/class-wpdd-download-handler.php',
|
'includes/class-wpdd-download-handler.php',
|
||||||
'includes/class-wpdd-customer.php',
|
'includes/class-wpdd-customer.php',
|
||||||
|
'includes/class-wpdd-license-manager.php',
|
||||||
|
'includes/class-wpdd-api.php',
|
||||||
'includes/class-wpdd-orders.php',
|
'includes/class-wpdd-orders.php',
|
||||||
'includes/class-wpdd-file-protection.php',
|
'includes/class-wpdd-file-protection.php',
|
||||||
'includes/class-wpdd-watermark.php',
|
'includes/class-wpdd-watermark.php',
|
||||||
@@ -168,7 +171,25 @@ final class WP_Digital_Download {
|
|||||||
error_log('WPDD Error: WPDD_Ajax class not found');
|
error_log('WPDD Error: WPDD_Ajax class not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (class_exists('WPDD_License_Manager')) {
|
||||||
|
WPDD_License_Manager::init();
|
||||||
|
} else {
|
||||||
|
error_log('WPDD Error: WPDD_License_Manager class not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (class_exists('WPDD_API')) {
|
||||||
|
WPDD_API::init();
|
||||||
|
} else {
|
||||||
|
error_log('WPDD Error: WPDD_API class not found');
|
||||||
|
}
|
||||||
|
|
||||||
if (is_admin()) {
|
if (is_admin()) {
|
||||||
|
if (class_exists('WPDD_Setup')) {
|
||||||
|
WPDD_Setup::init();
|
||||||
|
} else {
|
||||||
|
error_log('WPDD Error: WPDD_Setup class not found');
|
||||||
|
}
|
||||||
|
|
||||||
if (class_exists('WPDD_Admin')) {
|
if (class_exists('WPDD_Admin')) {
|
||||||
WPDD_Admin::init();
|
WPDD_Admin::init();
|
||||||
} else {
|
} else {
|
||||||
|
Reference in New Issue
Block a user