title: Woocommerce shipping cost not added to order
body: <p>I have created a custom shipping method that calculates the cost of shipping from an external shipping API.
  I have followed the instructions found <a href="https://docs.woocommerce.com/document/shipping-method-api/" rel="nofollow noreferrer">here (Woocommerce docs)</a> and <a href="https://code.tutsplus.com/tutorials/create-a-custom-shipping-method-for-woocommerce--cms-26098" rel="nofollow noreferrer">here(Tutsplus)</a>.</p>
  
  <p>I have added the shipping rate as shown below</p>
  
  <pre><code>//...
  public function calculate_shipping( $package ) {
      //...
  $rate = array(
              'id' =&gt; $this-&gt;id,
              'label' =&gt; $this-&gt;title,
              'cost' =&gt; $cost //calculated based on the distance
          );
  $this-&gt;add_rate( $rate );
  }
  //...
  </code></pre>
  
  <p>Everything works fine up to the point where the customer has to complete the order, then the items in the cart are the only ones included in the order total.</p>
  
  <p>The shipping method and cost are visible in the cart confirmation section but once the order is confirmed, the cost is not included in the order and the receipt.</p>
  
  <p>The shipping method is recorded but the cost is not included.</p>
  
  <p>Edit... Here is my entire shipping plugin file</p>
  
  <pre><code>&lt;?php
  /*
  Woocommerce plugin details
  */
  if (!defined('ABSPATH')) {
      exit;
      // Exit if accessed directly
  }
  /**
   * Check if WooCommerce is active
   **/
  if (in_array('woocommerce/woocommerce.php', apply_filters('active_plugins', get_option('active_plugins')))) {
      function my_shipping_method()
      {
          if (!class_exists('my_shipping_method')) {
              class my_shipping_method extends WC_Shipping_Method
              {
                  /**
                   * Constructor for the shipping class
                   *
                   * @access public
                   * @return void
                   */
                  public function __construct()
                  {
                      $this-&gt;id = 'my_shipping_method';
                      $this-&gt;method_title = __('Custom Ondemand Shipping', 'my_shipping_method');
                      $this-&gt;method_description = __('Custom Shipping Method for Custom Ondemand', 'my_shipping_method');
                      // Availability &amp; Countries
                      $this-&gt;availability = 'including';
                      $this-&gt;countries = array('KE');
                      $this-&gt;init();
                      $this-&gt;enabled = isset($this-&gt;settings['enabled']) ? $this-&gt;settings['enabled'] : 'yes';
                      $this-&gt;title = isset($this-&gt;settings['title']) ? $this-&gt;settings['title'] : __('Custom Ondemand Shipping', 'my_shipping_method');
                      $this-&gt;API_username = isset($this-&gt;settings['API_username']) ? $this-&gt;settings['API_username'] : 'myusername';
                      $this-&gt;API_key = isset($this-&gt;settings['API_key']) ? $this-&gt;settings['API_key'] : 'mykey';
                      $this-&gt;CUSTOM_LIVE_URL = 'https://api.custom.com/v1';
                      $this-&gt;CUSTOM_TEST_URL = 'https://apitest.custom.com/v1/';
                      $this-&gt;CUSTOM_URL = (isset($this-&gt;settingd['USE_LIVE_API']) &amp;&amp; $this-&gt;settingd['USE_LIVE_API'] == 'yes' )? $this-&gt;CUSTOM_LIVE_URL : $this-&gt;CUSTOM_TEST_URL;
                  }
  
                  /**
                   * Init the settings
                   *
                   * @access public
                   * @return void
                   */
                  function init()
                  {
                      // Load the settings API
                      $this-&gt;init_form_fields();
                      $this-&gt;init_settings();
                      // Save settings in admin if you have any defined
                      add_action('woocommerce_update_options_shipping_' . $this-&gt;id, array($this, 'process_admin_options'));
                      //register google maps places library
                      wp_register_script('mapsPlaces', 'https://maps.googleapis.com/maps/api/js?key=' . $this-&gt;settings['maps_API_key'] . '&amp;libraries=places', array(), false);
  
                      wp_register_style('main_style', plugins_url('css/my_shipping_method-woocommerce.css', __FILE__));
                      wp_enqueue_style('main_style');
  
                  }
  
                  /**
                   * Define settings field for this shipping
                   * @return void
                   */
                  function init_form_fields()
                  {
                      $this-&gt;form_fields = array(
                          'enabled' =&gt; array(
                              'title' =&gt; __('Enable', 'my_shipping_method'),
                              'type' =&gt; 'checkbox',
                              'description' =&gt; __('Enable this shipping method.', 'my_shipping_method'),
                              'default' =&gt; 'yes'
                          ),
                          'USE_LIVE_API' =&gt; array(
                              'title' =&gt; __('Use Live API', 'my_shipping_method'),
                              'type' =&gt; 'checkbox',
                              'description' =&gt; __('Check if you want to use the live API or uncheck to use the test API.', 'my_shipping_method'),
                              'default' =&gt; 'yes'
                          ),
                          'title' =&gt; array(
                              'title' =&gt; __('Title', 'my_shipping_method'),
                              'type' =&gt; 'text',
                              'description' =&gt; __('Title displayed to users', 'my_shipping_method'),
                              'default' =&gt; __('Custom Ondemand', 'my_shipping_method')
                          ),
                          'API_username' =&gt; array(
                              'title' =&gt; __('Custom Ondemand username', 'my_shipping_method'),
                              'type' =&gt; 'text',
                              'description' =&gt; __('Custom Ondemand merchant username. You can signup here &lt;a href="http://custom.com"&gt;http://custom.com&lt;/a&gt;', 'my_shipping_method'),
                              'default' =&gt; __('myusername', 'my_shipping_method')
                          ),
                          'API_key' =&gt; array(
                              'title' =&gt; __('Custom Ondemand API key', 'my_shipping_method'),
                              'type' =&gt; 'text',
                              'description' =&gt; __('Custom Ondemand merchant API key. You can find it in your Custom Ondemand merchant profile', 'my_shipping_method'),
                              'default' =&gt; __('mykey', 'my_shipping_method')
                          ),
                          'maps_API_key' =&gt; array(
                              'title' =&gt; __('Maps API key', 'my_shipping_method'),
                              'type' =&gt; 'text',
                              'description' =&gt; __('Google maps API key. You can find it Go to the &lt;a href="https://console.developers.google.com/project/_/apiui/apis/enabled"&gt;Google API Console&lt;/a&gt;' .
                                  'Click the Select a project button, then select the same project you set up for the Google Maps JavaScript API and click Open.' .
                                  'From the list of APIs on the Dashboard, look for Google Places API Web Service.' .
                                  'If you see the API in the list, you are all set. If the API is not listed, enable it:' .
                                  'At the top of the page, select ENABLE API to display the Library tab. Alternatively, from the left side menu, select Library.' .
                                  'Search for Google Places API Web Service, then select it from the results list.' .
                                  'Select ENABLE. When the process finishes, Google Places API Web Service appears in the list of APIs on the Dashboard.'
  
                                  , 'my_shipping_method'),
                              'default' =&gt; __('mapskey', 'my_shipping_method')
                          ),
                      );
                  }
  
                  /**
                   * This function is used to calculate the shipping cost. Within this function we can check for weights, dimensions and other parameters.
                   *
                   * @access public
                   * @param mixed $package
                   * @return void
                   */
                  public function calculate_shipping($package = Array())
                  {
                      $cost = 150;
                      $weight = 0;
  
                      parse_str($_POST['post_data'], $order_data);
  
                      $to_name = "";
                      $to_lat = "";
                      $to_long = "";
                      $recepient_name = "";
                      $recepient_phone = "";
                      $recepient_email = "";
  
                      if ($order_data['ship_to_different_address']) {
                          $to_name = $order_data['shipping_address_1'];
                          $to_lat = $order_data['shipping_lat'];
                          $to_long = $order_data['shipping_lng'];
                          $recepient_name = $order_data['shipping_first_name'] . " " . $order_data['shipping_last_name'];
                          $recepient_phone = isset($order_data['shipping_phone']) ? $order_data['shipping_phone'] : $order_data['billing_phone'];
                          $recepient_email = isset($order_data['shipping_email']) ? $order_data['shipping_email'] : $order_data['billing_email'];
  
                      } else {
                          $to_name = $order_data['billing_address_1'];
                          $to_lat = $order_data['billing_lat'];
                          $to_long = $order_data['billing_lng'];
                          $recepient_name = $order_data['billing_first_name'] . " " . $order_data['billing_last_name'];
                          $recepient_phone = $order_data['billing_phone'];
                          $recepient_email = $order_data['billing_email'];
                      }
  
                      foreach ($package['contents'] as $item_id =&gt; $values) {
                          $_product = $values['data'];
                          if ($_product-&gt;has_weight()) {
                              $weight = $weight + $_product-&gt;weight() * $values['quantity'];
                          }
                      }
  
  
                      $api_username = $this-&gt;settings['API_username'];
                      $api_key = $this-&gt;settings['API_key'];
                      $data = '';//data passed to the api. Tested to be okay.
  
                      $curl = curl_init();
  
                      curl_setopt_array($curl, array(
                          CURLOPT_URL =&gt;$this-&gt;CUSTOM_URL,
                          CURLOPT_RETURNTRANSFER =&gt; true,
                          CURLOPT_ENCODING =&gt; "",
                          CURLOPT_MAXREDIRS =&gt; 10,
                          CURLOPT_TIMEOUT =&gt; 30,
                          CURLOPT_HTTP_VERSION =&gt; CURL_HTTP_VERSION_1_1,
                          CURLOPT_CUSTOMREQUEST =&gt; "POST",
                          CURLOPT_POSTFIELDS =&gt; $data,
                          CURLOPT_HTTPHEADER =&gt; array(
                              "cache-control: no-cache",
                              "content-type: application/json"
                          ),
                      ));
  
                      $response = curl_exec($curl);
                      $err = curl_error($curl);
  
                      curl_close($curl);
  
                      if ($err) {
  
                          $cost = 200;
                      } else {
  //                        echo $response;
                          $result = json_decode($response, true);
                          $cost = $result['data']['amount'];
                      }
  
  
                      $rate = array(
                          'id' =&gt; $this-&gt;id,
                          'label' =&gt; $this-&gt;title,
                          'cost' =&gt; $cost
                      );
  
                      $this-&gt;add_rate($rate);
                  }
              }
          }
      }
  
      add_action('woocommerce_shipping_init', 'my_shipping_method');
      function add_my_shipping_method($methods)
      {
          $methods['my_shipping_method'] = 'my_shipping_method';
          return $methods;
      }
  
      add_filter('woocommerce_shipping_methods', 'add_my_shipping_method');
  
      function my_shipping_method_validate_order($posted)
      {
  
          $packages = WC()-&gt;shipping-&gt;get_packages();
  
          $chosen_methods = WC()-&gt;session-&gt;get('chosen_shipping_methods');
  
          if (is_array($chosen_methods) &amp;&amp; in_array('my_shipping_method', $chosen_methods)) {
  
              foreach ($packages as $i =&gt; $package) {
  
                  if ($chosen_methods[$i] != "my_shipping_method") {
  
                      continue;
  
                  }
  
                  $my_shipping_method = new my_shipping_method();
                  $weight = 0;
  
                  foreach ($package['contents'] as $item_id =&gt; $values) {
                      $_product = $values['data'];
                      if ($_product-&gt;has_weight()) {
                          $weight = $weight + $_product-&gt;weight * $values['quantity'];
                      }
                  }
  
                  $weight = wc_get_weight($weight, 'kg');
              }
          }
      }
  
  //    add_action('woocommerce_review_order_before_cart_contents', 'my_shipping_method_validate_order', 10);
  //    add_action('woocommerce_after_checkout_validation', 'my_shipping_method_validate_order', 10);
  
      // Hook in
      add_filter('woocommerce_checkout_fields', 'my_shipping_method_override_checkout_fields');
  
      // Our hooked in function - $fields is passed via the filter!
      function my_shipping_method_override_checkout_fields($fields)
      {
          // load the autocomplete script
          wp_enqueue_script('my_shipping_method-woocommerce', plugin_dir_url(__FILE__) . 'js/my_shipping_method-woocommerce.js', array('mapsPlaces'));
  
          $fields['shipping']['shipping_lat'] = array(
              'label' =&gt; __('Latitude', 'my_shipping_method'),
              'placeholder' =&gt; _x('Lat', 'placeholder', 'my_shipping_method'),
              'required' =&gt; true,
              'class' =&gt; array('form-row-first', 'placesAutoComplete', 'hidden'),
              'label_class' =&gt; array('hidden'),
              'clear' =&gt; true
          );
          $fields['shipping']['shipping_lng'] = array(
              'label' =&gt; __('Longitude', 'my_shipping_method'),
              'placeholder' =&gt; _x('long', 'placeholder', 'my_shipping_method'),
              'required' =&gt; true,
              'class' =&gt; array('form-row-last', 'placesAutoComplete', 'hidden'),
              'label_class' =&gt; array('hidden'),
              'clear' =&gt; true
          );
          $fields['billing']['billing_lat'] = array(
              'label' =&gt; __('Latitude', 'my_shipping_method'),
              'placeholder' =&gt; _x('Lat', 'placeholder', 'my_shipping_method'),
              'required' =&gt; true,
              'class' =&gt; array('form-row-first', 'placesAutoComplete', 'hidden'),
              'label_class' =&gt; array('hidden'),
              'clear' =&gt; true
          );
          $fields['billing']['billing_lng'] = array(
              'label' =&gt; __('Longitude', 'my_shipping_method'),
              'placeholder' =&gt; _x('long', 'placeholder', 'my_shipping_method'),
              'required' =&gt; true,
              'class' =&gt; array('form-row-last', 'placesAutoComplete', 'hidden'),
              'label_class' =&gt; array('hidden'),
              'clear' =&gt; true
          );
  
  
          return $fields;
      }
  
      /**
       * Display field value on the order edit page
       */
  
      add_action('woocommerce_admin_order_data_after_shipping_address', 'my_shipping_method_custom_checkout_field_display_admin_order_meta', 10, 1);
  
      function my_shipping_method_custom_checkout_field_display_admin_order_meta($order)
      {
          echo '&lt;p&gt;&lt;strong&gt;' . __('Delivery location') . ':&lt;/strong&gt; ' . get_post_meta($order-&gt;get_id(), '_shipping_location', true) . '&lt;/p&gt;';
      }
  
      /**
       * Process the checkout
       */
      add_action('woocommerce_checkout_process', 'location_checkout_field_process');
  
      function location_checkout_field_process()
      {
  //        // Check if set, if its not set add an error.
  //        if (!$_POST['shipping_location'] &amp;&amp; !$_POST['billing_location'])
  //            wc_add_notice(__('Please enter a biling location.'), 'error');
  //
  //    }
          // FORCE SHIPPING COST RECALCULATION
  
          WC()-&gt;cart-&gt;calculate_shipping();
          WC()-&gt;cart-&gt;calculate_totals();
  
      }
  }
  </code></pre>