diff --git a/includes/Api/CouponsController.php b/includes/Api/CouponsController.php new file mode 100644 index 0000000..4fae349 --- /dev/null +++ b/includes/Api/CouponsController.php @@ -0,0 +1,343 @@ + 'GET', + 'callback' => [__CLASS__, 'list_coupons'], + 'permission_callback' => function () { return current_user_can('manage_woocommerce'); }, + ]); + + // Get single coupon + register_rest_route('woonoow/v1', '/coupons/(?P\d+)', [ + 'methods' => 'GET', + 'callback' => [__CLASS__, 'get_coupon'], + 'permission_callback' => function () { return current_user_can('manage_woocommerce'); }, + ]); + + // Create coupon + register_rest_route('woonoow/v1', '/coupons', [ + 'methods' => 'POST', + 'callback' => [__CLASS__, 'create_coupon'], + 'permission_callback' => function () { return current_user_can('manage_woocommerce'); }, + ]); + + // Update coupon + register_rest_route('woonoow/v1', '/coupons/(?P\d+)', [ + 'methods' => 'PUT', + 'callback' => [__CLASS__, 'update_coupon'], + 'permission_callback' => function () { return current_user_can('manage_woocommerce'); }, + ]); + + // Delete coupon + register_rest_route('woonoow/v1', '/coupons/(?P\d+)', [ + 'methods' => 'DELETE', + 'callback' => [__CLASS__, 'delete_coupon'], + 'permission_callback' => function () { return current_user_can('manage_woocommerce'); }, + ]); + } + + /** + * GET /woonoow/v1/coupons + * List all coupons with pagination and filtering + */ + public static function list_coupons(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') ?: ''); + $discount_type = sanitize_text_field($request->get_param('discount_type') ?: ''); + + $args = [ + 'post_type' => 'shop_coupon', + 'post_status' => 'publish', + 'posts_per_page' => $per_page, + 'paged' => $page, + 'orderby' => 'date', + 'order' => 'DESC', + ]; + + // Search by code + if ($search) { + $args['s'] = $search; + } + + // Filter by discount type + if ($discount_type) { + $args['meta_query'] = [ + [ + 'key' => 'discount_type', + 'value' => $discount_type, + 'compare' => '=', + ], + ]; + } + + $query = new \WP_Query($args); + + $coupons = []; + foreach ($query->posts as $post) { + $coupon = new WC_Coupon($post->ID); + $coupons[] = self::format_coupon($coupon); + } + + return new WP_REST_Response([ + 'coupons' => $coupons, + 'total' => $query->found_posts, + 'page' => $page, + 'per_page' => $per_page, + 'total_pages' => $query->max_num_pages, + ], 200); + } + + /** + * GET /woonoow/v1/coupons/{id} + * Get single coupon + */ + public static function get_coupon(WP_REST_Request $request): WP_REST_Response { + $id = (int) $request->get_param('id'); + $coupon = new WC_Coupon($id); + + if (!$coupon->get_id()) { + return new WP_REST_Response([ + 'error' => 'not_found', + 'message' => __('Coupon not found', 'woonoow'), + ], 404); + } + + return new WP_REST_Response(self::format_coupon($coupon, true), 200); + } + + /** + * POST /woonoow/v1/coupons + * Create new coupon + */ + public static function create_coupon(WP_REST_Request $request): WP_REST_Response { + $data = $request->get_json_params(); + + // Validate required fields + if (empty($data['code'])) { + return new WP_REST_Response([ + 'error' => 'missing_code', + 'message' => __('Coupon code is required', 'woonoow'), + ], 400); + } + + // Check if code already exists + $existing = get_page_by_title($data['code'], OBJECT, 'shop_coupon'); + if ($existing) { + return new WP_REST_Response([ + 'error' => 'code_exists', + 'message' => __('Coupon code already exists', 'woonoow'), + ], 400); + } + + try { + $coupon = new WC_Coupon(); + self::update_coupon_data($coupon, $data); + $coupon->save(); + + return new WP_REST_Response(self::format_coupon($coupon, true), 201); + } catch (\Exception $e) { + return new WP_REST_Response([ + 'error' => 'create_failed', + 'message' => __('Failed to create coupon', 'woonoow'), + ], 500); + } + } + + /** + * PUT /woonoow/v1/coupons/{id} + * Update coupon + */ + public static function update_coupon(WP_REST_Request $request): WP_REST_Response { + $id = (int) $request->get_param('id'); + $data = $request->get_json_params(); + + $coupon = new WC_Coupon($id); + if (!$coupon->get_id()) { + return new WP_REST_Response([ + 'error' => 'not_found', + 'message' => __('Coupon not found', 'woonoow'), + ], 404); + } + + try { + self::update_coupon_data($coupon, $data); + $coupon->save(); + + return new WP_REST_Response(self::format_coupon($coupon, true), 200); + } catch (\Exception $e) { + return new WP_REST_Response([ + 'error' => 'update_failed', + 'message' => __('Failed to update coupon', 'woonoow'), + ], 500); + } + } + + /** + * DELETE /woonoow/v1/coupons/{id} + * Delete coupon + */ + public static function delete_coupon(WP_REST_Request $request): WP_REST_Response { + $id = (int) $request->get_param('id'); + $force = $request->get_param('force') === 'true'; + + $coupon = new WC_Coupon($id); + if (!$coupon->get_id()) { + return new WP_REST_Response([ + 'error' => 'not_found', + 'message' => __('Coupon not found', 'woonoow'), + ], 404); + } + + $result = wp_delete_post($id, $force); + + if (!$result) { + return new WP_REST_Response([ + 'error' => 'delete_failed', + 'message' => __('Failed to delete coupon', 'woonoow'), + ], 500); + } + + return new WP_REST_Response([ + 'success' => true, + 'id' => $id, + ], 200); + } + + /** + * Format coupon for API response + * + * @param WC_Coupon $coupon + * @param bool $full Include full details + * @return array + */ + private static function format_coupon(WC_Coupon $coupon, bool $full = false): array { + $data = [ + 'id' => $coupon->get_id(), + 'code' => $coupon->get_code(), + 'amount' => (float) $coupon->get_amount(), + 'discount_type' => $coupon->get_discount_type(), + 'description' => $coupon->get_description(), + 'usage_count' => $coupon->get_usage_count(), + 'usage_limit' => $coupon->get_usage_limit() ?: null, + 'date_expires' => $coupon->get_date_expires() ? $coupon->get_date_expires()->date('Y-m-d') : null, + ]; + + if ($full) { + $data = array_merge($data, [ + 'individual_use' => $coupon->get_individual_use(), + 'product_ids' => $coupon->get_product_ids(), + 'excluded_product_ids' => $coupon->get_excluded_product_ids(), + 'usage_limit_per_user' => $coupon->get_usage_limit_per_user() ?: null, + 'limit_usage_to_x_items' => $coupon->get_limit_usage_to_x_items() ?: null, + 'free_shipping' => $coupon->get_free_shipping(), + 'product_categories' => $coupon->get_product_categories(), + 'excluded_product_categories' => $coupon->get_excluded_product_categories(), + 'exclude_sale_items' => $coupon->get_exclude_sale_items(), + 'minimum_amount' => $coupon->get_minimum_amount() ? (float) $coupon->get_minimum_amount() : null, + 'maximum_amount' => $coupon->get_maximum_amount() ? (float) $coupon->get_maximum_amount() : null, + 'email_restrictions' => $coupon->get_email_restrictions(), + ]); + } + + return $data; + } + + /** + * Update coupon data from request + * + * @param WC_Coupon $coupon + * @param array $data + */ + private static function update_coupon_data(WC_Coupon $coupon, array $data) { + if (isset($data['code'])) { + $coupon->set_code(sanitize_text_field($data['code'])); + } + + if (isset($data['amount'])) { + $coupon->set_amount((float) $data['amount']); + } + + if (isset($data['discount_type'])) { + $coupon->set_discount_type(sanitize_text_field($data['discount_type'])); + } + + if (isset($data['description'])) { + $coupon->set_description(wp_kses_post($data['description'])); + } + + if (isset($data['date_expires'])) { + $coupon->set_date_expires($data['date_expires'] ? $data['date_expires'] : null); + } + + if (isset($data['individual_use'])) { + $coupon->set_individual_use((bool) $data['individual_use']); + } + + if (isset($data['product_ids'])) { + $coupon->set_product_ids(array_map('intval', (array) $data['product_ids'])); + } + + if (isset($data['excluded_product_ids'])) { + $coupon->set_excluded_product_ids(array_map('intval', (array) $data['excluded_product_ids'])); + } + + if (isset($data['usage_limit'])) { + $coupon->set_usage_limit($data['usage_limit'] ? (int) $data['usage_limit'] : null); + } + + if (isset($data['usage_limit_per_user'])) { + $coupon->set_usage_limit_per_user($data['usage_limit_per_user'] ? (int) $data['usage_limit_per_user'] : null); + } + + if (isset($data['limit_usage_to_x_items'])) { + $coupon->set_limit_usage_to_x_items($data['limit_usage_to_x_items'] ? (int) $data['limit_usage_to_x_items'] : null); + } + + if (isset($data['free_shipping'])) { + $coupon->set_free_shipping((bool) $data['free_shipping']); + } + + if (isset($data['product_categories'])) { + $coupon->set_product_categories(array_map('intval', (array) $data['product_categories'])); + } + + if (isset($data['excluded_product_categories'])) { + $coupon->set_excluded_product_categories(array_map('intval', (array) $data['excluded_product_categories'])); + } + + if (isset($data['exclude_sale_items'])) { + $coupon->set_exclude_sale_items((bool) $data['exclude_sale_items']); + } + + if (isset($data['minimum_amount'])) { + $coupon->set_minimum_amount($data['minimum_amount'] ? (float) $data['minimum_amount'] : ''); + } + + if (isset($data['maximum_amount'])) { + $coupon->set_maximum_amount($data['maximum_amount'] ? (float) $data['maximum_amount'] : ''); + } + + if (isset($data['email_restrictions'])) { + $coupon->set_email_restrictions(array_map('sanitize_email', (array) $data['email_restrictions'])); + } + } +} diff --git a/includes/Api/Routes.php b/includes/Api/Routes.php index e033b11..01870fc 100644 --- a/includes/Api/Routes.php +++ b/includes/Api/Routes.php @@ -18,6 +18,7 @@ use WooNooW\Api\SystemController; use WooNooW\Api\NotificationsController; use WooNooW\Api\ActivityLogController; use WooNooW\Api\ProductsController; +use WooNooW\Api\CouponsController; class Routes { public static function init() { @@ -108,6 +109,9 @@ class Routes { error_log('WooNooW Routes: Registering ProductsController routes'); ProductsController::register_routes(); error_log('WooNooW Routes: ProductsController routes registered'); + + // Coupons controller + CouponsController::register_routes(); }); } }