'GET', 'callback' => [__CLASS__, 'list_customers'], 'permission_callback' => function () { return current_user_can('list_users'); }, ]); // Get single customer register_rest_route('woonoow/v1', '/customers/(?P\d+)', [ 'methods' => 'GET', 'callback' => [__CLASS__, 'get_customer'], 'permission_callback' => function () { return current_user_can('list_users'); }, ]); // Create customer register_rest_route('woonoow/v1', '/customers', [ 'methods' => 'POST', 'callback' => [__CLASS__, 'create_customer'], 'permission_callback' => function () { return current_user_can('create_users'); }, ]); // Update customer register_rest_route('woonoow/v1', '/customers/(?P\d+)', [ 'methods' => 'PUT', 'callback' => [__CLASS__, 'update_customer'], 'permission_callback' => function () { return current_user_can('edit_users'); }, ]); // Delete customer register_rest_route('woonoow/v1', '/customers/(?P\d+)', [ 'methods' => 'DELETE', 'callback' => [__CLASS__, 'delete_customer'], 'permission_callback' => function () { return current_user_can('delete_users'); }, ]); // Search customers (for autocomplete) register_rest_route('woonoow/v1', '/customers/search', [ 'methods' => 'GET', 'callback' => [__CLASS__, 'search_customers'], 'permission_callback' => function () { return current_user_can('list_users'); }, ]); } /** * GET /woonoow/v1/customers * List all customers with pagination and filtering */ public static function list_customers(WP_REST_Request $request): WP_REST_Response { $page = max(1, (int) $request->get_param('page') ?: 1); $per_page = max(1, min(100, (int) $request->get_param('per_page') ?: 20)); $search = sanitize_text_field($request->get_param('search') ?: ''); $role = sanitize_text_field($request->get_param('role') ?: 'customer'); $args = [ 'role' => $role, 'number' => $per_page, 'paged' => $page, 'orderby' => 'registered', 'order' => 'DESC', ]; // Search by name or email if ($search) { $args['search'] = '*' . $search . '*'; $args['search_columns'] = ['user_login', 'user_email', 'display_name']; } $user_query = new \WP_User_Query($args); $users = $user_query->get_results(); $total = $user_query->get_total(); $customers = []; foreach ($users as $user) { $customers[] = self::format_customer($user, true); // Include stats for list view } return new WP_REST_Response([ 'data' => $customers, 'pagination' => [ 'total' => $total, 'total_pages' => ceil($total / $per_page), 'current' => $page, 'per_page' => $per_page, ], ]); } /** * GET /woonoow/v1/customers/{id} * Get single customer by ID */ public static function get_customer(WP_REST_Request $request): WP_REST_Response|WP_Error { $id = (int) $request->get_param('id'); $user = get_user_by('ID', $id); if (!$user) { return new WP_Error('customer_not_found', __('Customer not found', 'woonoow'), ['status' => 404]); } return new WP_REST_Response(self::format_customer($user, true)); } /** * POST /woonoow/v1/customers * Create new customer */ public static function create_customer(WP_REST_Request $request): WP_REST_Response|WP_Error { $data = $request->get_json_params(); // Validate required fields if (empty($data['email'])) { return new WP_Error('missing_email', __('Email is required', 'woonoow'), ['status' => 400]); } if (empty($data['first_name'])) { return new WP_Error('missing_first_name', __('First name is required', 'woonoow'), ['status' => 400]); } if (empty($data['last_name'])) { return new WP_Error('missing_last_name', __('Last name is required', 'woonoow'), ['status' => 400]); } // Check if email already exists if (email_exists($data['email'])) { return new WP_Error('email_exists', __('Email already exists', 'woonoow'), ['status' => 400]); } // Generate username from email if not provided $username = !empty($data['username']) ? sanitize_user($data['username']) : sanitize_user($data['email']); // Check if username exists if (username_exists($username)) { // Append number to make it unique $base_username = $username; $counter = 1; while (username_exists($username)) { $username = $base_username . $counter; $counter++; } } // Generate password if not provided $password = !empty($data['password']) ? $data['password'] : wp_generate_password(12, true, true); // Create user $user_id = wp_create_user($username, $password, sanitize_email($data['email'])); if (is_wp_error($user_id)) { return new WP_Error('create_failed', $user_id->get_error_message(), ['status' => 500]); } // Set role to customer $user = new WP_User($user_id); $user->set_role('customer'); // Update user meta wp_update_user([ 'ID' => $user_id, 'first_name' => sanitize_text_field($data['first_name']), 'last_name' => sanitize_text_field($data['last_name']), 'display_name' => sanitize_text_field($data['first_name'] . ' ' . $data['last_name']), ]); // Update WooCommerce customer data $customer = new WC_Customer($user_id); // Billing address if (!empty($data['billing'])) { $billing = $data['billing']; $customer->set_billing_first_name(sanitize_text_field($billing['first_name'] ?? $data['first_name'])); $customer->set_billing_last_name(sanitize_text_field($billing['last_name'] ?? $data['last_name'])); $customer->set_billing_company(sanitize_text_field($billing['company'] ?? '')); $customer->set_billing_address_1(sanitize_text_field($billing['address_1'] ?? '')); $customer->set_billing_address_2(sanitize_text_field($billing['address_2'] ?? '')); $customer->set_billing_city(sanitize_text_field($billing['city'] ?? '')); $customer->set_billing_state(sanitize_text_field($billing['state'] ?? '')); $customer->set_billing_postcode(sanitize_text_field($billing['postcode'] ?? '')); $customer->set_billing_country(sanitize_text_field($billing['country'] ?? '')); $customer->set_billing_phone(sanitize_text_field($billing['phone'] ?? '')); } // Shipping address if (!empty($data['shipping'])) { $shipping = $data['shipping']; $customer->set_shipping_first_name(sanitize_text_field($shipping['first_name'] ?? $data['first_name'])); $customer->set_shipping_last_name(sanitize_text_field($shipping['last_name'] ?? $data['last_name'])); $customer->set_shipping_company(sanitize_text_field($shipping['company'] ?? '')); $customer->set_shipping_address_1(sanitize_text_field($shipping['address_1'] ?? '')); $customer->set_shipping_address_2(sanitize_text_field($shipping['address_2'] ?? '')); $customer->set_shipping_city(sanitize_text_field($shipping['city'] ?? '')); $customer->set_shipping_state(sanitize_text_field($shipping['state'] ?? '')); $customer->set_shipping_postcode(sanitize_text_field($shipping['postcode'] ?? '')); $customer->set_shipping_country(sanitize_text_field($shipping['country'] ?? '')); } $customer->save(); // Send new account email if requested if (!empty($data['send_email'])) { wp_new_user_notification($user_id, null, 'both'); } return new WP_REST_Response(self::format_customer(get_user_by('ID', $user_id), true), 201); } /** * PUT /woonoow/v1/customers/{id} * Update existing customer */ public static function update_customer(WP_REST_Request $request): WP_REST_Response|WP_Error { $id = (int) $request->get_param('id'); $data = $request->get_json_params(); $user = get_user_by('ID', $id); if (!$user) { return new WP_Error('customer_not_found', __('Customer not found', 'woonoow'), ['status' => 404]); } // Update user data $user_data = ['ID' => $id]; if (!empty($data['email'])) { // Check if email is changing and if new email exists if ($data['email'] !== $user->user_email && email_exists($data['email'])) { return new WP_Error('email_exists', __('Email already exists', 'woonoow'), ['status' => 400]); } $user_data['user_email'] = sanitize_email($data['email']); } if (!empty($data['first_name'])) { $user_data['first_name'] = sanitize_text_field($data['first_name']); } if (!empty($data['last_name'])) { $user_data['last_name'] = sanitize_text_field($data['last_name']); } if (isset($data['first_name']) || isset($data['last_name'])) { $first = $data['first_name'] ?? get_user_meta($id, 'first_name', true); $last = $data['last_name'] ?? get_user_meta($id, 'last_name', true); $user_data['display_name'] = trim($first . ' ' . $last); } if (!empty($data['password'])) { $user_data['user_pass'] = $data['password']; } $result = wp_update_user($user_data); if (is_wp_error($result)) { return new WP_Error('update_failed', $result->get_error_message(), ['status' => 500]); } // Update WooCommerce customer data $customer = new WC_Customer($id); // Billing address if (!empty($data['billing'])) { $billing = $data['billing']; if (isset($billing['first_name'])) $customer->set_billing_first_name(sanitize_text_field($billing['first_name'])); if (isset($billing['last_name'])) $customer->set_billing_last_name(sanitize_text_field($billing['last_name'])); if (isset($billing['company'])) $customer->set_billing_company(sanitize_text_field($billing['company'])); if (isset($billing['address_1'])) $customer->set_billing_address_1(sanitize_text_field($billing['address_1'])); if (isset($billing['address_2'])) $customer->set_billing_address_2(sanitize_text_field($billing['address_2'])); if (isset($billing['city'])) $customer->set_billing_city(sanitize_text_field($billing['city'])); if (isset($billing['state'])) $customer->set_billing_state(sanitize_text_field($billing['state'])); if (isset($billing['postcode'])) $customer->set_billing_postcode(sanitize_text_field($billing['postcode'])); if (isset($billing['country'])) $customer->set_billing_country(sanitize_text_field($billing['country'])); if (isset($billing['phone'])) $customer->set_billing_phone(sanitize_text_field($billing['phone'])); } // Shipping address if (!empty($data['shipping'])) { $shipping = $data['shipping']; if (isset($shipping['first_name'])) $customer->set_shipping_first_name(sanitize_text_field($shipping['first_name'])); if (isset($shipping['last_name'])) $customer->set_shipping_last_name(sanitize_text_field($shipping['last_name'])); if (isset($shipping['company'])) $customer->set_shipping_company(sanitize_text_field($shipping['company'])); if (isset($shipping['address_1'])) $customer->set_shipping_address_1(sanitize_text_field($shipping['address_1'])); if (isset($shipping['address_2'])) $customer->set_shipping_address_2(sanitize_text_field($shipping['address_2'])); if (isset($shipping['city'])) $customer->set_shipping_city(sanitize_text_field($shipping['city'])); if (isset($shipping['state'])) $customer->set_shipping_state(sanitize_text_field($shipping['state'])); if (isset($shipping['postcode'])) $customer->set_shipping_postcode(sanitize_text_field($shipping['postcode'])); if (isset($shipping['country'])) $customer->set_shipping_country(sanitize_text_field($shipping['country'])); } $customer->save(); return new WP_REST_Response(self::format_customer(get_user_by('ID', $id), true)); } /** * DELETE /woonoow/v1/customers/{id} * Delete customer */ public static function delete_customer(WP_REST_Request $request): WP_REST_Response|WP_Error { $id = (int) $request->get_param('id'); $user = get_user_by('ID', $id); if (!$user) { return new WP_Error('customer_not_found', __('Customer not found', 'woonoow'), ['status' => 404]); } // Don't allow deleting current user if ($id === get_current_user_id()) { return new WP_Error('cannot_delete_self', __('You cannot delete your own account', 'woonoow'), ['status' => 403]); } require_once(ABSPATH . 'wp-admin/includes/user.php'); $result = wp_delete_user($id); if (!$result) { return new WP_Error('delete_failed', __('Failed to delete customer', 'woonoow'), ['status' => 500]); } return new WP_REST_Response(['success' => true]); } /** * GET /woonoow/v1/customers/search * Search customers for autocomplete */ public static function search_customers(WP_REST_Request $request): WP_REST_Response { $search = sanitize_text_field($request->get_param('q') ?: ''); $limit = max(1, min(50, (int) $request->get_param('limit') ?: 10)); if (empty($search)) { return new WP_REST_Response([]); } $args = [ 'role' => 'customer', 'number' => $limit, 'search' => '*' . $search . '*', 'search_columns' => ['user_login', 'user_email', 'display_name'], ]; $user_query = new \WP_User_Query($args); $users = $user_query->get_results(); $results = []; foreach ($users as $user) { $results[] = [ 'id' => $user->ID, 'name' => $user->display_name, 'email' => $user->user_email, ]; } return new WP_REST_Response($results); } /** * Format customer data for API response */ private static function format_customer(WP_User $user, bool $detailed = false): array { $customer = new WC_Customer($user->ID); $data = [ 'id' => $user->ID, 'username' => $user->user_login, 'email' => $user->user_email, 'first_name' => $user->first_name, 'last_name' => $user->last_name, 'display_name' => $user->display_name, 'registered' => $user->user_registered, 'role' => !empty($user->roles) ? $user->roles[0] : '', ]; if ($detailed) { $data['billing'] = [ 'first_name' => $customer->get_billing_first_name(), 'last_name' => $customer->get_billing_last_name(), 'company' => $customer->get_billing_company(), 'address_1' => $customer->get_billing_address_1(), 'address_2' => $customer->get_billing_address_2(), 'city' => $customer->get_billing_city(), 'state' => $customer->get_billing_state(), 'postcode' => $customer->get_billing_postcode(), 'country' => $customer->get_billing_country(), 'phone' => $customer->get_billing_phone(), ]; $data['shipping'] = [ 'first_name' => $customer->get_shipping_first_name(), 'last_name' => $customer->get_shipping_last_name(), 'company' => $customer->get_shipping_company(), 'address_1' => $customer->get_shipping_address_1(), 'address_2' => $customer->get_shipping_address_2(), 'city' => $customer->get_shipping_city(), 'state' => $customer->get_shipping_state(), 'postcode' => $customer->get_shipping_postcode(), 'country' => $customer->get_shipping_country(), ]; // Get order stats $orders = wc_get_orders([ 'customer_id' => $user->ID, 'limit' => -1, 'status' => ['wc-completed', 'wc-processing'], ]); $total_spent = 0; foreach ($orders as $order) { $total_spent += $order->get_total(); } $data['stats'] = [ 'total_orders' => count($orders), 'total_spent' => $total_spent, ]; } return $data; } }