Dynamic Shipping Fee Based on Custom Radio Buttons in Woocommerce

Dynamic shipping fee based on custom radio buttons in Woocommerce

You should give all necessary related code in your question. Remember that "Questions seeking debugging help ("why isn't this code working?") must include the desired behavior, a specific problem or error and the shortest code necessary to reproduce it in the question itself".

So, in the code below, you will find a complete working solution, with additional custom radio buttons, that will add dynamically a delivery fee depending on the selected radio button and for "local pickup" shipping method.

Sample Image

The code (where you will need to define your targeted "local pickup" method ID):

// Enabling delivery options for a specific defined shipping method
function targeted_shipping_method(){
// HERE below define the shipping method Id that enable the custom delivery options
return 'local_pickup:5';
}

// Customizing Woocommerce checkout 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 radio fields for packaging selection
add_action( 'woocommerce_review_order_after_shipping', 'checkout_shipping_form_delivery_addition', 20 );
function checkout_shipping_form_delivery_addition(){
$domain = 'wocommerce';

if ( WC()->session->get( 'chosen_shipping_methods' )[0] == targeted_shipping_method() ) :

echo '<tr class="delivery-radio"><th>' . __('Delivery options', $domain) . '</th><td>';

$chosen = WC()->session->get('chosen_delivery');
$chosen = empty($chosen) ? WC()->checkout->get_value('delivery') : $chosen;
$chosen = empty($chosen) ? 'regular' : $chosen;

// Add a custom checkbox field
woocommerce_form_field( 'radio_delivery', array(
'type' => 'radio',
'class' => array( 'form-row-wide' ),
'options' => array(
'regular' => __('Regular', $domain),
'premium' => __('Premium +'.wc_price(2.00), $domain),
),
'default' => $chosen,
), $chosen );

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

endif;
}

// jQuery - Ajax script
add_action( 'wp_footer', 'checkout_delivery_script' );
function checkout_delivery_script() {
// Only checkout page
if ( ! is_checkout() ) return;
?>
<script type="text/javascript">
jQuery( function($){
if (typeof wc_checkout_params === 'undefined')
return false;

$('form.checkout').on('change', 'input[name=radio_delivery]', function(e){
e.preventDefault();
var d = $(this).val();
$.ajax({
type: 'POST',
url: wc_checkout_params.ajax_url,
data: {
'action': 'delivery',
'delivery': d,
},
success: function (result) {
$('body').trigger('update_checkout');
console.log(result); // just for testing | TO BE REMOVED
},
error: function(error){
console.log(error); // just for testing | TO BE REMOVED
}
});
});
});
</script>
<?php

}

// Get Ajax request and saving to WC session
add_action( 'wp_ajax_delivery', 'wc_get_delivery_ajax_data' );
add_action( 'wp_ajax_nopriv_delivery', 'wc_get_delivery_ajax_data' );
function wc_get_delivery_ajax_data() {
if ( isset($_POST['delivery']) ){
WC()->session->set('chosen_delivery', sanitize_key( $_POST['delivery'] ) );
echo json_encode( $delivery ); // Return the value to jQuery
}
die();
}

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

// Only for targeted shipping method
if ( WC()->session->get( 'chosen_shipping_methods' )[0] != targeted_shipping_method() )
return;

if( WC()->session->get( 'chosen_delivery' ) == 'premium' )
$cart->add_fee( __( 'Delivery fee', 'woocommerce' ), 2.00 );
}

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

Set a dynamic fee based on custom checkout radio buttons and text field in WooCommerce

The following is something advanced using Ajax and WC Sessions:

It will add a custom a tip (as a custom fee) based on selected radio buttons options: fixed percentages options or custom option that will show a text field to allow customer to input a fixed amount.

Displayed Fields are hand coded to get a better display than WooCommerce form fields for radio buttons (see the screenshots below).

Sample Image

How it works for the customer:

On checkout page load, a Tip of 5% (a fee) is applied (selected by default). When changing the selected option to something else, the applied fee changes.

If the "custom" option is selected, the fee is removed while a text field appears below:

Sample Image
Customer can input a fixed amount and a Tip (a fee) is applied with this amount.

Here is the code:

