Search code examples
phpwordpresswoocommerceproductcart

Set price to zero after specific product is added programmatically in WooCommerce


I added the product to the cart from the product ID I retrieve from Ajax, but for some reason I cannot add the custom price to the added to cart product.

I tried adding product id as global variable to use variable in other function but I was unable to set the product to 0. This is what my code looks like in the end.

JS:

<script>
 const selectedProductId = getProdVal(randomDegree);
  const isProductInCart = isProductAlreadyInCart(selectedProductId);

  if (!productAdded && !isProductInCart) {
    jQuery.ajax({
      url: <?php echo json_encode(admin_url('admin-ajax.php')) ?>,
      type: 'POST',
      data: {
        action: 'add_product_to_cart',
        product_id: selectedProductId,
      },
      success: function(response) {
        console.log(response);
        productAdded = true;
        spinBtn.disabled = false;
      },
      error: function(error) {
        console.error(error);
        spinBtn.disabled = false;
      }
    });
    jQuery.ajax({
      url: <?php echo json_encode(admin_url('admin-ajax.php')) ?>,
      type: 'POST',
      data: {
        action: 'custom_set_cart_item_price',
        product_id: selectedProductId,
      },
      success: function(response) {
        console.log(response);
      },
      error: function(error) {
        console.error(error);
      }
    });
  }
</script>

PHP:

function add_product_to_cart()
{
  if (isset($_POST['product_id'])) {
    $product_id = intval($_POST['product_id']);
    if (!in_array($product_id, get_product_ids_from_cart())) {
      WC()->cart->add_to_cart($product_id, 1, 0, [], []);
    }
    echo json_encode(array('success' => true, 'message' => 'Product added to cart.'));
  } else {
    echo json_encode(array('success' => false, 'message' => 'Product ID is missing.'));
  }

  exit;
}
add_action('wp_ajax_add_product_to_cart', 'add_product_to_cart');
add_action('wp_ajax_nopriv_add_product_to_cart', 'add_product_to_cart');
add_action('wp_ajax_custom_set_cart_item_price', 'custom_set_cart_item_price');
add_action('wp_ajax_nopriv_custom_set_cart_item_price', 'custom_set_cart_item_price');
function custom_set_cart_item_price($cart) {
  if (isset($_POST['product_id'])) {
    $product_id = intval($_POST['product_id']);
    $custom_price = 0.00; 
    foreach ($cart->get_cart() as $cart_item_key => $cart_item) {
        if ($cart_item['product_id'] == $product_id) {
          
            $cart_item['data']->set_price($custom_price);
            $cart->cart_contents[$cart_item_key] = $cart_item;
        }
    }
  }
}
add_action('woocommerce_before_calculate_totals', 'custom_set_cart_item_price');

Solution

  • When you add a product to cart, the method add_to_cart() returns the cart item key, her below we will set that cart item key in a WC_Session variable that we will use in woocommerce_before_calculate_totals hook, to retrieve the right cart item to change its price to zero.

    Assuming that your jQuery code works, and send the product ID to be added via Ajax:

    <script>
    const selectedProductId = getProdVal(randomDegree);
    const isProductInCart = isProductAlreadyInCart(selectedProductId);
    
    if (!productAdded && !isProductInCart) {
        jQuery.ajax({
            url: <?php echo json_encode(admin_url('admin-ajax.php')) ?>,
            type: 'POST',
            data: {
                action: 'custom_add_to_cart', // <== Renamed
                product_id: selectedProductId,
            },
            success: function(response) {
                console.log(response);
                productAdded = true;
                spinBtn.disabled = false;
            },
            error: function(error) {
                console.error(error);
                spinBtn.disabled = false;
            }
        });
    }
    </script>
    

    The PHP:

    add_action('wp_ajax_custom_add_to_cart', 'custom_add_to_cart');
    add_action('wp_ajax_nopriv_custom_add_to_cart', 'custom_add_to_cart');
    function custom_add_to_cart() {
        if (isset($_POST['product_id']) && $_POST['product_id'] > 0 ) {
            $product_id = intval($_POST['product_id']);
    
            if (!in_array($product_id, get_product_ids_from_cart())) {
                // Get previous added products (if any) from WC_Session variable
                $items_keys = (array) WC()->session->get('zero_price_items'); 
                
                $cart_item_key = WC()->cart->add_to_cart($product_id, 1); // Add product 
                $items_keys[$cart_item_key] = $product_id; // add the key to the array
    
                // Save cart item key in a WC_Session variable
                WC()->session->set('zero_price_items', $items_keys );
    
                echo json_encode(array('success' => true, 'message' => 'Product added to cart.'));
            } else {
                echo json_encode(array('success' => true, 'message' => 'Product already in cart.'));
            }
        } else {
            echo json_encode(array('success' => false, 'message' => 'Product ID is missing.'));
        }
        wp_die();
    }
    
    // Change cart item price
    add_action('woocommerce_before_calculate_totals', 'custom_set_cart_item_price');
    function custom_set_cart_item_price( $cart ) {
        if ( ( is_admin() && ! defined( 'DOING_AJAX' ) ) )
            return;
    
        if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
            return;
    
        $items_keys = (array) WC()->session->get('zero_price_items'); // Get the WC_Session variable data
    
        // Loop through cart items
        foreach ($cart->get_cart() as $cart_item_key => $cart_item) {
            // Check if the product has been added via custom ajax
            if ( isset($items_keys[$cart_item_key]) && $items_keys[$cart_item_key] == $cart_item['product_id'] ) {
                $cart_item['data']->set_price(0);
            }
        }
    }
    
    // For mini cart displayed price
    add_action( 'woocommerce_cart_item_price', 'filter_cart_item_price', 10, 2 );
    function filter_cart_item_price( $price_html, $cart_item, $cart_item_key ) {
        $items_keys = (array) WC()->session->get('zero_price_items'); // Get the WC_Session variable data
    
        // Check if the product has been added via custom ajax
        if ( isset($items_keys[$cart_item_key]) && $items_keys[$cart_item_key] == $cart_item['product_id'] ) {
            return wc_price( 0 );
        }
        return $price_html;
    }
    

    Code goes in functions.php file of tour child theme (or in a plugin). It should work.