Update fee dynamically based on radio buttons in Woocommerce checkout
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>
Solution 1:
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