namespace, '/admin/affiliates', [ 'methods' => WP_REST_Server::READABLE, 'callback' => [$this, 'get_affiliates'], 'permission_callback' => [$this, 'check_permission'], ]); // Get Affiliate Balance (payable amount) register_rest_route($this->namespace, '/admin/affiliates/(?P\d+)/balance', [ 'methods' => WP_REST_Server::READABLE, 'callback' => [$this, 'get_affiliate_balance'], 'permission_callback' => [$this, 'check_permission'], ]); // Approve Affiliate register_rest_route($this->namespace, '/admin/affiliates/(?P\d+)/approve', [ 'methods' => WP_REST_Server::CREATABLE, 'callback' => [$this, 'approve_affiliate'], 'permission_callback' => [$this, 'check_permission'], ]); // Update Affiliate (commission rate) register_rest_route($this->namespace, '/admin/affiliates/(?P\d+)/update', [ 'methods' => WP_REST_Server::CREATABLE, 'callback' => [$this, 'update_affiliate'], 'permission_callback' => [$this, 'check_permission'], ]); // List Referrals register_rest_route($this->namespace, '/admin/affiliates/referrals', [ 'methods' => WP_REST_Server::READABLE, 'callback' => [$this, 'get_referrals'], 'permission_callback' => [$this, 'check_permission'], ]); // List Payouts (for all affiliates) register_rest_route($this->namespace, '/admin/affiliates/payouts', [ 'methods' => WP_REST_Server::READABLE, 'callback' => [$this, 'get_payouts'], 'permission_callback' => [$this, 'check_permission'], ]); // Create Payout register_rest_route($this->namespace, '/admin/affiliates/payouts', [ 'methods' => WP_REST_Server::CREATABLE, 'callback' => [$this, 'create_payout'], 'permission_callback' => [$this, 'check_permission'], ]); } public function check_permission() { return current_user_can('manage_woocommerce'); } public function get_affiliates(WP_REST_Request $request) { global $wpdb; $table = $wpdb->prefix . 'woonoow_affiliates'; $affiliates = $wpdb->get_results("SELECT * FROM $table ORDER BY created_at DESC", ARRAY_A); // Add payable_balance to each affiliate foreach ($affiliates as &$affiliate) { $affiliate['payable_balance'] = (float) ($affiliate['total_earnings'] ?? 0) - (float) ($affiliate['paid_earnings'] ?? 0); // Get user info $user = get_userdata($affiliate['user_id']); if ($user) { $affiliate['user_email'] = $user->user_email; $affiliate['user_name'] = $user->display_name; } } return rest_ensure_response($affiliates); } public function get_affiliate_balance(WP_REST_Request $request) { global $wpdb; $id = $request->get_param('id'); $table = $wpdb->prefix . 'woonoow_affiliates'; $affiliate = $wpdb->get_row($wpdb->prepare( "SELECT id, user_id, referral_code, total_earnings, paid_earnings, total_referrals FROM $table WHERE id = %d", $id )); if (!$affiliate) { return new WP_REST_Response(['error' => 'Affiliate not found'], 404); } $user = get_userdata($affiliate->user_id); return rest_ensure_response([ 'id' => (int) $affiliate->id, 'user_id' => (int) $affiliate->user_id, 'user_name' => $user ? $user->display_name : 'Unknown', 'user_email' => $user ? $user->user_email : '', 'referral_code' => $affiliate->referral_code, 'total_earnings' => (float) $affiliate->total_earnings, 'paid_earnings' => (float) $affiliate->paid_earnings, 'payable_balance' => (float) $affiliate->total_earnings - (float) $affiliate->paid_earnings, 'total_referrals' => (int) $affiliate->total_referrals, 'approved_referrals' => $this->get_approved_referral_count($affiliate->id), 'pending_referrals' => $this->get_pending_referral_count($affiliate->id), ]); } private function get_approved_referral_count($affiliate_id) { global $wpdb; return (int) $wpdb->get_var($wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->prefix}woonoow_referrals WHERE affiliate_id = %d AND status = 'approved'", $affiliate_id )); } private function get_pending_referral_count($affiliate_id) { global $wpdb; return (int) $wpdb->get_var($wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->prefix}woonoow_referrals WHERE affiliate_id = %d AND status = 'pending'", $affiliate_id )); } public function approve_affiliate(WP_REST_Request $request) { global $wpdb; $id = $request->get_param('id'); $table = $wpdb->prefix . 'woonoow_affiliates'; $wpdb->update( $table, ['status' => 'active'], ['id' => $id] ); // Trigger email notification for approval $affiliate = $wpdb->get_row($wpdb->prepare("SELECT * FROM $table WHERE id = %d", $id)); if ($affiliate) { $user = get_userdata($affiliate->user_id); if ($user) { do_action('woonoow/email/trigger', 'affiliate_application_approved', $user->user_email, [ 'affiliate_name' => $user->display_name, 'referral_code' => $affiliate->referral_code ]); } } return rest_ensure_response(['success' => true]); } public function update_affiliate(WP_REST_Request $request) { global $wpdb; $id = $request->get_param('id'); $table = $wpdb->prefix . 'woonoow_affiliates'; $custom_rate = $request->get_param('custom_commission_rate'); // If rate is empty string or not provided, clear custom rate if ($custom_rate === '' || $custom_rate === null || $custom_rate === false) { $data = ['custom_commission_rate' => null]; } else { $custom_rate = floatval($custom_rate); if ($custom_rate < 0 || $custom_rate > 100) { return new WP_REST_Response(['error' => 'Commission rate must be between 0 and 100'], 400); } $data = ['custom_commission_rate' => $custom_rate]; } $result = $wpdb->update( $table, $data, ['id' => $id] ); if ($result === false) { return new WP_REST_Response(['error' => 'Failed to update affiliate'], 500); } return rest_ensure_response(['success' => true, 'custom_commission_rate' => $data['custom_commission_rate']]); } public function get_referrals(WP_REST_Request $request) { global $wpdb; $table = $wpdb->prefix . 'woonoow_referrals'; // Add filter support $where = "1=1"; $affiliate_id = $request->get_param('affiliate_id'); if ($affiliate_id) { $where .= $wpdb->prepare(" AND affiliate_id = %d", $affiliate_id); } $status = $request->get_param('status'); if ($status) { $where .= $wpdb->prepare(" AND status = %s", $status); } $date_start = $request->get_param('date_start'); if ($date_start) { $where .= $wpdb->prepare(" AND created_at >= %s", $date_start . ' 00:00:00'); } $date_end = $request->get_param('date_end'); if ($date_end) { $where .= $wpdb->prepare(" AND created_at <= %s", $date_end . ' 23:59:59'); } $order_id = $request->get_param('order_id'); if ($order_id) { $where .= $wpdb->prepare(" AND order_id = %d", $order_id); } $referrals = $wpdb->get_results("SELECT * FROM $table WHERE $where ORDER BY created_at DESC", ARRAY_A); // Enrich with affiliate info $affiliates_table = $wpdb->prefix . 'woonoow_affiliates'; foreach ($referrals as &$referral) { $affiliate = $wpdb->get_row($wpdb->prepare( "SELECT a.*, u.display_name as affiliate_name, u.user_email as affiliate_email FROM $affiliates_table a LEFT JOIN $wpdb->users u ON a.user_id = u.ID WHERE a.id = %d", $referral['affiliate_id'] )); if ($affiliate) { $referral['affiliate_name'] = $affiliate->affiliate_name; $referral['affiliate_email'] = $affiliate->affiliate_email; } } return rest_ensure_response($referrals); } public function get_payouts(WP_REST_Request $request) { global $wpdb; $table = $wpdb->prefix . 'woonoow_affiliate_payouts'; $affiliate_id = $request->get_param('affiliate_id'); $where = $affiliate_id ? $wpdb->prepare("WHERE affiliate_id = %d", $affiliate_id) : ""; $payouts = $wpdb->get_results("SELECT * FROM $table $where ORDER BY created_at DESC", ARRAY_A); // Enrich with affiliate info $affiliates_table = $wpdb->prefix . 'woonoow_affiliates'; foreach ($payouts as &$payout) { $affiliate = $wpdb->get_row($wpdb->prepare( "SELECT a.*, u.display_name as affiliate_name, u.user_email as affiliate_email FROM $affiliates_table a LEFT JOIN $wpdb->users u ON a.user_id = u.ID WHERE a.id = %d", $payout['affiliate_id'] )); if ($affiliate) { $payout['affiliate_name'] = $affiliate->affiliate_name; $payout['affiliate_email'] = $affiliate->affiliate_email; } } return rest_ensure_response($payouts); } public function create_payout(WP_REST_Request $request) { global $wpdb; $payouts_table = $wpdb->prefix . 'woonoow_affiliate_payouts'; $affiliates_table = $wpdb->prefix . 'woonoow_affiliates'; $affiliate_id = absint($request->get_param('affiliate_id')); $amount = floatval($request->get_param('amount')); $method = sanitize_text_field($request->get_param('method') ?: 'bank_transfer'); $notes = ''; // Validate affiliate exists and get balance $affiliate = $wpdb->get_row($wpdb->prepare( "SELECT * FROM $affiliates_table WHERE id = %d", $affiliate_id )); if (!$affiliate) { return new WP_REST_Response(['error' => 'Affiliate not found'], 404); } $payable_balance = (float) $affiliate->total_earnings - (float) $affiliate->paid_earnings; if ($amount <= 0) { return new WP_REST_Response(['error' => 'Amount must be greater than 0'], 400); } if ($amount > $payable_balance) { return new WP_REST_Response([ 'error' => 'Amount exceeds payable balance', 'payable_balance' => $payable_balance ], 400); } // Generate coupon for store_credit method if ($method === 'store_credit') { $user = get_userdata($affiliate->user_id); if ($user) { $coupon_code = 'CREDIT-' . strtoupper(wp_generate_password(8, false)); $coupon = new \WC_Coupon(); $coupon->set_code($coupon_code); $coupon->set_discount_type('fixed_cart'); $coupon->set_amount($amount); $coupon->set_email_restrictions([$user->user_email]); $coupon->set_usage_limit(1); $coupon->set_description('Store Credit for Affiliate Payout'); $coupon->save(); $notes = 'Generated Store Credit Coupon: ' . $coupon_code; } } // Create payout record $wpdb->insert($payouts_table, [ 'affiliate_id' => $affiliate_id, 'amount' => $amount, 'currency' => get_woocommerce_currency(), 'method' => $method, 'status' => 'completed', 'notes' => $notes, 'completed_at' => current_time('mysql') ]); $payout_id = $wpdb->insert_id; // Update affiliate's paid_earnings $wpdb->query($wpdb->prepare( "UPDATE $affiliates_table SET paid_earnings = paid_earnings + %f WHERE id = %d", $amount, $affiliate_id )); return rest_ensure_response([ 'success' => true, 'id' => $payout_id, 'new_paid_earnings' => (float) $affiliate->paid_earnings + $amount, 'new_payable_balance' => $payable_balance - $amount, 'coupon_code' => $method === 'store_credit' ? $coupon_code : null ]); } }