I am trying to update the shipping prices for my shipping methods dynamically. I get the shipping costs from an API and I want to update the prices when the I successfully get the prices from the API response. I am using the following code :
function handle_api_response( $rates ) {
// api call code here.....
if ($response_code === 200){
$response_data = json_decode($response, true);
$price = $response_data['shipments']
}
add_filter( 'woocommerce_package_rates', 'set_shipping_prices', PHP_INT_MAX, 1 );
function set_shipping_prices( $rates ) {
foreach ( $rates as $rate_id => $rate ) {
$rates[ $rate_id ]->cost = $price;
}
return $rates;
}
}
The above code does not work but if I take the filter outside of the handle_api_response
function and set some static value to the cost it seems to work. Like this:
function handle_api_response( $rates ) {
// api call code here.....
if ($response_code === 200){
$response_data = json_decode($response, true);
$price = $response_data['shipments']
}
}
add_filter( 'woocommerce_package_rates', 'set_shipping_prices', PHP_INT_MAX, 1 );
function set_shipping_prices( $rates ) {
foreach ( $rates as $rate_id => $rate ) {
$rates[ $rate_id ]->cost = 50;
}
return $rates;
}
My problem is that, since I get the price value from the API I need to pass the price from the API response to set_shipping_prices
which runs when the filter is triggered.
This answer code is untested and may need some adjustments, but it can be the right path, to make your external API call effective.
We can try using a WC_Session variables to set the shipping cost that we want to pass:
function handle_api_response( $rates ) {
// api call code here.....
if ($response_code === 200){
$response_data = json_decode($response, true);
$cost = $response_data['shipments'];
if ( $cost > 0 ) {
// Set the cost in a WC Session variable
WC()->session->set('shipment_cost', floatval($cost));
// Trying to trigger "Update checkout" Ajax event
?><script>jQuery('body').trigger('update_checkout');</script><?php
}
}
}
Then we can recall the WC_Session variable in your hooked function like:
add_filter('woocommerce_package_rates', 'update_shipping_costs', 10, 2);
function update_shipping_costs( $rates, $package ) {
foreach ( $rates as $rate_id => $rate ) {
// Get the new cost from the Session variable
$new_cost = WC()->session->get('shipment_cost');
if( isset($rate->cost) && $rate->cost > 0 && $new_cost > 0 ) {
$rates[ $rate_id ]->cost = $new_cost; // Set the new cost
}
}
return $rates;
}
But we need something else to refresh cached chipping methods, to get it effective:
add_action('woocommerce_checkout_update_order_review', 'refresh_shipping_methods');
function refresh_shipping_methods( $post_data ){
$bool = true;
// We check for the session variable
if ( WC()->session->get('shipment_cost') > 0 ) {
$bool = false;
}
// Mandatory to make it work with shipping methods
foreach ( WC()->cart->get_shipping_packages() as $package_key => $package ){
WC()->session->set( 'shipping_for_package_' . $package_key, $bool );
}
WC()->cart->calculate_shipping();
}
And to finish we unset the WC Session variable (on checkout first load and thankyou page):
add_action('wp_footer', 'reset_wc_session_variable');
function reset_wc_session_variable() {
if (is_checkout() && WC()->session->get('shipment_cost') > 0) {
WC()->session->__unset('shipment_cost');
}
}
Code goes in functions.php file of your child theme (or in a plugin). It could work.
Related: Remove shipping cost if custom checkbox is checked in WooCommerce Checkout