5

So I'm developing a plugin for woocommerce, and I have added a selection for packing, either in a plastic bag or in Cartoon Box with each having a different cost.

Ones the user selects one of the options I need the WordPress to refresh the prices and update the cost by adding in the correct fee by using:

WC_Cart $cart->add_fee( 'Emballagegebyr', intval($fees));

What is the best way to have the WC_Cart fee added and update the price? and how should the code look like?

And is it okay to use $_GET and $_POST to get the values or even better is there a way to use AJAX to update the price without refreshing the page?

Currently, I'm using $_GET to get the data from the browser by the following code

function at87_add_custom_fees( WC_Cart $cart ){
    $fees = 3; // fee amount
    $fees = isset($_GET['test']) ? $_GET['test'] : 3;

    $cart->add_fee( 'Emballagegebyr', intval($fees));
}

and my plan is then to maybe add a Javascript code like below to then use the radio button to refresh the page and pass the selected option.

add_action( 'wp_footer', 'woocommerce_add_gift_box' );
function woocommerce_add_gift_box() {
    if (is_checkout()) {
    ?>
    <script type="text/javascript">
    jQuery( document ).ready(function( $ ) {
       // $('#add_gift_box').click(function(){
    //       jQuery('body').trigger('update_checkout');
    //    });

        $("#pakpose1 input:radio").change(function(){
    // Do something interesting here
            alert("test");
        });
    });
    </script>
    <?php
    }
}

I'm not sure though if this is the smartest way to do it, and or if there is another way, that could be better, and what security affect it might have, maybe there are some hooks that can get the same job done.

BTW: To get the radio buttons in the checkout-page I have made the plugin in such way that it override the woocommerce review-order.php and added the radio buttons in that template as following:

<tr class="packing-selections">
      <th>Pakning</th>
      <td>
                <input type="radio" id="pakpose1" name="pakpose" value="pakpose" checked="checked">Pak i pose <?php echo get_woocommerce_currency_symbol() ?>3.00<br>
                <input type="radio" id="pakpose2" name="pakpose" value="pakkasse">Pak i papkasse <?php echo get_woocommerce_currency_symbol() ?>9.00
     </td>
</tr>
LoicTheAztec
  • 229,944
  • 23
  • 356
  • 399
Ali T
  • 85
  • 1
  • 5
  • when the page first loads do you show the price of the item to the user? or do they have to select to see a price? Why not just load the price and the price of each option when you first load the page, than use JS to modify the displayed price to the user, but calculate the real price from the $_POST keys sent over to the server. – Orlando P. Mar 23 '18 at 20:45
  • Well I show the price for each of the options, so its a possibility, I then guess i will need to update the total price via a JS but then how do you make sure the correct price is then made once the customer click "Place order" and how do you make the fee reflect in the invoice? – Ali T Mar 23 '18 at 20:49
  • so im not sure which hook you will have to tie into but when submitted you check the $_POST values for the value of the radio field of the form submitted and add the WC_Cart $cart->add_fee( 'Emballagegebyr', intval($fees)); or others lines of code. – Orlando P. Mar 23 '18 at 20:50
  • http://hookr.io/actions/woocommerce_cart_calculate_fees/ Here i see a hook for when your in the cart to add the fee. Not sure if this will work you will have to test it. Also I would recommend looking at other plugins to see how they have achieve this. https://github.com/WPprodigy/woocommerce-product-fees/blob/master/classes/class-woocommerce-product-fees.php – Orlando P. Mar 23 '18 at 20:54

1 Answers1

10

Code updated on 2019: Works for non logged users too and on last WooCommerce version 3.7.x

This needs Ajax and can not be done without it… Here is the Ajax way. In this code you got everything, so remove your related customizations before trying it (meaning remove your radio buttons from template and all related code)…

The complete code (nothing else needed):

// Customizing Woocommerce radio form field
add_action( 'woocommerce_form_field_radio', 'custom_form_field_radio', 20, 4 );
function custom_form_field_radio( $field, $key, $args, $value ) {
    if ( ! empty( $args['options'] ) && is_checkout() ) {
        $field = str_replace( '</label><input ', '</label><br><input ', $field );
        $field = str_replace( '<label ', '<label style="display:inline;margin-left:8px;" ', $field );
    }
    return $field;
}

// Add a custom dynamic packaging fee
add_action( 'woocommerce_cart_calculate_fees', 'add_packaging_fee', 20, 1 );
function add_packaging_fee( $cart ) {
    if ( is_admin() && ! defined( 'DOING_AJAX' ) )
        return;

    $packing_fee = WC()->session->get( 'chosen_packing' ); // Dynamic packing fee
    $fee = $packing_fee === 'box' ? 9.00 : 3.00;
    $cart->add_fee( __( 'Packaging fee', 'woocommerce' ), $fee );
}

