1

In WooCommerce I want to have a checkbox that, when pressed, it adds a product to my cart, and the price of that product is 3% of the total cart price.

Based on "Checkbox field that add to cart a product in Woocommerce checkout page" answer thread and edited a bit to suit my needs, here is my code:


 // Display a custom checkout field
add_action( 'woocommerce_checkout_before_terms_and_conditions', 'custom_checkbox_checkout_field' );
function custom_checkbox_checkout_field() {
    $value = WC()->session->get('add_a_product');

    woocommerce_form_field( 'cb_add_product', array(
        'type'          => 'checkbox',
        'label'         => '  ' . __('Add Assembly Service (3% extra)'),
        'class'         => array('form-row-wide'),
    ), $value == 'yes' ? true : false );
}


// The jQuery Ajax request
add_action( 'wp_footer', 'checkout_custom_jquery_script' );
function checkout_custom_jquery_script() {
    // Only checkout page
    if( is_checkout() && ! is_wc_endpoint_url() ):

    // Remove "ship_different" custom WC session on load
    if( WC()->session->get('add_a_product') ){
        WC()->session->__unset('add_a_product');
    }
    if( WC()->session->get('product_added_key') ){
        WC()->session->__unset('product_added_key');
    }
    // jQuery Ajax code
    ?>
    <script type="text/javascript">
    jQuery( function($){
        if (typeof wc_checkout_params === 'undefined')
            return false;

        $('form.checkout').on( 'change', '#cb_add_product', function(){
            var value = $(this).prop('checked') === true ? 'yes' : 'no';

            $.ajax({
                type: 'POST',
                url: wc_checkout_params.ajax_url,
                data: {
                    'action': 'add_a_product',
                    'add_a_product': value,
                },
                success: function (result) {
                    $('body').trigger('update_checkout');
                    console.log(result);
                }
            });
        });
    });
    </script>
    <?php
    endif;
}

// The Wordpress Ajax PHP receiver
add_action( 'wp_ajax_add_a_product', 'checkout_ajax_add_a_product' );
add_action( 'wp_ajax_nopriv_add_a_product', 'checkout_ajax_add_a_product' );
function checkout_ajax_add_a_product() {
    if ( isset($_POST['add_a_product']) ){
        WC()->session->set('add_a_product', esc_attr($_POST['add_a_product']));
        echo $_POST['add_a_product'];
    }
    die();
}

// Add remove free product
add_action( 'woocommerce_before_calculate_totals', 'adding_removing_specific_product');
function adding_removing_specific_product( $cart ) {
    if (is_admin() && !defined('DOING_AJAX'))
        return;

    if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
        return;

    // HERE the specific Product ID
    $product_id = 1514;

    $result = WC()->session->get('add_a_product');
    if( strpos($result, 'yes') !== false && ! WC()->session->get('product_added_key') )
    {
        $cart_item_key = $cart->add_to_cart( $product_id );
        WC()->session->set('product_added_key', $cart_item_key);

    }
    elseif( strpos($result, 'no') !== false && WC()->session->get('product_added_key') )
    {
        $cart_item_key = WC()->session->get('product_added_key');
        $cart->remove_cart_item( $cart_item_key );
        WC()->session->__unset('product_added_key');
    }
}

function woocommerce_custom_price_to_cart_item( $cart ) {  
    if (is_admin() && !defined('DOING_AJAX'))
        return;

    if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
        return;

    if( !WC()->session->__isset( "reload_checkout" )) {
         global $woocommerce; 
        $totalPriceBeforeService = $cart->cart_contents_total;
        $servicePrice = (float) ceil($totalPriceBeforeService) * 0.03;
        foreach ( $cart->cart_contents as $key => $value ) {
            $product_idnew = $value['data']->get_id();
             if($product_idnew == 1514){
                 //set new prices
                $value['data']->set_price($servicePrice);
                $new_price = $value['data']->get_price();
                echo" $new_price ";
             }
        } 
    }  
}
add_action( 'woocommerce_before_calculate_totals', 'woocommerce_custom_price_to_cart_item' );

It adds the product fine, and all is good. However, when I set the price, its not updating.

Okay, So here is the checkbox unselected.

enter image description here

And here is the checkbox after checking it. As you can see, the value of the item is 46.5, but its not updating in the cart. Any idea how to fix this?

enter image description here

LoicTheAztec
  • 229,944
  • 23
  • 356
  • 399

1 Answers1

1
  • You use 2x woocommerce_before_calculate_totals, I reduced it to 1.

  • $cart->get_cart_contents_total(); also include the original product price (of the new added product), so this will be deducted before the final calculation.

// Display a custom checkout field
add_action( 'woocommerce_checkout_before_terms_and_conditions', 'custom_checkbox_checkout_field' );
function custom_checkbox_checkout_field() {
    $value = WC()->session->get('add_a_product');

    woocommerce_form_field( 'cb_add_product', array(
        'type'          => 'checkbox',
        'label'         => '&nbsp;&nbsp;' . __('Add a demo product to your order'),
        'class'         => array('form-row-wide'),
    ), $value == 'yes' ? true : false );
}