// Display custom checkout fields
add_action( 'woocommerce_after_checkout_billing_form', 'add_box_option_to_checkout' );
function add_box_option_to_checkout( ) {
// Here set your radio button options (Values / Labels pairs)
$options = array( '5' => '5%', '10' => '10%', '15' => '15%', 'custom' => __('Custom', 'woocommerce') );

// Radio button fields
echo '<style> #add_tip_field.form-row label { display:inline-block; margin-left:6px; } </style>
<p class="form-row form-row-wide" id="add_tip_field"><span class="woocommerce-input-wrapper">
<label for="add_tip"><strong>'.__('Add a tip', 'woocommerce'). ':</strong> </label>';

foreach ( $options as $value => $label_name ) {
$checked = $value == '5' ? ' checked="checked"' : '';

echo '<label for="add_tip_'.$value.'" class="radio ">
<input type="radio" class="input-radio " value="'.$value.'" name="add_tip" id="add_tip_'.$value.'"'.$checked.'> '.$label_name.'
</label>';
}
echo '</span></p>';

// Text field (hidden by default)
echo '<p class="form-row form-row-wide" id="custom_tip_field" style="display:none""><span class="woocommerce-input-wrapper">
<input type="text" class="input-text " name="custom_tip" id="custom_tip" value="" placeholder="'.__('Input a tip amount', 'woocommerce').'">
</span></p>';
}

// jQuery / Ajax script
add_action( 'woocommerce_after_checkout_form', 'wc_checkout_fee_script' );
function wc_checkout_fee_script() {
?>
<script type="text/javascript">
jQuery( function($){
if (typeof wc_checkout_params === 'undefined')
return false;

var addTip = 'input[name="add_tip"]',
customTip = 'input[name="custom_tip"]'

function triggerAjaxEvent( amount, type = 'percent' ){
$.ajax({
type: 'POST',
url: wc_checkout_params.ajax_url,
data: {
'action': 'tip_fee',
'amount': amount,
'type' : type
},
success: function (result) {
$(document.body).trigger('update_checkout');
console.log(result);
},
});
}

triggerAjaxEvent( $(addTip+':checked').val() );

$('form.checkout').on('change', addTip, function() {
var textField = $('#custom_tip_field'),
percent = $(this).val();

if( percent === 'custom' && textField.css('display') === 'none' ) {
textField.show(200);
} else if ( percent !== 'custom' && textField.css('display') !== 'none' ) {
textField.hide(200, function(){
$(customTip).val('');
});
}
triggerAjaxEvent( percent );
});

$('form.checkout').on('input change', customTip, function() {
triggerAjaxEvent( $(this).val(), 'custom' );
});
});
</script>
<?php
}

// Get Ajax request and save data to WC session
add_action( 'wp_ajax_tip_fee', 'get_tip_fee' );
add_action( 'wp_ajax_nopriv_tip_fee', 'get_tip_fee' );
function get_tip_fee() {
if ( isset($_POST['amount']) && isset($_POST['type']) ) {
$fee = is_numeric($_POST['amount']) && $_POST['amount'] > 0 ? floatval($_POST['amount']) : 0;

WC()->session->set('fee_data', array(
'type' => esc_attr($_POST['type']),
'amount' => $fee
) );

print_r(WC()->session->get('fee_data'));
}
die();
}

// Add a dynamic fee from WC Session ajax data
add_action( 'woocommerce_cart_calculate_fees', 'checkout_custom_tip_fee' );
function checkout_custom_tip_fee( $cart ) {
if ( is_admin() && !defined('DOING_AJAX') )
return;

$data = WC()->session->get('fee_data');
$fee = $total = 0;

if ( isset($data['type']) && isset($data['amount']) ) {
// 1. Fixed Fee amount
if ( $data['type'] === 'custom' ) {
$text = $data['type'];
$fee = $data['amount'];
}
// 2. Calculated percentage Fee amount
elseif ( $data['type'] === 'percent' && $data['amount'] > 0 ) {
$text = $data['amount'] . '%';

// Get cart subtotal excl. Taxes (discounted)
foreach ( $cart->get_cart() as $cart_item ) {
$total = $cart_item['line_total'];
}
// Calculate fee
$fee = $total * $data['amount'] / 100 ;
}
// Add the fee
if ( $fee > 0 ) {
$cart->add_fee( sprintf( __('Tip (%s)', 'woocommerce' ), $text ), $fee );
}
}
}

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


Addition related to your comment:

To use cart items total including taxes replace in last function:

            // Get cart subtotal excl. Taxes (discounted)
foreach ( $cart->get_cart() as $cart_item ) {
$total = $cart_item['line_total'];
}

with

            // Get cart subtotal Incl. Taxes (discounted)
foreach ( $cart->get_cart() as $cart_item ) {
$total = $cart_item['line_total'] + $cart_item['line_tax'];
}

WooCommerce checkout radio buttons that set a percentage fee based on specific items subtotal

To get a custom percentage fee based on specific cart items subtotal (for a defined set of product IDs) and on radio button (percentage warranty choice), use the following revisited code:

// Custom function to get related cart items subtotal for specific defined product Ids
function get_related_items_subtotal( $cart ) {
// HERE below define the related targeted products IDs in the array
$targeted_ids = array(29, 27, 28, 72, 84, 95);
$custom_subtotal = 0; // Initialize

// Loop through cart items
foreach ( $cart->get_cart() as $item ) {
if ( array_intersect($targeted_ids, array($item['product_id'], $item['variation_id']) ) ) {
$custom_subtotal += $item['line_subtotal'] + $item['line_subtotal_tax'];
}
}
return $custom_subtotal;
}