// Add a custom radio fields for packaging selection
add_action( 'woocommerce_review_order_after_shipping', 'checkout_shipping_form_packing_addition', 20 );
function checkout_shipping_form_packing_addition() {
    $domain = 'woocommerce';

    echo '<tr class="packing-select"><th>' . __('Packing options', $domain) . '</th><td>';

    $chosen   = WC()->session->get('chosen_packing');
    $chosen   = empty($chosen) ? WC()->checkout->get_value('radio_packing') : $chosen;
    $chosen   = empty($chosen) ? 'bag' : $chosen;

    // Add a custom checkbox field
    woocommerce_form_field( 'radio_packing', array(
        'type' => 'radio',
        'class' => array( 'form-row-wide packing' ),
        'options' => array(
            'bag' => __('In a bag '.wc_price(3.00), $domain),
            'box' => __('In a gift box '.wc_price(9.00), $domain),
        ),
        'default' => $chosen,
    ), $chosen );

    echo '</td></tr>';
}

// jQuery - Ajax script
add_action( 'wp_footer', 'checkout_shipping_packing_script' );
function checkout_shipping_packing_script() {
    if ( ! is_checkout() )
        return; // Only checkout page
    ?>
    <script type="text/javascript">
    jQuery( function($){
        $('form.checkout').on('change', 'input[name=radio_packing]', function(e){
            e.preventDefault();
            var p = $(this).val();
            $.ajax({
                type: 'POST',
                url: wc_checkout_params.ajax_url,
                data: {
                    'action': 'woo_get_ajax_data',
                    'packing': p,
                },
                success: function (result) {
                    $('body').trigger('update_checkout');
                    console.log('response: '+result); // just for testing | TO BE REMOVED
                },
                error: function(error){
                    console.log(error); // just for testing | TO BE REMOVED
                }
            });
        });
    });
    </script>
    <?php

}

// Php Ajax (Receiving request and saving to WC session)
add_action( 'wp_ajax_woo_get_ajax_data', 'woo_get_ajax_data' );
add_action( 'wp_ajax_nopriv_woo_get_ajax_data', 'woo_get_ajax_data' );
function woo_get_ajax_data() {
    if ( isset($_POST['packing']) ){
        $packing = sanitize_key( $_POST['packing'] );
        WC()->session->set('chosen_packing', $packing );
        echo json_encode( $packing );
    }
    die(); // Alway at the end (to avoid server error 500)
}

Code goes in functions.php file of your active child theme (or active theme). Tested and works.

Similar: Add a dynamic fee based on a select field in Woocommerce checkout

enter image description here

LoicTheAztec
  • 229,944
  • 23
  • 356
  • 399
  • 1
    @fja3omega I have updated the code. It works also for non logged users too. – LoicTheAztec Jul 02 '18 at 04:22
  • 1
    I actually added a nopriv due to me researching for that also. I should have mentioned that here. – fja3omega Jul 02 '18 at 05:28
  • 1
    Thanks for this code @LoicTheAztec it works flawlessly. I tried hooking it outside the checkout table to `woocommerce_checkout_before_order_review` where i actually want to display the radio buttons and i was amazed how properly aligned it was. You're a life saver – Kolawole Emmanuel Izzy Feb 15 '19 at 21:47
  • So the $('body').trigger('update_checkout'); will refresh the checkout. I want to do the same thing but in the cart page. What should I use instead of update_checkout? I have a checkbox and if it is checked then I will dynamically add the surcharge –  coinhndp May 10 '20 at 13:48
  • It doesn't work. I need to calculate the custom surcharge fee based on the number of items via external API. I successfully update my custom component (TEMPLATE custom-fee) with the surcharge fee but fail to update the woocommerce surcharge in cart total. It always gets the PREVIOUS value of WC()->session->get( 'custom_charge_amount', 0 ); instead of the CURRENT value. Could you please take a look: https://pastebin.com/TG2RDNrP https://pastebin.com/TG2RDNrP –  coinhndp May 10 '20 at 14:42
  • I use the woocommerce_cart_calculate_fees hook. For some reasons, the woo surcharge always pick up the previous value of my custom_charge_amount in the session :( –  coinhndp May 10 '20 at 14:46
  • @coinhndp You should better ask a new question, with all involved code in it, to make it testable, with clear explanations on what you have and what you exoect to have. – LoicTheAztec May 10 '20 at 14:47