In WooCommerce checkout page, shipping cost not updated when changing payment type, But shipping cost changes when updating address fields.
Requirement :
If the payment type is COD, then shipping cost is 100; If the payment type is UPI, then shipping cost is 70;
Following are the two files, we use to address the requirement
// Adjust shipping cost based on the selected payment method
add_filter('woocommerce_package_rates', 'adjust_shipping_based_on_payment_method', 10, 2);
function adjust_shipping_based_on_payment_method($rates, $package) {
$chosen_payment_method = WC()->session->get('chosen_payment_method');
if ($chosen_payment_method) {
foreach ($rates as $rate_key => $rate) {
if ($chosen_payment_method === 'cod') {
$rates[$rate_key]->cost = 100;
} elseif ($chosen_payment_method === 'wc-upi') {
$rates[$rate_key]->cost = 70;
}
}
}
return $rates;
}
// Enqueue custom JavaScript
function enqueue_custom_shipping_scripts() {
if (is_checkout()) {
wp_enqueue_script('custom-shipping-js', plugin_dir_url(__FILE__) . 'custom-shipping.js', array('jquery', 'wc-checkout'), '1.0', true);
wp_localize_script('custom-shipping-js', 'customShippingParams', array(
'ajax_url' => admin_url('admin-ajax.php'),
));
}
}
add_action('wp_enqueue_scripts', 'enqueue_custom_shipping_scripts');
// Handle AJAX request to update shipping cost
add_action('wp_ajax_update_shipping_cost', 'update_shipping_cost');
add_action('wp_ajax_nopriv_update_shipping_cost', 'update_shipping_cost');
function update_shipping_cost() {
if (isset($_POST['payment_method'])) {
WC()->session->set('chosen_payment_method', sanitize_text_field($_POST['payment_method']));
}
WC()->cart->calculate_totals();
WC()->cart->maybe_set_cart_cookies();
wp_send_json_success();
}
jQuery(document).ready(function($) {
// Detect changes in payment method selection
$('form.checkout').on('change', 'input[name="payment_method"]', function() {
var paymentMethod = $(this).val();
console.log("Selected Payment Method: " + paymentMethod); // Log selected payment method
// Update session and trigger checkout update via AJAX
$.ajax({
type: 'POST',
url: customShippingParams.ajax_url,
data: {
action: 'update_shipping_cost',
payment_method: paymentMethod,
},
success: function(response) {
console.log("AJAX Response: ", response); // Log the response
if (response.success) {
console.log('Success');
// Trigger WooCommerce update checkout event
$(document.body).trigger('update_checkout');
// location.reload(true);
}
},
error: function(error) {
console.error('AJAX error:', error); // Debugging
}
});
});
// Prevent the 'update_checkout' event from being triggered infinitely
var isUpdating = false;
$(document.body).on('update_checkout', function() {
if (!isUpdating) {
isUpdating = true;
$('body').trigger('update_checkout');
isUpdating = false;
}
});
});
What I am doing wrong?
The following code only works with Classic Checkout (but not with newly Checkout Block). For info, here is the way to switch back to legacy Classic Checkout (shortcode).
Your code is much more complicated than needed. There are some mistakes, your custom Ajax is not needed and there are important missing things.
In the first function below (settings), we define the shipping cost by payment method Id.
Try the following instead:
// Utility function (settings: payment ID / cost pairs)
function get_shipping_cost_for_chosen_payment_method() {
$payment_id = WC()->session->get('chosen_payment_method');
$data_costs = array(
'cod' => 100,
'wc-upi' => 70,
);
return isset($data_costs[$payment_id]) ? floatval($data_costs[$payment_id]) : false;
}
// Change shipping cost based on the chosen payment method
add_filter('woocommerce_package_rates', 'adjust_shipping_based_on_payment_method', 10, 2);
function adjust_shipping_based_on_payment_method($rates, $package) {
if ( $new_cost = get_shipping_cost_for_chosen_payment_method() ) {
foreach ($rates as $rate_key => $rate) {
if ( $rate->cost > 0 && $new_cost > 0 ) {
$rates[$rate_key]->cost = $new_cost;
}
}
}
return $rates;
}
// Update checkout on payment method change (jQuery)
add_action( 'woocommerce_checkout_init', 'trigger_update_checkout_on_payment_method_change' );
function trigger_update_checkout_on_payment_method_change(){
wc_enqueue_js("$('form.checkout').on( 'change', 'input[name=payment_method]', function(){
$(document.body).trigger('update_checkout');
});");
}
// Refresh shipping methods on payment method change (Mandatory)
add_action('woocommerce_checkout_update_order_review', 'payment_method_change_refresh_shipping_methods' );
function payment_method_change_refresh_shipping_methods() {
if ( WC()->session->get('chosen_payment_method') ) {
$packages = WC()->cart->get_shipping_packages();
foreach ($packages as $package_key => $package ) {
WC()->session->set( 'shipping_for_package_' . $package_key, false );
}
}
}
Code goes in function.php file of your child theme (or in a plugin). Tested and works.