'ajaxGetCart', 'arrayfunnel_update_item' => 'ajaxUpdateItem', 'arrayfunnel_remove_item' => 'ajaxRemoveItem', 'arrayfunnel_apply_coupon' => 'ajaxApplyCoupon', 'arrayfunnel_remove_coupon' => 'ajaxRemoveCoupon', ]; foreach ( $ajax_actions as $action => $method ) { add_action( "wp_ajax_{$action}", [ $this, $method ] ); add_action( "wp_ajax_nopriv_{$action}", [ $this, $method ] ); } } public function renderCartDrawer(): void { if ( is_cart() || is_checkout() || is_account_page() ) { return; } if ( ! function_exists( 'WC' ) || ! WC()->cart ) { return; } require dirname( __DIR__ ) . '/views/side-cart.php'; } public function updateCartFragments( array $fragments ): array { if ( ! function_exists( 'WC' ) || ! WC()->cart ) { return $fragments; } ob_start(); $this->renderCartItemsHtml(); $fragments['.arrayfunnel-cart-items'] = ob_get_clean(); $fragments['.arrayfunnel-cart-count'] = '' . esc_html( (string) WC()->cart->get_cart_contents_count() ) . ''; $fragments['.arrayfunnel-cart-total'] = '' . wp_kses_post( WC()->cart->get_cart_total() ) . ''; return $fragments; } /** Return the current cart payload to the side cart. */ public function ajaxGetCart(): void { check_ajax_referer( 'arrayfunnel_cart_nonce', 'arrayfunnel_nonce' ); $this->requireCart(); WC()->cart->calculate_totals(); wp_send_json_success( $this->getCartData() ); } public function ajaxUpdateItem(): void { check_ajax_referer( 'arrayfunnel_cart_nonce', 'arrayfunnel_nonce' ); $this->requireCart(); $cart_item_key = sanitize_text_field( wp_unslash( $_POST['arrayfunnel_cart_item_key'] ?? '' ) ); $quantity = absint( $_POST['arrayfunnel_quantity'] ?? 0 ); if ( empty( $cart_item_key ) ) { wp_send_json_error( __( 'Invalid cart item.', 'arrayfunnel' ), 400 ); } if ( ! $this->cartItemExists( $cart_item_key ) ) { wp_send_json_error( __( 'Invalid cart item.', 'arrayfunnel' ), 400 ); } if ( $quantity === 0 ) { WC()->cart->remove_cart_item( $cart_item_key ); } else { WC()->cart->set_quantity( $cart_item_key, $quantity, true ); } WC()->cart->calculate_totals(); wp_send_json_success( $this->getCartData() ); } public function ajaxRemoveItem(): void { check_ajax_referer( 'arrayfunnel_cart_nonce', 'arrayfunnel_nonce' ); $this->requireCart(); $cart_item_key = sanitize_text_field( wp_unslash( $_POST['arrayfunnel_cart_item_key'] ?? '' ) ); if ( empty( $cart_item_key ) ) { wp_send_json_error( __( 'Invalid cart item.', 'arrayfunnel' ), 400 ); } if ( ! $this->cartItemExists( $cart_item_key ) ) { wp_send_json_error( __( 'Invalid cart item.', 'arrayfunnel' ), 400 ); } WC()->cart->remove_cart_item( $cart_item_key ); WC()->cart->calculate_totals(); wp_send_json_success( $this->getCartData() ); } public function ajaxApplyCoupon(): void { check_ajax_referer( 'arrayfunnel_cart_nonce', 'arrayfunnel_nonce' ); $this->requireCart(); $coupon_code = sanitize_text_field( wp_unslash( $_POST['arrayfunnel_coupon_code'] ?? '' ) ); if ( empty( $coupon_code ) ) { wp_send_json_error( __( 'Please enter a coupon code.', 'arrayfunnel' ), 400 ); } // Clear existing notices wc_clear_notices(); $result = WC()->cart->apply_coupon( $coupon_code ); if ( $result ) { WC()->cart->calculate_totals(); wp_send_json_success( array_merge( $this->getCartData(), [ 'message' => __( 'Coupon applied successfully!', 'arrayfunnel' ) ] ) ); } else { $notices = wc_get_notices( 'error' ); $message = ! empty( $notices ) ? $notices[0]['notice'] : __( 'Invalid coupon code.', 'arrayfunnel' ); $plain_message = html_entity_decode( wp_strip_all_tags( (string) $message ), ENT_QUOTES, 'UTF-8' ); wp_send_json_error( $plain_message, 400 ); } } public function ajaxRemoveCoupon(): void { check_ajax_referer( 'arrayfunnel_cart_nonce', 'arrayfunnel_nonce' ); $this->requireCart(); $coupon_code = sanitize_text_field( wp_unslash( $_POST['arrayfunnel_coupon_code'] ?? '' ) ); WC()->cart->remove_coupon( $coupon_code ); WC()->cart->calculate_totals(); wp_send_json_success( $this->getCartData() ); } /** Build the current cart payload. */ public function getCartData(): array { $cart = WC()->cart; $items = []; $free_goal = (float) get_option( 'arrayfunnel_side_cart_free_shipping', 0 ); $cart_total = (float) $cart->get_cart_contents_total(); foreach ( $cart->get_cart() as $key => $item ) { $product = $item['data']; if ( ! $product ) { continue; } $image_id = $product->get_image_id(); $image_url = $image_id ? wp_get_attachment_image_url( $image_id, 'thumbnail' ) : wc_placeholder_img_src( 'thumbnail' ); $items[] = [ 'key' => $key, 'product_id' => $item['product_id'], 'variation_id' => $item['variation_id'] ?? 0, 'name' => $product->get_name(), 'image' => $image_url, 'price_value' => (float) $product->get_price(), 'line_total_value' => (float) $product->get_price() * (float) $item['quantity'], 'quantity' => $item['quantity'], 'max_qty' => $product->get_manage_stock() ? $product->get_stock_quantity() : 99, 'permalink' => get_permalink( $item['product_id'] ), 'variation' => $this->formatVariationData( $item['variation'] ?? [] ), ]; } $free_shipping_progress = $free_goal > 0 ? min( 100, round( ( $cart_total / $free_goal ) * 100 ) ) : 0; $free_shipping_remaining = $free_goal > 0 ? max( 0, $free_goal - $cart_total ) : 0; return [ 'items' => $items, 'item_count' => $cart->get_cart_contents_count(), 'subtotal_value' => (float) $cart->get_subtotal(), 'total_value' => (float) $cart->get_total( 'edit' ), 'coupons' => $cart->get_applied_coupons(), 'discount_value' => (float) $cart->get_discount_total(), 'free_shipping_goal' => $free_goal, 'free_shipping_progress' => $free_shipping_progress, 'free_shipping_remaining_value' => $free_shipping_remaining, 'free_shipping_unlocked' => $free_goal > 0 && $cart_total >= $free_goal, 'currency' => [ 'symbol' => get_woocommerce_currency_symbol(), 'code' => get_woocommerce_currency(), 'position' => get_option( 'woocommerce_currency_pos' ), ], ]; } private function formatVariationData( array $variation ): array { $formatted = []; foreach ( $variation as $key => $value ) { $taxonomy = str_replace( 'attribute_', '', $key ); $label = wc_attribute_label( $taxonomy ); $formatted[] = [ 'label' => $label, 'value' => $value ]; } return $formatted; } /** Abort the request when WooCommerce has no cart to act on. */ private function requireCart(): void { if ( ! function_exists( 'WC' ) || ! WC()->cart ) { wp_send_json_error( __( 'Cart is unavailable.', 'arrayfunnel' ), 400 ); } } private function cartItemExists( string $cart_item_key ): bool { $cart_item = WC()->cart ? WC()->cart->get_cart_item( $cart_item_key ) : null; return is_array( $cart_item ) && ! empty( $cart_item ); } private function renderCartItemsHtml(): void { // Used for WC fragments - outputs cart item list echo '