'GET', 'callback' => [__CLASS__, 'get_cart'], 'permission_callback' => '__return_true', ]); // Add to cart $result = register_rest_route($namespace, '/cart/add', [ 'methods' => 'POST', 'callback' => [__CLASS__, 'add_to_cart'], 'permission_callback' => function () { // Allow both logged-in and guest users return true; }, 'args' => [ 'product_id' => [ 'required' => true, 'validate_callback' => function ($param) { return is_numeric($param); }, ], 'quantity' => [ 'default' => 1, 'sanitize_callback' => 'absint', ], 'variation_id' => [ 'default' => 0, 'sanitize_callback' => 'absint', ], ], ]); // Update cart item register_rest_route($namespace, '/cart/update', [ 'methods' => 'POST', 'callback' => [__CLASS__, 'update_cart'], 'permission_callback' => function () { return true; }, 'args' => [ 'cart_item_key' => [ 'required' => true, 'sanitize_callback' => 'sanitize_text_field', ], 'quantity' => [ 'required' => true, 'sanitize_callback' => 'absint', ], ], ]); // Remove from cart register_rest_route($namespace, '/cart/remove', [ 'methods' => 'POST', 'callback' => [__CLASS__, 'remove_from_cart'], 'permission_callback' => function () { return true; }, 'args' => [ 'cart_item_key' => [ 'required' => true, 'sanitize_callback' => 'sanitize_text_field', ], ], ]); // Apply coupon register_rest_route($namespace, '/cart/apply-coupon', [ 'methods' => 'POST', 'callback' => [__CLASS__, 'apply_coupon'], 'permission_callback' => function () { return true; }, 'args' => [ 'coupon_code' => [ 'required' => true, 'sanitize_callback' => 'sanitize_text_field', ], ], ]); // Clear cart register_rest_route($namespace, '/cart/clear', [ 'methods' => 'POST', 'callback' => [__CLASS__, 'clear_cart'], 'permission_callback' => function () { return true; }, ]); // Remove coupon register_rest_route($namespace, '/cart/remove-coupon', [ 'methods' => 'POST', 'callback' => [__CLASS__, 'remove_coupon'], 'permission_callback' => function () { return true; }, 'args' => [ 'coupon_code' => [ 'required' => true, 'type' => 'string', ], ], ]); } /** * Get cart contents */ public static function get_cart(WP_REST_Request $request) { // Initialize WooCommerce session and cart for REST API requests if (!WC()->session) { WC()->initialize_session(); } if (!WC()->cart) { WC()->initialize_cart(); } // Set session cookie for guest users to persist cart if (!WC()->session->has_session()) { WC()->session->set_customer_session_cookie(true); } return new WP_REST_Response(self::format_cart(), 200); } /** * Add item to cart */ public static function add_to_cart(WP_REST_Request $request) { $product_id = $request->get_param('product_id'); $quantity = $request->get_param('quantity') ?: 1; // Default to 1 $variation_id = $request->get_param('variation_id'); // Initialize WooCommerce session and cart for REST API requests if (!WC()->session) { WC()->initialize_session(); } if (!WC()->cart) { WC()->initialize_cart(); } // CRITICAL: Set session cookie for guest users to persist cart if (!WC()->session->has_session()) { WC()->session->set_customer_session_cookie(true); } // Validate product $product = wc_get_product($product_id); if (!$product) { return new WP_Error('invalid_product', 'Product not found', ['status' => 404]); } // For variable products, get attributes from request or variation $variation_attributes = []; if ($variation_id > 0) { $variation = wc_get_product($variation_id); if (!$variation) { return new WP_Error('invalid_variation', "Variation not found", ['status' => 404]); } if ($variation->get_parent_id() != $product_id) { return new WP_Error('invalid_variation', "Variation does not belong to this product", ['status' => 400]); } if (!$variation->is_in_stock()) { return new WP_Error('variation_not_available', "This variation is out of stock", ['status' => 400]); } // Build attributes from request parameters (like WooCommerce does) // Check for attribute_* parameters in the request $params = $request->get_params(); foreach ($params as $key => $value) { if (strpos($key, 'attribute_') === 0) { $variation_attributes[sanitize_title($key)] = wc_clean($value); } } // If no attributes in request, get from variation meta directly if (empty($variation_attributes)) { $parent = wc_get_product($product_id); foreach ($parent->get_attributes() as $attr_name => $attribute) { if (!$attribute->get_variation()) continue; $meta_key = 'attribute_' . $attr_name; $value = get_post_meta($variation_id, $meta_key, true); if (!empty($value)) { $variation_attributes[$meta_key] = $value; } } } } // Clear any existing notices before adding to cart wc_clear_notices(); // Add to cart with variation attributes $cart_item_key = WC()->cart->add_to_cart($product_id, $quantity, $variation_id, $variation_attributes); if (!$cart_item_key) { $notices = wc_get_notices('error'); $error_messages = []; foreach ($notices as $notice) { $error_messages[] = is_array($notice) ? $notice['notice'] : $notice; } $error_message = !empty($error_messages) ? implode(', ', $error_messages) : 'Failed to add product to cart'; wc_clear_notices(); return new WP_Error('add_to_cart_failed', $error_message, ['status' => 400]); } return new WP_REST_Response([ 'message' => 'Product added to cart', 'cart_item_key' => $cart_item_key, 'cart' => self::format_cart(), ], 200); } /** * Update cart item quantity */ public static function update_cart(WP_REST_Request $request) { $cart_item_key = $request->get_param('cart_item_key'); $quantity = $request->get_param('quantity'); // Initialize WooCommerce session and cart for REST API requests if (!WC()->session) { WC()->initialize_session(); } if (!WC()->cart) { WC()->initialize_cart(); } if (!WC()->session->has_session()) { WC()->session->set_customer_session_cookie(true); } // Update quantity $updated = WC()->cart->set_quantity($cart_item_key, $quantity); if (!$updated) { return new WP_Error('update_failed', 'Failed to update cart item', ['status' => 400]); } return new WP_REST_Response([ 'message' => 'Cart updated', 'cart' => self::format_cart(), ], 200); } /** * Remove item from cart */ public static function remove_from_cart(WP_REST_Request $request) { $cart_item_key = $request->get_param('cart_item_key'); // Initialize WooCommerce session and cart for REST API requests if (!WC()->session) { WC()->initialize_session(); } if (!WC()->cart) { WC()->initialize_cart(); } if (!WC()->session->has_session()) { WC()->session->set_customer_session_cookie(true); } // Check if item exists in cart $cart_contents = WC()->cart->get_cart(); if (!isset($cart_contents[$cart_item_key])) { return new WP_Error('item_not_found', "Cart item not found", ['status' => 404]); } // Remove item $removed = WC()->cart->remove_cart_item($cart_item_key); if (!$removed) { return new WP_Error('remove_failed', 'Failed to remove cart item', ['status' => 400]); } return new WP_REST_Response([ 'message' => 'Item removed from cart', 'cart' => self::format_cart(), ], 200); } /** * Clear entire cart */ public static function clear_cart(WP_REST_Request $request) { // Initialize WooCommerce session and cart for REST API requests if (!WC()->session) { WC()->initialize_session(); } if (!WC()->cart) { WC()->initialize_cart(); } if (!WC()->session->has_session()) { WC()->session->set_customer_session_cookie(true); } // Empty the cart WC()->cart->empty_cart(); return new WP_REST_Response([ 'message' => 'Cart cleared', 'cart' => self::format_cart(), ], 200); } /** * Apply coupon to cart */ public static function apply_coupon(WP_REST_Request $request) { $coupon_code = $request->get_param('coupon_code'); if (!WC()->cart) { return new WP_Error('cart_error', 'Cart not initialized', ['status' => 500]); } // Apply coupon $applied = WC()->cart->apply_coupon($coupon_code); if (!$applied) { return new WP_Error('coupon_failed', 'Failed to apply coupon', ['status' => 400]); } return new WP_REST_Response([ 'message' => 'Coupon applied', 'cart' => self::format_cart(), ], 200); } /** * Remove coupon from cart */ public static function remove_coupon(WP_REST_Request $request) { $coupon_code = $request->get_param('coupon_code'); if (!WC()->cart) { return new WP_Error('cart_error', 'Cart not initialized', ['status' => 500]); } // Remove coupon $removed = WC()->cart->remove_coupon($coupon_code); if (!$removed) { return new WP_Error('remove_coupon_failed', 'Failed to remove coupon', ['status' => 400]); } return new WP_REST_Response([ 'message' => 'Coupon removed', 'cart' => self::format_cart(), ], 200); } /** * Format cart data for API response */ private static function format_cart() { $cart = WC()->cart; if (!$cart) { return null; } $items = []; foreach ($cart->get_cart() as $cart_item_key => $cart_item) { $product = $cart_item['data']; // Format variation attributes with clean names (Size instead of attribute_size) $formatted_attributes = []; if (!empty($cart_item['variation'])) { foreach ($cart_item['variation'] as $attr_key => $attr_value) { // Remove 'attribute_' prefix and capitalize $clean_key = str_replace('attribute_', '', $attr_key); $clean_key = ucfirst($clean_key); // Capitalize value $formatted_attributes[$clean_key] = ucfirst($attr_value); } } $items[] = [ 'key' => $cart_item_key, 'product_id' => $cart_item['product_id'], 'variation_id' => $cart_item['variation_id'] ?? 0, 'quantity' => $cart_item['quantity'], 'name' => $product->get_name(), 'price' => $product->get_price(), 'subtotal' => $cart_item['line_subtotal'], 'total' => $cart_item['line_total'], 'image' => wp_get_attachment_url($product->get_image_id()), 'permalink' => get_permalink($cart_item['product_id']), 'attributes' => $formatted_attributes, ]; } // Get applied coupons $coupons = []; foreach ($cart->get_applied_coupons() as $coupon_code) { $coupon = new \WC_Coupon($coupon_code); $coupons[] = [ 'code' => $coupon_code, 'discount' => $cart->get_coupon_discount_amount($coupon_code), 'type' => $coupon->get_discount_type(), ]; } return [ 'items' => $items, 'subtotal' => $cart->get_subtotal(), 'subtotal_tax' => $cart->get_subtotal_tax(), 'discount_total' => $cart->get_discount_total(), 'discount_tax' => $cart->get_discount_tax(), 'shipping_total' => $cart->get_shipping_total(), 'shipping_tax' => $cart->get_shipping_tax(), 'cart_contents_tax' => $cart->get_cart_contents_tax(), 'fee_total' => $cart->get_fee_total(), 'fee_tax' => $cart->get_fee_tax(), 'total' => $cart->get_total('edit'), 'total_tax' => $cart->get_total_tax(), 'coupons' => $coupons, 'needs_shipping' => $cart->needs_shipping(), 'needs_payment' => $cart->needs_payment(), ]; } }