// 1 - Display custom checkout radio buttons fields
add_action( 'woocommerce_review_order_before_payment', 'display_custom_checkout_radio_buttons' );
function display_custom_checkout_radio_buttons() {
$custom_subtotal = get_related_items_subtotal( WC()->cart );

if ( $custom_subtotal > 0 ) {
$value = WC()->session->get( 'warranty' );
$value = empty( $value ) ? WC()->checkout->get_value( 'warranty' ) : $value;
$value = empty( $value ) ? '0' : $value;

echo '<div id="checkout-radio">
<h4>' . __("Choose your Warranty") .'</h4>';

woocommerce_form_field( 'warranty', array(
'type' => 'radio',
'class' => array( 'form-row-wide', 'update_totals_on_change' ),
'options' => array(
'0' => __( '1 Year Repair or Replace Warranty - Included', 'woocommerce' ),
'10' => __( '2 Years Extended Warranty', 'woocommerce' ) . ' (' . strip_tags( wc_price( 10 * $custom_subtotal / 100 ) ) . ')',
'15' => __( '3 Years Extended Warranty', 'woocommerce' ) . ' (' . strip_tags( wc_price( 15 * $custom_subtotal / 100 ) ) . ')',
),
), $value );

echo '</div>';
}

}

// 2 - Customizing Woocommerce checkout radio form field
add_filter( 'woocommerce_form_field_radio', 'custom_form_field_radio', 20, 4 );
function custom_form_field_radio( $field, $key, $args, $value ) {
if ( ! empty( $args['options'] ) && 'warranty' === $key && 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;
}

// 3 - Add a percentage Fee based on radio buttons for specific defined product Ids
add_action( 'woocommerce_cart_calculate_fees', 'percentage_fee_based_on_radio_buttons', 20 );
function percentage_fee_based_on_radio_buttons( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;

$percentage = (float) WC()->session->get( 'warranty' );

if ( $percentage ) {
$custom_subtotal = get_related_items_subtotal( $cart );

if ( $custom_subtotal > 0 ) {
$label_text = sprintf( __('Extended Warranty %d years', 'woocommerce'), $percentage == 10 ? 2 : 3 );
$cart->add_fee( $label_text, $custom_subtotal * $percentage / 100 );
}
}
}

// 4 - Set chosen radio button value to a WC Session variable
add_action( 'woocommerce_checkout_update_order_review', 'chosen_input_radio_button_value_to_wc_session' );
function chosen_input_radio_button_value_to_wc_session( $posted_data ) {
parse_str( $posted_data, $fields );

if ( isset( $fields['warranty'] ) ){
WC()->session->set( 'warranty', $fields['warranty'] );
}
}

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

WooCommerce Dynamic Surchage based on radio button in checkout

So I solved this by having the AJAX, handle the button change and call a PHP callback that set WC->session->set variable. Then in the AJAX is issued a location.reload which forced functions.php to reload. Inside of my surcharge filter I then checked for the session variable and added the fee or not based on the session value.

Add Delivery Radio Buttons before order total in WooCommerce checkout

To move your radio buttons before order total you will need to use another hook. But you can't have that delivery radio buttons on the fee total line…

I have simplified and revisited the code:

add_action( 'woocommerce_review_order_before_order_total', 'checkout_delivery_radio_buttons' );
function checkout_delivery_radio_buttons() {
echo '<tr class="delivery-radio">
<th>'.__("Delivery Options").'</th><td>';

$chosen = WC()->session->get( 'delivery' );
$chosen = empty( $chosen ) ? WC()->checkout->get_value( 'delivery' ) : $chosen;
$chosen = empty( $chosen ) ? '0' : $chosen;

woocommerce_form_field( 'delivery', array(
'type' => 'radio',
'class' => array( 'form-row-wide', 'update_totals_on_change' ),
'options' => array(
'2.95' => '60 MINUTES: €2.95',
'0' => '24 - 48 HOURS',
),
), $chosen );

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

add_action( 'woocommerce_cart_calculate_fees', 'checkout_delivery_fee', 20, 1 );
function checkout_delivery_fee( $cart ) {
if ( $radio = WC()->session->get( 'delivery' ) ) {
$cart->add_fee( 'Delivery Fee', $radio );
}
}

add_action( 'woocommerce_checkout_update_order_review', 'checkout_delivery_choice_to_session' );

function checkout_delivery_choice_to_session( $posted_data ) {
parse_str( $posted_data, $output );
if ( isset( $output['delivery'] ) ){
WC()->session->set( 'delivery', $output['delivery'] );
}
}

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



Related Topics



Leave a reply



Submit