'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', ], ], ]); // 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, 'sanitize_callback' => 'sanitize_text_field', ], ], ]); } /** * Get cart contents */ public static function get_cart(WP_REST_Request $request) { if (!WC()->cart) { return new WP_Error('cart_error', 'Cart not initialized', ['status' => 500]); } 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'); $variation_id = $request->get_param('variation_id'); error_log("WooNooW Cart: Adding product {$product_id} (variation: {$variation_id}) qty: {$quantity}"); // Check if WooCommerce is available if (!function_exists('WC')) { error_log('WooNooW Cart Error: WooCommerce not loaded'); return new WP_Error('wc_not_loaded', 'WooCommerce is not loaded', ['status' => 500]); } // Initialize WooCommerce session and cart for REST API requests // WooCommerce doesn't auto-initialize these for REST API calls if (!WC()->session) { error_log('WooNooW Cart: Initializing WC session for REST API'); WC()->initialize_session(); } if (!WC()->cart) { error_log('WooNooW Cart: Initializing WC cart for REST API'); WC()->initialize_cart(); } // Set session cookie for guest users if (!WC()->session->has_session()) { WC()->session->set_customer_session_cookie(true); error_log('WooNooW Cart: Session cookie set for guest user'); } error_log('WooNooW Cart: WC Session and Cart initialized successfully'); // Validate product $product = wc_get_product($product_id); if (!$product) { error_log("WooNooW Cart Error: Product {$product_id} not found"); return new WP_Error('invalid_product', 'Product not found', ['status' => 404]); } error_log("WooNooW Cart: Product validated - {$product->get_name()} (Type: {$product->get_type()})"); // For variable products, validate the variation and get attributes $variation_attributes = []; if ($variation_id > 0) { $variation = wc_get_product($variation_id); if (!$variation) { error_log("WooNooW Cart Error: Variation {$variation_id} not found"); return new WP_Error('invalid_variation', "Variation {$variation_id} not found", ['status' => 404]); } if ($variation->get_parent_id() != $product_id) { error_log("WooNooW Cart Error: Variation {$variation_id} does not belong to product {$product_id}"); return new WP_Error('invalid_variation', "Variation does not belong to this product", ['status' => 400]); } if (!$variation->is_purchasable() || !$variation->is_in_stock()) { error_log("WooNooW Cart Error: Variation {$variation_id} is not purchasable or out of stock"); return new WP_Error('variation_not_available', "This variation is not available for purchase", ['status' => 400]); } // Get variation attributes from post meta // WooCommerce stores variation attributes as post meta with 'attribute_' prefix $variation_attributes = []; // Get parent product to know which attributes to look for $parent_product = wc_get_product($product_id); $parent_attributes = $parent_product->get_attributes(); error_log("WooNooW Cart: Parent product attributes: " . print_r(array_keys($parent_attributes), true)); // For each parent attribute, get the value from variation post meta foreach ($parent_attributes as $attribute) { if ($attribute->get_variation()) { $attribute_name = $attribute->get_name(); $meta_key = 'attribute_' . $attribute_name; // Get the value from post meta $attribute_value = get_post_meta($variation_id, $meta_key, true); error_log("WooNooW Cart: Checking attribute {$attribute_name} (meta key: {$meta_key}): {$attribute_value}"); if (!empty($attribute_value)) { // WooCommerce expects lowercase attribute names $wc_attribute_key = 'attribute_' . strtolower($attribute_name); $variation_attributes[$wc_attribute_key] = $attribute_value; } } } error_log("WooNooW Cart: Variation validated - {$variation->get_name()}"); error_log("WooNooW Cart: Variation attributes extracted: " . print_r($variation_attributes, true)); } // Clear any existing notices before adding to cart wc_clear_notices(); // Add to cart with variation attributes error_log("WooNooW Cart: Calling WC()->cart->add_to_cart({$product_id}, {$quantity}, {$variation_id}, attributes)"); $cart_item_key = WC()->cart->add_to_cart($product_id, $quantity, $variation_id, $variation_attributes); if (!$cart_item_key) { // Get WooCommerce notices to provide better error message $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(); // Clear notices after reading error_log("WooNooW Cart Error: add_to_cart returned false - {$error_message}"); error_log("WooNooW Cart Error: All WC notices: " . print_r($notices, true)); return new WP_Error('add_to_cart_failed', $error_message, ['status' => 400]); } error_log("WooNooW Cart: Product added successfully - Key: {$cart_item_key}"); 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'); if (!WC()->cart) { return new WP_Error('cart_error', 'Cart not initialized', ['status' => 500]); } // 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'); if (!WC()->cart) { return new WP_Error('cart_error', 'Cart not initialized', ['status' => 500]); } // 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); } /** * 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']; $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' => $cart_item['variation'] ?? [], ]; } // 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(), ]; } }