'GET', 'callback' => [__CLASS__, 'get_orders'], 'permission_callback' => [__CLASS__, 'check_customer_permission'], 'args' => [ 'page' => [ 'default' => 1, 'sanitize_callback' => 'absint', ], 'per_page' => [ 'default' => 10, 'sanitize_callback' => 'absint', ], ], ]); // Get single order register_rest_route($namespace, '/account/orders/(?P\d+)', [ 'methods' => 'GET', 'callback' => [__CLASS__, 'get_order'], 'permission_callback' => [__CLASS__, 'check_customer_permission'], 'args' => [ 'id' => [ 'validate_callback' => function($param) { return is_numeric($param); }, ], ], ]); // Get customer profile register_rest_route($namespace, '/account/profile', [ [ 'methods' => 'GET', 'callback' => [__CLASS__, 'get_profile'], 'permission_callback' => [__CLASS__, 'check_customer_permission'], ], [ 'methods' => 'POST', 'callback' => [__CLASS__, 'update_profile'], 'permission_callback' => [__CLASS__, 'check_customer_permission'], ], ]); // Update password register_rest_route($namespace, '/account/password', [ 'methods' => 'POST', 'callback' => [__CLASS__, 'update_password'], 'permission_callback' => [__CLASS__, 'check_customer_permission'], 'args' => [ 'current_password' => [ 'required' => true, 'sanitize_callback' => 'sanitize_text_field', ], 'new_password' => [ 'required' => true, 'sanitize_callback' => 'sanitize_text_field', ], ], ]); // Address routes moved to AddressController // Get downloads (for digital products) register_rest_route($namespace, '/account/downloads', [ 'methods' => 'GET', 'callback' => [__CLASS__, 'get_downloads'], 'permission_callback' => [__CLASS__, 'check_customer_permission'], ]); // Avatar upload register_rest_route($namespace, '/account/avatar', [ [ 'methods' => 'POST', 'callback' => [__CLASS__, 'upload_avatar'], 'permission_callback' => [__CLASS__, 'check_customer_permission'], ], [ 'methods' => 'DELETE', 'callback' => [__CLASS__, 'delete_avatar'], 'permission_callback' => [__CLASS__, 'check_customer_permission'], ], ]); // Get avatar settings (check if custom avatars are enabled) register_rest_route($namespace, '/account/avatar-settings', [ 'methods' => 'GET', 'callback' => [__CLASS__, 'get_avatar_settings'], 'permission_callback' => [__CLASS__, 'check_customer_permission'], ]); } /** * Check if user is logged in */ public static function check_customer_permission() { return is_user_logged_in(); } /** * Get customer orders */ public static function get_orders(WP_REST_Request $request) { $customer_id = get_current_user_id(); $page = $request->get_param('page'); $per_page = $request->get_param('per_page'); $args = [ 'customer_id' => $customer_id, 'limit' => $per_page, 'page' => $page, 'orderby' => 'date', 'order' => 'DESC', ]; $orders = wc_get_orders($args); $formatted_orders = array_map(function($order) { return self::format_order($order); }, $orders); // Get total count $total_args = [ 'customer_id' => $customer_id, 'return' => 'ids', ]; $total = count(wc_get_orders($total_args)); return new WP_REST_Response([ 'orders' => $formatted_orders, 'total' => $total, 'total_pages' => ceil($total / $per_page), 'page' => $page, 'per_page' => $per_page, ], 200); } /** * Get single order */ public static function get_order(WP_REST_Request $request) { $order_id = $request->get_param('id'); $customer_id = get_current_user_id(); $order = wc_get_order($order_id); if (!$order) { return new WP_Error('order_not_found', 'Order not found', ['status' => 404]); } // Check if order belongs to customer if ($order->get_customer_id() !== $customer_id) { return new WP_Error('forbidden', 'You do not have permission to view this order', ['status' => 403]); } return new WP_REST_Response(self::format_order($order, true), 200); } /** * Get customer profile */ public static function get_profile(WP_REST_Request $request) { $user_id = get_current_user_id(); $user = get_userdata($user_id); if (!$user) { return new WP_Error('user_not_found', 'User not found', ['status' => 404]); } return new WP_REST_Response([ 'id' => $user->ID, 'email' => $user->user_email, 'first_name' => get_user_meta($user_id, 'first_name', true), 'last_name' => get_user_meta($user_id, 'last_name', true), 'username' => $user->user_login, ], 200); } /** * Update customer profile */ public static function update_profile(WP_REST_Request $request) { $user_id = get_current_user_id(); $first_name = $request->get_param('first_name'); $last_name = $request->get_param('last_name'); $email = $request->get_param('email'); // Update user meta if ($first_name !== null) { update_user_meta($user_id, 'first_name', sanitize_text_field($first_name)); } if ($last_name !== null) { update_user_meta($user_id, 'last_name', sanitize_text_field($last_name)); } // Update email if changed if ($email !== null && is_email($email)) { $user = get_userdata($user_id); if ($user->user_email !== $email) { wp_update_user([ 'ID' => $user_id, 'user_email' => $email, ]); } } return new WP_REST_Response([ 'message' => 'Profile updated successfully', ], 200); } /** * Upload customer avatar */ public static function upload_avatar(WP_REST_Request $request) { // Check if custom avatars are enabled (stored as 'yes' or 'no') $allow_custom_avatar = get_option('woonoow_allow_custom_avatar', 'no') === 'yes'; if (!$allow_custom_avatar) { return new WP_Error('avatar_disabled', 'Custom avatars are not enabled', ['status' => 403]); } $user_id = get_current_user_id(); // Check for file data (base64 or URL) $avatar_data = $request->get_param('avatar'); $avatar_url = $request->get_param('avatar_url'); if ($avatar_url) { // Avatar URL provided (from media library) update_user_meta($user_id, 'woonoow_custom_avatar', esc_url_raw($avatar_url)); return new WP_REST_Response([ 'success' => true, 'message' => 'Avatar updated successfully', 'avatar_url' => $avatar_url, ], 200); } if (!$avatar_data) { return new WP_Error('no_avatar', 'No avatar data provided', ['status' => 400]); } // Handle base64 image upload if (strpos($avatar_data, 'data:image') === 0) { // Extract base64 data $parts = explode(',', $avatar_data); if (count($parts) !== 2) { return new WP_Error('invalid_data', 'Invalid image data format', ['status' => 400]); } $image_data = base64_decode($parts[1]); // Determine file extension from mime type preg_match('/data:image\/(\w+);/', $parts[0], $matches); $extension = $matches[1] ?? 'png'; // Validate extension $allowed = ['jpg', 'jpeg', 'png', 'gif', 'webp']; if (!in_array(strtolower($extension), $allowed)) { return new WP_Error('invalid_type', 'Invalid image type. Allowed: jpg, png, gif, webp', ['status' => 400]); } // Create upload directory $upload_dir = wp_upload_dir(); $avatar_dir = $upload_dir['basedir'] . '/woonoow-avatars'; if (!file_exists($avatar_dir)) { wp_mkdir_p($avatar_dir); } // Generate unique filename $filename = 'avatar-' . $user_id . '-' . time() . '.' . $extension; $filepath = $avatar_dir . '/' . $filename; // Delete old avatar if exists $old_avatar = get_user_meta($user_id, 'woonoow_custom_avatar', true); if ($old_avatar) { $old_path = str_replace($upload_dir['baseurl'], $upload_dir['basedir'], $old_avatar); if (file_exists($old_path)) { unlink($old_path); } } // Save new avatar if (file_put_contents($filepath, $image_data) === false) { return new WP_Error('upload_failed', 'Failed to save avatar', ['status' => 500]); } // Get URL $avatar_url = $upload_dir['baseurl'] . '/woonoow-avatars/' . $filename; // Save to user meta update_user_meta($user_id, 'woonoow_custom_avatar', $avatar_url); return new WP_REST_Response([ 'success' => true, 'message' => 'Avatar uploaded successfully', 'avatar_url' => $avatar_url, ], 200); } return new WP_Error('invalid_data', 'Invalid avatar data', ['status' => 400]); } /** * Delete customer avatar */ public static function delete_avatar(WP_REST_Request $request) { $user_id = get_current_user_id(); // Get current avatar $avatar_url = get_user_meta($user_id, 'woonoow_custom_avatar', true); if ($avatar_url) { // Try to delete the file $upload_dir = wp_upload_dir(); $filepath = str_replace($upload_dir['baseurl'], $upload_dir['basedir'], $avatar_url); if (file_exists($filepath)) { unlink($filepath); } // Remove from user meta delete_user_meta($user_id, 'woonoow_custom_avatar'); } return new WP_REST_Response([ 'success' => true, 'message' => 'Avatar removed successfully', ], 200); } /** * Get avatar settings */ public static function get_avatar_settings(WP_REST_Request $request) { $user_id = get_current_user_id(); // Use correct option key (stored as 'yes' or 'no') $allow_custom_avatar = get_option('woonoow_allow_custom_avatar', 'no') === 'yes'; return new WP_REST_Response([ 'allow_custom_avatar' => $allow_custom_avatar, 'current_avatar' => get_user_meta($user_id, 'woonoow_custom_avatar', true) ?: null, 'gravatar_url' => get_avatar_url($user_id), ], 200); } /** * Update password */ public static function update_password(WP_REST_Request $request) { $user_id = get_current_user_id(); $current_password = $request->get_param('current_password'); $new_password = $request->get_param('new_password'); $user = get_userdata($user_id); // Verify current password if (!wp_check_password($current_password, $user->user_pass, $user_id)) { return new WP_Error('invalid_password', 'Current password is incorrect', ['status' => 400]); } // Update password wp_set_password($new_password, $user_id); return new WP_REST_Response([ 'message' => 'Password updated successfully', ], 200); } /** * Get customer addresses */ public static function get_addresses(WP_REST_Request $request) { $customer_id = get_current_user_id(); $customer = new \WC_Customer($customer_id); return new WP_REST_Response([ '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(), 'email' => $customer->get_billing_email(), 'phone' => $customer->get_billing_phone(), ], '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(), ], ], 200); } /** * Update customer addresses */ public static function update_addresses(WP_REST_Request $request) { $customer_id = get_current_user_id(); $customer = new \WC_Customer($customer_id); $billing = $request->get_param('billing'); $shipping = $request->get_param('shipping'); // Update billing address if ($billing) { foreach ($billing as $key => $value) { $method = 'set_billing_' . $key; if (method_exists($customer, $method)) { $customer->$method(sanitize_text_field($value)); } } } // Update shipping address if ($shipping) { foreach ($shipping as $key => $value) { $method = 'set_shipping_' . $key; if (method_exists($customer, $method)) { $customer->$method(sanitize_text_field($value)); } } } $customer->save(); return new WP_REST_Response([ 'message' => 'Addresses updated successfully', ], 200); } /** * Get customer downloads */ public static function get_downloads(WP_REST_Request $request) { $customer_id = get_current_user_id(); $downloads = wc_get_customer_available_downloads($customer_id); return new WP_REST_Response($downloads, 200); } /** * Format order data for API response */ private static function format_order($order, $detailed = false) { $payment_title = $order->get_payment_method_title(); if (empty($payment_title)) { $payment_title = $order->get_payment_method() ?: 'Not specified'; } $data = [ 'id' => $order->get_id(), 'order_number' => $order->get_order_number(), 'status' => $order->get_status(), 'date' => $order->get_date_created()->date('Y-m-d H:i:s'), 'total' => html_entity_decode(strip_tags(wc_price($order->get_total()))), 'currency' => $order->get_currency(), 'payment_method_title' => $payment_title, ]; if ($detailed) { $items = $order->get_items(); $data['items'] = is_array($items) ? array_values(array_map(function($item) { $product = $item->get_product(); return [ 'id' => $item->get_id(), 'name' => $item->get_name(), 'quantity' => $item->get_quantity(), 'total' => html_entity_decode(strip_tags(wc_price($item->get_total()))), 'image' => $product ? wp_get_attachment_url($product->get_image_id()) : '', ]; }, $items)) : []; // Check if order needs shipping (not virtual-only) $needs_shipping = false; foreach ($order->get_items() as $item) { $product = $item->get_product(); if ($product && !$product->is_virtual()) { $needs_shipping = true; break; } } $data['billing'] = $order->get_address('billing'); $data['shipping'] = $order->get_address('shipping'); $data['needs_shipping'] = $needs_shipping; $data['subtotal'] = html_entity_decode(strip_tags(wc_price($order->get_subtotal()))); $data['shipping_total'] = html_entity_decode(strip_tags(wc_price($order->get_shipping_total()))); $data['tax_total'] = html_entity_decode(strip_tags(wc_price($order->get_total_tax()))); $data['discount_total'] = html_entity_decode(strip_tags(wc_price($order->get_discount_total()))); // Shipping lines with method details $shipping_lines = []; foreach ($order->get_shipping_methods() as $shipping_item) { $shipping_lines[] = [ 'id' => $shipping_item->get_id(), 'method_title' => $shipping_item->get_method_title(), 'method_id' => $shipping_item->get_method_id(), 'total' => html_entity_decode(strip_tags(wc_price($shipping_item->get_total()))), ]; } $data['shipping_lines'] = $shipping_lines; // Tracking info (from various shipping tracking plugins) $tracking_number = $order->get_meta('_tracking_number') ?: $order->get_meta('_wc_shipment_tracking_items') ?: $order->get_meta('_rajaongkir_awb_number') ?: ''; $tracking_url = $order->get_meta('_tracking_url') ?: $order->get_meta('_rajaongkir_tracking_url') ?: ''; // Handle WooCommerce Shipment Tracking plugin format (array) if (is_array($tracking_number) && !empty($tracking_number)) { $first_tracking = reset($tracking_number); $tracking_number = $first_tracking['tracking_number'] ?? ''; $tracking_url = $first_tracking['tracking_url'] ?? $tracking_url; } $data['tracking_number'] = $tracking_number; $data['tracking_url'] = $tracking_url; } return $data; } }