Home » Php » php – Get fields from checkout form into calculate_shipping

php – Get fields from checkout form into calculate_shipping

Posted by: admin July 12, 2020 Leave a comment


I’m creating a custom shipping calculator for my WooCommerce store. I need to collect some extra form data from the user to calculate the shipping properly.

I have the form fields added to woocommerce_checkout_after_customer_details. I can probably get this into the cart page without much trouble once I figure out the checkout page functionality, so let’s focus on this.

I have a class extending WC_Shipping_Method with a calculate_shipping method a la this tutorial.

In that method, I want to use the extra form data (plus the destination) to calculate the shipping for that customer. Right now I just have this adding a dummy rate, which shows up.

I also created a custom cart-shipping.php file to not show any inputs for the available method in the order review cart, just the label and cost. Since the picking happens in my custom form, having options here is unnecessary.

  1. Am I doing this right, or is this approach super hacky?
  2. If this is the correct approach, how do I access the extra form fields inside the calculate_shipping method?

Have tried

Named the final options in my custom form shipping_method_* which triggers the wc-ajax=update_order_review call… which inits my custom shipping method class, but does not appear to ever call the calculate_shipping method anyhow. The only time this method seems to be called is when I’m actually adding a product to the order.

Also tried

Defined all of my possible delivery options and added them in calculate_shipping like $this->add_rate( $rate );. I think then I can somehow select one (force the user into one) when the cart updates via update_order_review ajax call? Again, the set option should be determined by these fields the user interacts with on the checkout form. But I haven’t figured out how to set the shipping on that action yet.

How to&Answers:

calculate_shipping() is not always called

First off, you should know that the calculate_shipping() method of your shipping method class will only be called when the shipping rates are calculated for the first time (in the current WooCommerce session), each time after a product is added to the cart, and whenever the shipping address is changed — from the cart or checkout page.

But of course, a plugin or custom code can programmatically call the method (or re-calculate the rates) at any times. However, the default behavior is as follows:

Rates are stored in the session based on the package hash to avoid
re-calculation every page load

And the calculate_shipping() method is executed by the WC_Shipping class through its calculate_shipping_for_package() method. In fact, the above quote was taken from the description of WC_Shipping::calculate_shipping_for_package() which calls WC_Shipping_Method::get_rates_for_package() and eventually the calculate_shipping() method in your own class.

How to get the submitted value of a form field in the checkout form from the calculate_shipping() method

So if you have this in the checkout form:

<input type="text" name="my_field"...>

Then you can get the submitted (POSTed) value using either the $_POST superglobal or the WC_Checkout::get_value() method:

  1. $_POST['my_field']

  2. WC()->checkout->get_value( 'my_field' )

It’s as simple as that. 🙂

To ensure calculate_shipping is called

Run something similar to the following to clear out the session information, which will indicate that calculation still needs to be done. Be aware that if you do this on every page, it will mean that the shipping is constantly being recalculated when it doesn’t need to be.

$packages = WC()->cart->get_shipping_packages();
foreach( $packages as $package_key => $package ) {
    $session_key  = 'shipping_for_package_'.$package_key;
    $stored_rates = WC()->session->__unset( $session_key );


I’m not sure if this is the best way to accomplish this, but currently I am making this work by setting a session value on woocommerce_checkout_update_order_review and then accessing that value in my calculate_shipping method.


Looking at the WooCommerce documentation, it seems their desired way for you to access things such as user location from the calculate_shipping function is to use their Settings API. https://docs.woocommerce.com/document/settings-api/. Read the docs to see exactly how, but here is a simplification:

Read the location using

$this->location = $this->settings['location'];

Write/Set the user’s location with this table:

<?php function admin_options() {
    <h2><?php _e('You plugin name','woocommerce'); ?></h2>
     <table class="form-table">
     <?php $this->generate_settings_html(); ?>

The table would be made with

function init_form_fields() {
     $this->form_fields = array(
     'location' => array(
          'title' => __( 'Location', 'woocommerce' ),
          'type' => 'text',
          'description' => __( 'This is the users address.', 'woocommerce' ),
          'default' => __( 'none', 'woocommerce' )