// The jQuery Ajax request
add_action( 'wp_footer', 'checkout_custom_jquery_script' );
function checkout_custom_jquery_script() {
    // Only checkout page
    if( is_checkout() && ! is_wc_endpoint_url() ):

    // Remove "ship_different" custom WC session on load
    if( WC()->session->get('add_a_product') ){
        WC()->session->__unset('add_a_product');
    }
    if( WC()->session->get('product_added_key') ){
        WC()->session->__unset('product_added_key');
    }
    // jQuery Ajax code
    ?>
    <script type="text/javascript">
    jQuery( function($){
        if (typeof wc_checkout_params === 'undefined')
            return false;

        $('form.checkout').on( 'change', '#cb_add_product', function(){
            var value = $(this).prop('checked') === true ? 'yes' : 'no';

            $.ajax({
                type: 'POST',
                url: wc_checkout_params.ajax_url,
                data: {
                    'action': 'add_a_product',
                    'add_a_product': value,
                },
                success: function (result) {
                    $('body').trigger('update_checkout');
                    console.log(result);
                }
            });
        });
    });
    </script>
    <?php
    endif;
}

// The Wordpress Ajax PHP receiver
add_action( 'wp_ajax_add_a_product', 'checkout_ajax_add_a_product' );
add_action( 'wp_ajax_nopriv_add_a_product', 'checkout_ajax_add_a_product' );
function checkout_ajax_add_a_product() {
    if ( isset($_POST['add_a_product']) ){
        WC()->session->set('add_a_product', esc_attr($_POST['add_a_product']));
        echo $_POST['add_a_product'];
    }
    die();
}

// Add remove free product
function adding_removing_specific_product( $cart ) {
    if (is_admin() && !defined('DOING_AJAX'))
        return;

    if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
        return;

    // HERE the specific Product ID
    $product_id = 1514;

    if( WC()->session->get('add_a_product') == 'yes' && ! WC()->session->get('product_added_key') ) {
        $cart_item_key = $cart->add_to_cart( $product_id );
        WC()->session->set('product_added_key', $cart_item_key);
    }
    elseif( WC()->session->get('add_a_product') == 'no' && WC()->session->get('product_added_key') ) {
        $cart_item_key = WC()->session->get('product_added_key');
        $cart->remove_cart_item( $cart_item_key );
        WC()->session->__unset('product_added_key');
    }
    
    if( !WC()->session->__isset( "reload_checkout" )) {
        // Get cart total
        $cart_total = $cart->get_cart_contents_total();
        
        if ( isset( $cart_total ) && $cart_total != 0 ) {                       
            foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) {
                // Get product id
                $product_id_new = $cart_item['product_id'];
                
                // Get original product price
                $exclude_original_product_price = $cart_item['data']->get_price();

                if( $product_id_new == $product_id ) {
                    // Calculate new cart total
                    $new_cart_total = $cart_total - $exclude_original_product_price;
                    
                    // Caclulate new price ( 3% )
                    $new_price = $new_cart_total * 3 / 100;
                    
                    $cart_item['data']->set_price( $new_price );
                    break;
                }
            }
        }
    }
}
add_action( 'woocommerce_before_calculate_totals', 'adding_removing_specific_product', 10, 1 );
7uc1f3r
  • 28,449
  • 17
  • 32
  • 50
  • Hey man. I really appreciate your taking the time to help me out. @7uc1f3r i took your code as is, but still same exact problem. The price is still showing as 0.00 – Mohammad Ghali Apr 26 '20 at 15:27
  • Hi, I have tested it extensively (WooCommerce 4.0.1 - Wordpress 5.4) and it does work. The product (ID) I used for testing did contain a price, so it was not 0) I think it's best to debug the code step by step to see where it might go wrong for you – 7uc1f3r Apr 26 '20 at 16:04
  • Hey man, thank you for replying. I've checked my wordpress and woocommerce versions, and they are the same as yours. Ive put a price to the product, and I also tried to created another product, but still the price in the object is updating, but its not being reflected in the cart. Do you think that there's a better idea to do this other than having to add a product and change its price? I just want the total of the whole cart to increase by 3% and indicate that its the user added the service. – Mohammad Ghali Apr 26 '20 at 18:39
  • It looks like `if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 ) return; ` this was the issue. When you increase 2 it gets solved which is weird. Any who, I found this, and I think it better suits my need [https://stackoverflow.com/questions/57613334/display-a-checkbox-that-add-a-fee-in-woocommerce-checkout-page] Thank you very much @7uc1f3r – Mohammad Ghali Apr 26 '20 at 18:59
  • 1
    @7uc1f3r I think that the OP problem is about minicart. Since WC 3.6, using woocommerce_before_calculate_totals hook for cart item changes is not reflected in minicart… – LoicTheAztec Apr 27 '20 at 11:36