get_charset_collate(); $t_products = $wpdb->prefix . 'ppg_products'; $t_wallet = $wpdb->prefix . 'ppg_wallet'; $t_trans = $wpdb->prefix . 'ppg_transactions'; $t_withdraw = $wpdb->prefix . 'ppg_withdrawals'; require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); $sql = " CREATE TABLE $t_products ( id BIGINT(20) NOT NULL AUTO_INCREMENT, name VARCHAR(191) NOT NULL, reward INT NOT NULL DEFAULT 0, time_allowed INT NOT NULL DEFAULT 60, affiliate_url TEXT, PRIMARY KEY (id) ) $charset_collate; CREATE TABLE $t_wallet ( user_id BIGINT(20) NOT NULL, balance BIGINT(20) NOT NULL DEFAULT 0, PRIMARY KEY (user_id) ) $charset_collate; CREATE TABLE $t_trans ( id BIGINT(20) NOT NULL AUTO_INCREMENT, user_id BIGINT(20) NOT NULL, amount INT NOT NULL, type VARCHAR(50) NOT NULL, note TEXT, created_at DATETIME NOT NULL, PRIMARY KEY (id) ) $charset_collate; CREATE TABLE $t_withdraw ( id BIGINT(20) NOT NULL AUTO_INCREMENT, user_id BIGINT(20) NOT NULL, upi VARCHAR(191), amount INT NOT NULL, status VARCHAR(50) NOT NULL DEFAULT 'pending', created_at DATETIME NOT NULL, updated_at DATETIME NULL, PRIMARY KEY (id) ) $charset_collate; "; dbDelta($sql); // default options: affiliate tag + levels if(get_option('ppg_affiliate_tag') === false) update_option('ppg_affiliate_tag','yourtag-21'); if(get_option('ppg_levels') === false){ $default = [ ["MOBILE",10,60], ["LAPTOP",12,70], ["SHOES",8,45], ["WATCH",9,50], ["TABLET",10,60], ["HEADPHONE",9,55], ["CAMERA",11,65] ]; update_option('ppg_levels',$default); } } /* -------------------------- Admin menu and pages (+ wallets view) ----------------------------*/ add_action('admin_menu','ppg_admin_menu'); function ppg_admin_menu(){ add_menu_page('Product Puzzle','Product Puzzle','manage_options','ppg-admin','ppg_admin_page','dashicons-games',6); add_submenu_page('ppg-admin','Withdrawals','Withdrawals','manage_options','ppg-withdrawals','ppg_withdrawals_page'); add_submenu_page('ppg-admin','Wallets','Wallets','manage_options','ppg-wallets','ppg_wallets_page'); } function ppg_admin_page(){ if(!current_user_can('manage_options')) return; // Save settings (levels etc) if(isset($_POST['ppg_save'])){ check_admin_referer('ppg_save_settings','ppg_nonce'); update_option('ppg_affiliate_tag', sanitize_text_field($_POST['affiliate_tag'])); $levels = []; if(!empty($_POST['level_name']) && is_array($_POST['level_name'])){ foreach($_POST['level_name'] as $i=>$n){ $name = sanitize_text_field($n); $reward = intval($_POST['level_reward'][$i]); $time_allowed = intval($_POST['level_time'][$i]); if($name) $levels[] = [$name,$reward,$time_allowed]; } } update_option('ppg_levels',$levels); echo '

Settings saved.

'; } $affiliate = esc_attr(get_option('ppg_affiliate_tag','')); $levels = get_option('ppg_levels', []); ?>

Product Puzzle — Settings

Affiliate Tag

Levels (Product name, Reward coins, Time seconds)

NameRewardTime (s)Remove

prefix . 'ppg_wallet'; $t_trans = $wpdb->prefix . 'ppg_transactions'; $wallets = $wpdb->get_results($wpdb->prepare("SELECT * FROM {$t_wallet} ORDER BY balance DESC LIMIT %d", 500)); $recent_trans = $wpdb->get_results("SELECT t.*, u.user_login, u.user_email FROM $t_trans t LEFT JOIN {$wpdb->users} u ON u.ID = t.user_id ORDER BY t.created_at DESC LIMIT 200"); ?>

