Files
WooNooW/includes/Api/Controllers/AffiliateCustomerController.php

476 lines
18 KiB
PHP

<?php
namespace WooNooW\Api\Controllers;
use WP_REST_Request;
use WP_REST_Response;
use WP_REST_Server;
use WooNooW\Modules\Affiliate\AffiliateSettings;
class AffiliateCustomerController
{
private $namespace = 'woonoow/v1';
public function register_routes()
{
register_rest_route($this->namespace, '/account/affiliate', [
'methods' => WP_REST_Server::READABLE,
'callback' => [$this, 'get_dashboard'],
'permission_callback' => [$this, 'check_permission'],
]);
register_rest_route($this->namespace, '/account/affiliate/apply', [
'methods' => WP_REST_Server::CREATABLE,
'callback' => [$this, 'apply_affiliate'],
'permission_callback' => [$this, 'check_permission'],
]);
register_rest_route($this->namespace, '/account/affiliate/referrals', [
'methods' => WP_REST_Server::READABLE,
'callback' => [$this, 'get_referrals'],
'permission_callback' => [$this, 'check_permission'],
]);
register_rest_route($this->namespace, '/account/affiliate/payouts', [
'methods' => WP_REST_Server::READABLE,
'callback' => [$this, 'get_payouts'],
'permission_callback' => [$this, 'check_permission'],
]);
register_rest_route($this->namespace, '/account/affiliate/payment-details', [
'methods' => WP_REST_Server::READABLE,
'callback' => [$this, 'get_payment_details'],
'permission_callback' => [$this, 'check_permission'],
]);
register_rest_route($this->namespace, '/account/affiliate/payment-details', [
'methods' => WP_REST_Server::CREATABLE,
'callback' => [$this, 'update_payment_details'],
'permission_callback' => [$this, 'check_permission'],
]);
// Affiliate Collections
register_rest_route($this->namespace, '/account/affiliate/collections', [
[
'methods' => WP_REST_Server::READABLE,
'callback' => [$this, 'get_collections'],
'permission_callback' => [$this, 'check_permission'],
],
[
'methods' => WP_REST_Server::CREATABLE,
'callback' => [$this, 'create_collection'],
'permission_callback' => [$this, 'check_permission'],
]
]);
register_rest_route($this->namespace, '/account/affiliate/collections/(?P<id>\d+)', [
[
'methods' => WP_REST_Server::EDITABLE,
'callback' => [$this, 'update_collection'],
'permission_callback' => [$this, 'check_permission'],
],
[
'methods' => WP_REST_Server::DELETABLE,
'callback' => [$this, 'delete_collection'],
'permission_callback' => [$this, 'check_permission'],
]
]);
}
public function check_permission()
{
return is_user_logged_in();
}
public function get_dashboard(WP_REST_Request $request)
{
global $wpdb;
$user_id = get_current_user_id();
$table = $wpdb->prefix . 'woonoow_affiliates';
$affiliate = $wpdb->get_row($wpdb->prepare("SELECT * FROM $table WHERE user_id = %d", $user_id), ARRAY_A);
if (!$affiliate) {
return new \WP_Error('not_found', 'Affiliate profile not found', ['status' => 404]);
}
// Get global default rate
$global_rate = (float) get_option('woonoow_affiliate_default_rate', 10);
// Use custom rate if set, otherwise global
$effective_rate = !empty($affiliate['custom_commission_rate'])
? (float) $affiliate['custom_commission_rate']
: $global_rate;
$referrals_table = $wpdb->prefix . 'woonoow_referrals';
$earnings = $wpdb->get_row($wpdb->prepare(
"SELECT
SUM(CASE WHEN status = 'approved' THEN commission_amount ELSE 0 END) as total_earnings,
SUM(CASE WHEN status = 'pending' THEN commission_amount ELSE 0 END) as pending_earnings
FROM $referrals_table
WHERE affiliate_id = %d",
$affiliate['id']
));
$affiliate['global_commission_rate'] = $global_rate;
$affiliate['commission_rate'] = $effective_rate;
$affiliate['total_earnings'] = $earnings->total_earnings ?: 0;
$affiliate['pending_earnings'] = $earnings->pending_earnings ?: 0;
if (class_exists('\WooNooW\Modules\Affiliate\AffiliateSettings')) {
$affiliate['collections_enabled'] = \WooNooW\Modules\Affiliate\AffiliateSettings::get_setting('woonoow_affiliate_enable_curated_collections', true);
} else {
$affiliate['collections_enabled'] = true;
}
return rest_ensure_response($affiliate);
}
public function apply_affiliate(WP_REST_Request $request)
{
global $wpdb;
$user_id = get_current_user_id();
$table = $wpdb->prefix . 'woonoow_affiliates';
// Check if already applied
$exists = $wpdb->get_var($wpdb->prepare("SELECT id FROM $table WHERE user_id = %d", $user_id));
if ($exists) {
return new \WP_Error('exists', 'You have already applied.', ['status' => 400]);
}
// Generate simple code
$user = wp_get_current_user();
$referral_code = strtolower(preg_replace('/[^a-zA-Z0-9]/', '', $user->user_login)) . wp_generate_password(4, false);
$auto_approve = get_option('woonoow_affiliate_auto_approve', false);
$status = $auto_approve ? 'active' : 'pending';
$wpdb->insert($table, [
'user_id' => $user_id,
'referral_code' => $referral_code,
'commission_rate' => get_option('woonoow_affiliate_default_rate', 10), // 10% default
'status' => $status
]);
// Trigger email notification for admin
$admin_email = get_option('admin_email');
do_action('woonoow/email/trigger', 'affiliate_application_received', $admin_email, [
'affiliate_name' => $user->display_name,
'customer_email' => $user->user_email
]);
if ($auto_approve) {
do_action('woonoow/email/trigger', 'affiliate_application_approved', $user->user_email, [
'affiliate_name' => $user->display_name,
'customer_email' => $user->user_email,
'referral_code' => $referral_code
]);
}
return rest_ensure_response(['success' => true, 'status' => $status, 'referral_code' => $referral_code]);
}
public function get_referrals(WP_REST_Request $request)
{
global $wpdb;
$user_id = get_current_user_id();
$affiliates_table = $wpdb->prefix . 'woonoow_affiliates';
$referrals_table = $wpdb->prefix . 'woonoow_referrals';
$affiliate = $wpdb->get_row($wpdb->prepare("SELECT id FROM $affiliates_table WHERE user_id = %d", $user_id));
if (!$affiliate) {
return rest_ensure_response([]);
}
$limit = (int) $request->get_param('limit');
$page = max(1, (int) $request->get_param('page'));
$order_id = $request->get_param('order_id') ? (int) $request->get_param('order_id') : null;
$where = $wpdb->prepare("WHERE r.affiliate_id = %d", $affiliate->id);
if ($order_id) {
$where .= $wpdb->prepare(" AND r.order_id = %d", $order_id);
}
$sql = "SELECT r.*,
COALESCE(NULLIF(r.cancelled_reason, ''), NULL) as cancelled_reason,
COALESCE(r.approved_at, r.created_at) as approved_at
FROM $referrals_table r
$where
ORDER BY r.created_at DESC";
if ($limit > 0) {
$offset = ($page - 1) * $limit;
$sql .= $wpdb->prepare(" LIMIT %d OFFSET %d", $limit, $offset);
}
$referrals = $wpdb->get_results($sql, ARRAY_A);
$total = $wpdb->get_var("SELECT COUNT(r.id) FROM $referrals_table r $where");
// Attach customer data if enabled
if (!empty($referrals) && AffiliateSettings::get_setting('woonoow_affiliate_share_customer_data', false)) {
foreach ($referrals as &$ref) {
if (!empty($ref['order_id'])) {
$order = wc_get_order($ref['order_id']);
if ($order) {
$ref['customer_name'] = trim($order->get_billing_first_name() . ' ' . $order->get_billing_last_name());
$ref['customer_email'] = $order->get_billing_email();
}
}
}
}
return rest_ensure_response([
'referrals' => $referrals,
'total' => (int) $total,
'page' => $page,
'limit' => $limit > 0 ? $limit : (int) $total,
'total_pages' => $limit > 0 ? ceil($total / $limit) : 1
]);
}
public function get_payouts(WP_REST_Request $request)
{
global $wpdb;
$user_id = get_current_user_id();
$affiliates_table = $wpdb->prefix . 'woonoow_affiliates';
$payouts_table = $wpdb->prefix . 'woonoow_affiliate_payouts';
$affiliate = $wpdb->get_row($wpdb->prepare("SELECT id FROM $affiliates_table WHERE user_id = %d", $user_id));
if (!$affiliate) {
return rest_ensure_response([]);
}
$payouts = $wpdb->get_results($wpdb->prepare(
"SELECT id, amount, currency, method, status, notes, created_at, completed_at
FROM $payouts_table
WHERE affiliate_id = %d
ORDER BY created_at DESC",
$affiliate->id
), ARRAY_A);
return rest_ensure_response($payouts);
}
public function get_payment_details(WP_REST_Request $request)
{
global $wpdb;
$user_id = get_current_user_id();
$table = $wpdb->prefix . 'woonoow_affiliates';
$affiliate = $wpdb->get_row($wpdb->prepare(
"SELECT payment_method, payment_details FROM $table WHERE user_id = %d",
$user_id
));
if (!$affiliate) {
return new \WP_Error('not_found', 'Affiliate not found', ['status' => 404]);
}
$payment_details = $affiliate->payment_details ? json_decode($affiliate->payment_details, true) : [];
return rest_ensure_response([
'payment_method' => $affiliate->payment_method ?: '',
'payment_details' => $payment_details ?: new \stdClass()
]);
}
public function update_payment_details(WP_REST_Request $request)
{
global $wpdb;
$user_id = get_current_user_id();
$table = $wpdb->prefix . 'woonoow_affiliates';
// Get allowed payment methods from settings
$settings = get_option('woonoow_module_affiliate_settings', []);
$allowed_methods = $settings['woonoow_affiliate_payment_methods'] ?? ['bank_transfer'];
$payment_method = sanitize_text_field($request->get_param('payment_method') ?: '');
$payment_details_raw = $request->get_param('payment_details') ?: [];
// Validate payment method is allowed
if (!in_array($payment_method, $allowed_methods)) {
return new \WP_Error(
'invalid_payment_method',
'This payment method is not available. Please contact admin.',
['status' => 400]
);
}
// Sanitize payment details based on method
$sanitized_details = self::sanitize_payment_details($payment_method, $payment_details_raw);
$result = $wpdb->update(
$table,
[
'payment_method' => $payment_method,
'payment_details' => json_encode($sanitized_details)
],
['user_id' => $user_id]
);
if ($result === false) {
return new \WP_Error('update_failed', 'Failed to update payment details', ['status' => 500]);
}
return rest_ensure_response([
'success' => true,
'payment_method' => $payment_method,
'payment_details' => $sanitized_details
]);
}
/**
* Sanitize payment details based on payment method
*/
private static function sanitize_payment_details($method, $details)
{
$sanitized = [];
switch ($method) {
case 'bank_transfer':
$sanitized['bank_name'] = sanitize_text_field($details['bank_name'] ?? '');
$sanitized['account_number'] = sanitize_text_field($details['account_number'] ?? '');
$sanitized['account_holder'] = sanitize_text_field($details['account_holder'] ?? '');
$sanitized['swift_code'] = sanitize_text_field($details['swift_code'] ?? '');
$sanitized['bank_address'] = sanitize_text_field($details['bank_address'] ?? '');
break;
case 'paypal':
case 'wise':
case 'skrill':
case 'payoneer':
$sanitized['email'] = sanitize_email($details['email'] ?? '');
$sanitized['name'] = sanitize_text_field($details['name'] ?? '');
break;
case 'custom':
$sanitized['notes'] = sanitize_textarea_field($details['notes'] ?? '');
break;
}
return $sanitized;
}
// --- Collections ---
public function get_collections(WP_REST_Request $request)
{
if (class_exists('\WooNooW\Modules\Affiliate\AffiliateSettings') && !\WooNooW\Modules\Affiliate\AffiliateSettings::get_setting('woonoow_affiliate_enable_curated_collections', true)) {
return new \WP_Error('rest_forbidden', 'Curated collections are disabled.', ['status' => 403]);
}
global $wpdb;
$user_id = get_current_user_id();
$affiliate_table = $wpdb->prefix . 'woonoow_affiliates';
$collections_table = $wpdb->prefix . 'woonoow_affiliate_collections';
$affiliate = $wpdb->get_row($wpdb->prepare("SELECT id, referral_code FROM $affiliate_table WHERE user_id = %d", $user_id));
if (!$affiliate) {
return rest_ensure_response([]);
}
$collections = $wpdb->get_results($wpdb->prepare("SELECT * FROM $collections_table WHERE affiliate_id = %d ORDER BY created_at DESC", $affiliate->id), ARRAY_A);
foreach ($collections as &$collection) {
$collection['product_ids'] = $collection['product_ids'] ? json_decode($collection['product_ids'], true) : [];
$collection['link'] = site_url("/collection/{$collection['slug']}");
}
return rest_ensure_response($collections);
}
public function create_collection(WP_REST_Request $request)
{
global $wpdb;
$user_id = get_current_user_id();
$affiliate_table = $wpdb->prefix . 'woonoow_affiliates';
$collections_table = $wpdb->prefix . 'woonoow_affiliate_collections';
$affiliate = $wpdb->get_row($wpdb->prepare("SELECT id FROM $affiliate_table WHERE user_id = %d", $user_id));
if (!$affiliate) {
return new \WP_Error('not_found', 'Affiliate profile not found', ['status' => 404]);
}
$title = sanitize_text_field($request->get_param('title'));
$description = sanitize_textarea_field($request->get_param('description'));
$product_ids = $request->get_param('product_ids');
if (!is_array($product_ids)) $product_ids = [];
$product_ids = array_map('intval', $product_ids);
if (count($product_ids) > 20) {
return new \WP_Error('too_many_products', 'A collection can have a maximum of 20 products.', ['status' => 400]);
}
$slug = sanitize_title($title);
// Check unique slug for this affiliate
$existing = $wpdb->get_var($wpdb->prepare("SELECT id FROM $collections_table WHERE affiliate_id = %d AND slug = %s", $affiliate->id, $slug));
if ($existing) {
$slug .= '-' . wp_generate_password(4, false);
}
$data = [
'affiliate_id' => $affiliate->id,
'title' => $title,
'slug' => $slug,
'description' => $description,
'product_ids' => json_encode($product_ids)
];
$wpdb->insert($collections_table, $data);
$data['id'] = $wpdb->insert_id;
$data['product_ids'] = $product_ids;
return rest_ensure_response($data);
}
public function update_collection(WP_REST_Request $request)
{
global $wpdb;
$user_id = get_current_user_id();
$id = (int) $request->get_param('id');
$affiliate_table = $wpdb->prefix . 'woonoow_affiliates';
$collections_table = $wpdb->prefix . 'woonoow_affiliate_collections';
$affiliate = $wpdb->get_row($wpdb->prepare("SELECT id FROM $affiliate_table WHERE user_id = %d", $user_id));
if (!$affiliate) return new \WP_Error('unauthorized', 'Unauthorized', ['status' => 401]);
$collection = $wpdb->get_row($wpdb->prepare("SELECT id FROM $collections_table WHERE id = %d AND affiliate_id = %d", $id, $affiliate->id));
if (!$collection) return new \WP_Error('not_found', 'Collection not found', ['status' => 404]);
$title = sanitize_text_field($request->get_param('title'));
$description = sanitize_textarea_field($request->get_param('description'));
$product_ids = $request->get_param('product_ids');
if (!is_array($product_ids)) $product_ids = [];
$product_ids = array_map('intval', $product_ids);
if (count($product_ids) > 20) {
return new \WP_Error('too_many_products', 'A collection can have a maximum of 20 products.', ['status' => 400]);
}
$wpdb->update($collections_table, [
'title' => $title,
'description' => $description,
'product_ids' => json_encode($product_ids)
], ['id' => $id]);
return rest_ensure_response(['success' => true]);
}
public function delete_collection(WP_REST_Request $request)
{
global $wpdb;
$user_id = get_current_user_id();
$id = (int) $request->get_param('id');
$affiliate_table = $wpdb->prefix . 'woonoow_affiliates';
$collections_table = $wpdb->prefix . 'woonoow_affiliate_collections';
$affiliate = $wpdb->get_row($wpdb->prepare("SELECT id FROM $affiliate_table WHERE user_id = %d", $user_id));
if (!$affiliate) return new \WP_Error('unauthorized', 'Unauthorized', ['status' => 401]);
$wpdb->delete($collections_table, ['id' => $id, 'affiliate_id' => $affiliate->id]);
return rest_ensure_response(['success' => true]);
}
}