0

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.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
AD1TYA
  • 93
  • 3
  • 12

1 Answers1

1

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

LoicTheAztec
  • 229,944
  • 23
  • 356
  • 399