PPG Wallets & Transactions

Wallets (top 500)

user_id); $login = $user ? $user->user_login : '-'; ?>
User IDWP UserBalance
user_id); ?> balance); ?>

Recent Transactions (200)

IDUserEmailAmountTypeNoteDate
id); ?> user_id).' / '.esc_html($t->user_login); ?> user_email); ?> amount); ?> type); ?> note); ?> created_at); ?>
prefix . 'ppg_wallet'; // ensure wallet row $bal = $wpdb->get_var($wpdb->prepare("SELECT balance FROM {$t_wallet} WHERE user_id=%d",$user_id)); if($bal === null){ $res = $wpdb->insert($t_wallet,['user_id'=>$user_id,'balance'=>0]); if($res === false) wp_send_json_error('db_error: '.$wpdb->last_error); $bal = 0; } $bal = intval($bal); $correct = intval(get_user_meta($user_id,'ppg_correct',true) ?: 0); $help = intval(get_user_meta($user_id,'ppg_help',true) ?: 0); $aff = intval(get_user_meta($user_id,'ppg_aff',true) ?: 0); $level = intval(get_user_meta($user_id,'ppg_level',true) ?: 0); $playSeconds = intval(get_user_meta($user_id,'ppg_play_seconds',true) ?: 0); wp_send_json_success([ 'balance'=>$bal, 'correct'=>$correct, 'help'=>$help, 'aff'=>$aff, 'level'=>$level, 'playSeconds'=>$playSeconds ]); } /* FIXED award: server-only */ add_action('wp_ajax_ppg_award','ppg_award'); function ppg_award(){ check_ajax_referer('ppg_nonce','nonce'); if(!is_user_logged_in()){ wp_send_json_error('login_required'); } global $wpdb; $user_id = get_current_user_id(); $amount = intval($_POST['amount'] ?? 0); $note = sanitize_text_field($_POST['note'] ?? 'level_reward'); if($amount <= 0){ wp_send_json_error('invalid_amount'); } $t_wallet = $wpdb->prefix . 'ppg_wallet'; $t_trans = $wpdb->prefix . 'ppg_transactions'; // ensure wallet $bal = $wpdb->get_var($wpdb->prepare("SELECT balance FROM {$t_wallet} WHERE user_id=%d",$user_id)); if($bal === null){ $ins = $wpdb->insert($t_wallet, ['user_id'=>$user_id,'balance'=>0]); if($ins === false) wp_send_json_error('db_error: '.$wpdb->last_error); $bal = 0; } $bal = intval($bal); // compute new balance and update $newbal = $bal + $amount; $updated = $wpdb->update($t_wallet, ['balance'=>$newbal], ['user_id'=>$user_id], ['%d'], ['%d']); if($updated === false){ wp_send_json_error('db_error: '.$wpdb->last_error); } // insert transaction log $ins = $wpdb->insert($t_trans, [ 'user_id'=>$user_id, 'amount'=>$amount, 'type'=>'credit', 'note'=>$note, 'created_at'=>current_time('mysql') ]); if($ins === false){ wp_send_json_error('db_error_trans: '.$wpdb->last_error); } // update counters $correct = intval(get_user_meta($user_id,'ppg_correct',true) ?: 0) + 1; update_user_meta($user_id,'ppg_correct',$correct); $levels = get_option('ppg_levels', []); $current_level = intval(get_user_meta($user_id,'ppg_level',true) ?: 0); $max_level = max(0, count($levels)-1); $new_level = ($current_level < $max_level) ? ($current_level + 1) : $max_level; update_user_meta($user_id,'ppg_level',$new_level); $final = $wpdb->get_var($wpdb->prepare("SELECT balance FROM {$t_wallet} WHERE user_id=%d",$user_id)); if($final === null) wp_send_json_error('db_error_final: '.$wpdb->last_error); wp_send_json_success(['balance'=>intval($final),'level'=>$new_level,'correct'=>$correct]); } /* affiliate click */ add_action('wp_ajax_ppg_aff_click','ppg_aff_click'); add_action('wp_ajax_nopriv_ppg_aff_click','ppg_aff_click'); function ppg_aff_click(){ check_ajax_referer('ppg_nonce','nonce'); global $wpdb; $t_trans = $wpdb->prefix . 'ppg_transactions'; $user_id = get_current_user_id() ?: 0; $product = sanitize_text_field($_POST['product'] ?? ''); $wpdb->insert($t_trans,[ 'user_id'=>$user_id, 'amount'=>0, 'type'=>'affiliate_click', 'note'=>$product, 'created_at'=>current_time('mysql') ]); if($user_id){ $aff = intval(get_user_meta($user_id,'ppg_aff',true) ?: 0) + 1; update_user_meta($user_id,'ppg_aff',$aff); } wp_send_json_success(); } /* withdraw */ add_action('wp_ajax_ppg_withdraw','ppg_withdraw'); add_action('wp_ajax_nopriv_ppg_withdraw','ppg_withdraw'); function ppg_withdraw(){ check_ajax_referer('ppg_nonce','nonce'); if(!is_user_logged_in()){ wp_send_json_error('login_required'); } global $wpdb; $user_id = get_current_user_id(); $upi = sanitize_text_field($_POST['upi'] ?? ''); $amount = intval($_POST['amount'] ?? 0); if(!$upi || $amount <= 0){ wp_send_json_error('invalid_input'); } if($amount < 50){ wp_send_json_error('min_withdraw_50'); } $t_withdraw = $wpdb->prefix . 'ppg_withdrawals'; $t_wallet = $wpdb->prefix . 'ppg_wallet'; $t_trans = $wpdb->prefix . 'ppg_transactions'; // ensure wallet exists $bal_db = $wpdb->get_var($wpdb->prepare("SELECT balance FROM {$t_wallet} WHERE user_id=%d",$user_id)); if($bal_db === null){ $wpdb->insert($t_wallet, ['user_id'=>$user_id,'balance'=>0]); if($wpdb->last_error) wp_send_json_error('db_error: '.$wpdb->last_error); $bal_db = 0; } $bal = intval($bal_db); if($bal < $amount){ wp_send_json_error('insufficient'); } // create withdraw request (pending) $inserted = $wpdb->insert($t_withdraw,[ 'user_id'=>$user_id, 'upi'=>$upi, 'amount'=>$amount, 'status'=>'pending', 'created_at'=>current_time('mysql') ]); if(!$inserted) wp_send_json_error('db_error: '.$wpdb->last_error); // log transaction (type withdraw_request) $wpdb->insert($t_trans,[ 'user_id'=>$user_id, 'amount'=>0, 'type'=>'withdraw_request', 'note'=>"Withdraw request to $upi", 'created_at'=>current_time('mysql') ]); // notify admin $admin_email = get_option('admin_email'); if($admin_email){ $user_info = get_userdata($user_id); $user_name = $user_info ? $user_info->display_name : "User #{$user_id}"; $subject = "New Withdrawal Request — Product Puzzle (ID: {$wpdb->insert_id})"; $message = "Hello Admin,\n\nA new withdrawal request was created:\n\n"; $message .= "Request ID: {$wpdb->insert_id}\nUser ID: {$user_id}\n"; if($user_info && $user_info->user_email) $message .= "User Email: {$user_info->user_email}\n"; $message .= "User Name: {$user_name}\nAmount: {$amount}\nUPI: {$upi}\nCreated: ".current_time('mysql')."\n\nApprove in WP Admin > Product Puzzle > Withdrawals."; wp_mail($admin_email, $subject, $message); } wp_send_json_success(['message'=>'request_created','id'=>$wpdb->insert_id]); } /* get wallet */ add_action('wp_ajax_ppg_get_wallet','ppg_get_wallet'); add_action('wp_ajax_nopriv_ppg_get_wallet','ppg_get_wallet'); function ppg_get_wallet(){ check_ajax_referer('ppg_nonce','nonce'); if(!is_user_logged_in()){ wp_send_json_error('login_required'); } global $wpdb; $user_id = get_current_user_id(); $t_wallet = $wpdb->prefix . 'ppg_wallet'; $bal = $wpdb->get_var($wpdb->prepare("SELECT balance FROM {$t_wallet} WHERE user_id=%d",$user_id)); if($bal === null){ $wpdb->insert($t_wallet,['user_id'=>$user_id,'balance'=>0]); if($wpdb->last_error) wp_send_json_error('db_error: '.$wpdb->last_error); $bal = 0; } wp_send_json_success(['balance'=>intval($bal)]); } /* -------------------------- Admin withdrawals page (approve/reject sends user email) ----------------------------*/ function ppg_withdrawals_page(){ if(!current_user_can('manage_options')) return; global $wpdb; $t_withdraw = $wpdb->prefix . 'ppg_withdrawals'; $t_wallet = $wpdb->prefix . 'ppg_wallet'; $t_trans = $wpdb->prefix . 'ppg_transactions'; // process actions: approve/reject if(isset($_GET['action']) && isset($_GET['id'])){ if(check_admin_referer('ppg_withdraw_action','ppg_withdraw_nonce')){ $id = intval($_GET['id']); $action = sanitize_text_field($_GET['action']); $row = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$t_withdraw} WHERE id=%d",$id)); if($row){ $user_id = intval($row->user_id); $amount = intval($row->amount); $user_info = get_userdata($user_id); $user_email = $user_info ? $user_info->user_email : ''; if($action === 'approve' && $row->status === 'pending'){ $bal = $wpdb->get_var($wpdb->prepare("SELECT balance FROM {$t_wallet} WHERE user_id=%d",$user_id)); $bal = ($bal === null) ? 0 : intval($bal); if($bal >= $amount){ $wpdb->update($t_wallet, ['balance' => $bal - $amount], ['user_id' => $user_id], ['%d'], ['%d']); $wpdb->insert($t_trans,[ 'user_id'=>$user_id, 'amount'=>-$amount, 'type'=>'withdraw_approved', 'note'=>"Withdraw approved (id {$id})", 'created_at'=>current_time('mysql') ]); $wpdb->update($t_withdraw,['status'=>'approved','updated_at'=>current_time('mysql')],['id'=>$id]); if($user_email){ $subject = "Your withdrawal request #{$id} approved"; $msg = "Hello,\n\nYour withdrawal request (ID: {$id}) for amount {$amount} has been approved by admin.\nUPI: {$row->upi}\n\nRegards,\nProduct Puzzle"; wp_mail($user_email, $subject, $msg); } } else { $wpdb->update($t_withdraw,['status'=>'rejected','updated_at'=>current_time('mysql')],['id'=>$id]); if($user_email){ $subject = "Your withdrawal request #{$id} rejected"; $msg = "Hello,\n\nYour withdrawal request (ID: {$id}) was rejected due to insufficient balance. Please contact admin.\n\nRegards,\nProduct Puzzle"; wp_mail($user_email, $subject, $msg); } } } elseif($action === 'reject' && $row->status === 'pending'){ $wpdb->update($t_withdraw,['status'=>'rejected','updated_at'=>current_time('mysql')],['id'=>$id]); if($user_email){ $subject = "Your withdrawal request #{$id} rejected"; $msg = "Hello,\n\nYour withdrawal request (ID: {$id}) has been rejected by admin.\n\nRegards,\nProduct Puzzle"; wp_mail($user_email, $subject, $msg); } } } } } $rows = $wpdb->get_results("SELECT w.*, u.user_login, u.user_email FROM {$t_withdraw} w LEFT JOIN {$wpdb->users} u ON u.ID = w.user_id ORDER BY w.created_at DESC"); ?>

Withdrawals

IDUserEmailUPIAmountStatusCreatedAction
id; ?> user_id).' / '.esc_html($r->user_login); ?> user_email); ?> upi); ?> amount); ?> status); ?> created_at); ?> status === 'pending'): ?> Approve | Reject
$l[0],'reward'=>intval($l[1]),'time'=>intval($l[2])]; } $ajax_url = admin_url('admin-ajax.php'); $nonce = wp_create_nonce('ppg_nonce'); ob_start(); ?>

🧩 Product Puzzle

💰 0

Level 1

Reward: 10V • Time: 60s
Click letters to place them — server-synced.
✔️ Correct: 0
❓ Help used: 0
🔗 Affiliate clicks: 0
⏱️ Play time today: 0s

⛽ Withdraw Petrol

Balance: 0 coins

Popular posts from this blog