This website uses cookies

Our website, platform and/or any sub domains use cookies to understand how you use our services, and to improve both your experience and our marketing relevance.

📣 Try the fastest hosting platform with pay-as-you-go pricing & 24/7 expert support! MIGRATE NOW →

Create a WooCommerce Custom Shipping Method Plugin

Updated on June 14, 2021

7 Min Read

In this tutorial, I will  create a simple shipping method for WooCommerce, which will calculate the cost of the shipping.

WooCommerce Custom Shipping Method

Before I begin, I will need to define how this shipping method will calculate the cost and where it can ship. The cost will be determined by the weight and by the zone where the shipment is going.

  • Spain
  • United Kingdom
  • USA

WooCommerce Shipping Method API

When creating a shipping method, I will extend the class from the WooCommerce abstract class WC_Shipping_Method. The defined attributes in this class are:

$id: ID (slug, keyword) of the shipping. Required.

$number: Integer ID.

$method_title: Name of the shipping as shown in the admin.

$method_description: Short description of the shipping as shown in the admin (Optional).

$enabled: String Boolean (“yes” or “no”) that gives the information if the shipping is enabled and can be used or not.

$title: Used to display the shipping name on the store.

$availability: Defines if the shipping is available or not.

$countries: Array of countries this method is enabled for. Default value is an empty array.

$tax_status: Default value is taxable. If it is taxable then the tax will be charged.

$fee: Default value is 0. Fees for the method.

$minimum_fee: The minimum fee for the method. Default value is null.

$has_settings: Defines if this method has any settings. Default value is true.

$supports: Array containing features this method supports. Default value is an empty array.

$rates: Array of rates. This must be populated to register shipping costs. Default value is an empty array.

The defined methods in the WC_Shipping_Method are:

is_taxable(): Returns whether or not tax needs to be calculated on top of the shipping rate.

add_rate( $args = array() ): Pushes a shipping rate defined in the parameter $args into the attribute $rates.

has_settings(): Returns the value of the attribute $has_settings.

is_available(): Returns if the shipping is available. If there are countries set in the attribute $countries and the attribute $availability is set to values including, specific or excluding, it will return true or false if the country is available for shipping.

get_title(): Returns the title of this shipping.

get_fee( $fee, $total ): Returns the fee value for this shipping based on the parsed $fee and $total.

supports( $feature ): Returns if this shipping method supports a feature or not.

Since the class WC_Shipping_Method extends the class WC_Settings_API, many more attributes and methods are available. However, to keep things simple, I will not go into the details of these elements.

At this point, I need to define several other methods so that the shipping method could GET or SET settings and also calculate the actual cost of the shipping. These methods are:

  • init(): Creates the form fields and settings (can be named differently, as long as I use the methods inside it and call it in the __constructor method).
  • calculate_shipping( $package ): This method is used to calculate the cost for this particular shipping. Package is an array that contains all the products included in the shipment.

In the calculate_shipping method, I will add the rate with the method add_rate. This method accepts an array with the following options:

  • id: ID of the rate.
  • label: Label for the rate.
  • cost: Amount of the shipping. It can be a single value or an array with costs of each item in the cart.
  • taxes : It accepts an array of taxes or nothing so the tax is calculated by WooCommerce. It can even accept false so that the tax will not be calculated.

calc_tax: Accepts per_order or per_item. If you use per_item, an array of costs is required.

To register the shipping method, I will add this shipping method in the array of the registered method by passing the name of the class. I will access this array and send the modified array back using a WordPress filter that is defined inside the WooCommerce plugin. The filter is called woocommerce_shipping_methods.

Create Shipping Method as a New Plugin

I  will create this shipping method as a new plugin that extends the WooCommerce plugin.

Create a new folder Cloudways under wp-content/plugins. Also, create a file with the same name cloudways-shipping.php and add this code:

<?php
/**
 * Plugin Name: Cloudways
 * Description: Create a woocommerce custom shipping method plugin
 */
if ( ! defined( 'WPINC' ) ){
 die('security by preventing any direct access to your plugin file');
}
if (in_array('woocommerce/woocommerce.php', apply_filters('active_plugins', get_option('active_plugins')))) {
 function cloudways_shipping_method()
 {
 if (!class_exists('cloudways_Shipping_Method')) {
 class cloudways_Shipping_Method extends WC_Shipping_Method
 {
 public function __construct()
 {
 $this->id = 'cloudways';
 $this->method_title = __('cloudways Shipping', 'cloudways');
 $this->method_description = __('Custom Shipping Method for cloudways', 'cloudways');
 // Contreis availability
 $this->availability = 'including';
 $this->countries = array(
 'ES',
 'GB',
 'US',
 );
 $this->init();
 $this->enabled = isset($this->settings['enabled']) ? $this->settings['enabled'] : 'yes';
 $this->title = isset($this->settings['title']) ? $this->settings['title'] : __('cloudways Shipping', 'cloudways');
 }

 /**
 Load the settings API
 */
 function init()
 {
 $this->init_form_fields();
 $this->init_settings();
 add_action('woocommerce_update_options_shipping_' . $this->id, array($this, 'process_admin_options'));
 }

 function init_form_fields()
 {
 $this->form_fields = array(
 'enabled' => array(
 'title' => __('Enable', 'cloudways'),
 'type' => 'checkbox',
 'default' => 'yes'
 ),
 'weight' => array(
 'title' => __('Weight (kg)', 'cloudways'),
 'type' => 'number',
 'default' => 50
 ),
 'title' => array(
 'title' => __('Title', 'cloudways'),
 'type' => 'text',
 'default' => __('cloudways Shipping', 'cloudways')
 ),
 );
 }

 public function cloudways_shipping_calculation($package)
 {
 $weight = 0;
 $cost = 0;
 $country = $package["destination"]["country"];
 foreach ($package['contents'] as $item_id => $values) {
 $_product = $values['data'];
 $weight = $weight + $_product->get_weight() * $values['quantity'];
 }
 $weight = wc_get_weight($weight, 'kg');
 if ($weight <= 5) {
 $cost = 0;
 } elseif ($weight <= 25) {
 $cost = 5;
 } elseif ($weight <= 45) {
 $cost = 10;
 } else {
 $cost = 15;
 }
 $countryZones = array(
 'ES' => 2,
 'GB' => 2,
 'US' => 3
 );
 $zonePrices = array(
 2 => 50,
 3 => 70
 );
 $zoneFromCountry = $countryZones[$country];
 $priceFromZone = $zonePrices[$zoneFromCountry];
 $cost += $priceFromZone;
 $rate = array(
 'id' => $this->id,
 'label' => $this->title,
 'cost' => $cost
 );
 $this->add_rate($rate);
 }
 }
 }
 }

 add_action('woocommerce_shipping_init', 'cloudways_shipping_method');
 function add_cloudways_shipping_method($methods)
 {
 $methods[] = 'cloudways_Shipping_Method';
 return $methods;
 }

 add_filter('woocommerce_shipping_methods', 'add_cloudways_shipping_method');
 function cloudways_validate_order($posted)
 {
 $packages = WC()->shipping->get_packages();
 $chosen_methods = WC()->session->get('chosen_shipping_methods');
 if (is_array($chosen_methods) && in_array('cloudways', $chosen_methods)) {
 foreach ($packages as $i => $package) {
 if ($chosen_methods[$i] != "cloudways") {
 continue;
 }
 $cloudways_Shipping_Method = new cloudways_Shipping_Method();
 $weightLimit = (int)$cloudways_Shipping_Method->settings['weight'];
 $weight = 0;
 foreach ($package['contents'] as $item_id => $values) {
 $_product = $values['data'];
 $weight = $weight + $_product->get_weight() * $values['quantity'];
 }
 $weight = wc_get_weight($weight, 'kg');
 if ($weight > $weightLimit) {
 $message = sprintf(__('OOPS, %d kg increase the maximum weight of %d kg for %s', 'cloudways'), $weight, $weightLimit, $cloudways_Shipping_Method->title);
 $messageType = "error";
 if (!wc_has_notice($message, $messageType)) {
 wc_add_notice($message, $messageType);
 }
 }
 }
 }
 }

 add_action('woocommerce_review_order_before_cart_contents', 'cloudways_validate_order', 10);
 add_action('woocommerce_after_checkout_validation', 'cloudways_validate_order', 10);
}

This shipping method is available only in the previously determined list of countries. This will be set before the method init inside the method __construct. Add the following code to the method __construct:

public function __construct()
                {
                    $this->id = 'cloudways';
                    $this->method_title = __('cloudways Shipping', 'cloudways');
                    $this->method_description = __('Custom Shipping Method for cloudways', 'cloudways');
                    // Contreis availability
                    $this->availability = 'including';
                    $this->countries = array(
                        'ES',
                        'GB',
                        'US',
                    );
                    $this->init();
                    $this->enabled = isset($this->settings['enabled']) ? $this->settings['enabled'] : 'yes';
                    $this->title = isset($this->settings['title']) ? $this->settings['title'] : __('cloudways Shipping', 'cloudways');
                }

Now, go to WordPress Admin Panel and activate the plugin.

Activate Cloudways

Next, go to WooCommerce > Settings > Shipping > Cloudways Shipping.

Shipping method

I have also defined a weight restriction of upto 50 kg in this shipping method.

 function init_form_fields()
                {
                    $this->form_fields = array(
                        'enabled' => array(
                            'title' => __('Enable', 'cloudways'),
                            'type' => 'checkbox',
                            'default' => 'yes'
                        ),
                        'weight' => array(
                            'title' => __('Weight (kg)', 'cloudways'),
                            'type' => 'number',
                            'default' => 50
                        ),
                        'title' => array(
                            'title' => __('Title', 'cloudways'),
                            'type' => 'text',
                            'default' => __('cloudways Shipping', 'cloudways')
                        ),
                    );
                }

I calculated the cost by the total weight, and also added the cost by the shipping country. The last step here is to register the rate. For this add this last part:

public function cloudways_shipping_calculation($package)
                {
                    $weight = 0;
                    $cost = 0;
                    $country = $package["destination"]["country"];
                    foreach ($package['contents'] as $item_id => $values) {
                        $_product = $values['data'];
                        $weight = $weight + $_product->get_weight() * $values['quantity'];
                    }
                    $weight = wc_get_weight($weight, 'kg');
                    if ($weight <= 5) {
                        $cost = 0;
                    } elseif ($weight <= 25) {
                        $cost = 5;
                    } elseif ($weight <= 45) {
                        $cost = 10;
                    } else {
                        $cost = 15;
                    }
                    $countryZones = array(
                        'ES' => 2,
                        'GB' => 2,
                        'US' => 3
                    );
                    $zonePrices = array(
                        2 => 50,
                        3 => 70
                    );
                    $zoneFromCountry = $countryZones[$country];
                    $priceFromZone = $zonePrices[$zoneFromCountry];
                    $cost += $priceFromZone;
                    $rate = array(
                        'id' => $this->id,
                        'label' => $this->title,
                        'cost' => $cost
                    );
                    $this->add_rate($rate);
                }

If you try now to view your cart or go to the checkout page and select a country that is available for this shipping, you’ll get a shipping cost displayed with this custom shipping method.

Your order

In this function, I will look for the shipping method which has been chosen. If the method is Cloudways_Shipping_Method  then I will check for its weight limit and the total weight in the cart. If the weight of the cart exceeds the weight limit, I will notify the customer.

After the filter woocommerce_shipping_methods add this code:

function cloudways_validate_order($posted)
    {
        $packages = WC()->shipping->get_packages();
        $chosen_methods = WC()->session->get('chosen_shipping_methods');
        if (is_array($chosen_methods) && in_array('cloudways', $chosen_methods)) {
            foreach ($packages as $i => $package) {
                if ($chosen_methods[$i] != "cloudways") {
                    continue;
                }
                $cloudways_Shipping_Method = new cloudways_Shipping_Method();
                $weightLimit = (int)$cloudways_Shipping_Method->settings['weight'];
                $weight = 0;
                foreach ($package['contents'] as $item_id => $values) {
                    $_product = $values['data'];
                    $weight = $weight + $_product->get_weight() * $values['quantity'];
                }
                $weight = wc_get_weight($weight, 'kg');
                if ($weight > $weightLimit) {
                    $message = sprintf(__('OOPS, %d kg increase the maximum weight of %d kg for %s', 'cloudways'), $weight, $weightLimit, $cloudways_Shipping_Method->title);
                    $messageType = "error";
                    if (!wc_has_notice($message, $messageType)) {
                        wc_add_notice($message, $messageType);
                    }
                }
            }
        }
    }

Conclusion

The WooCommerce Shipping API enables you to create your own shipping method and integrate it within WooCommerce. Limitations and availability can be set within the shipping method when calculating the availability or the cost for the shipping method. However, you could also set these methods outside the shipping method by using WooCommerce actions.

If you need help with the code or would like to contribute to the discussion, do leave a comment below. Meanwhile, you can also checkout the best WooCommerce shipping plugins.

Share your opinion in the comment section. COMMENT NOW

Share This Article

Owais Alam

is the WordPress Community Manager at Cloudways - A Managed WooCommerce Hosting Platform and a seasoned PHP developer. He loves to develop all sorts of websites on WordPress and is in love with WooCommerce in particular. You can email him at [email protected]

×

Get Our Newsletter
Be the first to get the latest updates and tutorials.

Thankyou for Subscribing Us!

×

Webinar: How to Get 100% Scores on Core Web Vitals

Join Joe Williams & Aleksandar Savkovic on 29th of March, 2021.

Do you like what you read?

Get the Latest Updates

Share Your Feedback

Please insert Content

Thank you for your feedback!

Do you like what you read?

Get the Latest Updates

Share Your Feedback

Please insert Content

Thank you for your feedback!

Want to Experience the Cloudways Platform in Its Full Glory?

Take a FREE guided tour of Cloudways and see for yourself how easily you can manage your server & apps on the leading cloud-hosting platform.

Start my tour

CYBER WEEK SAVINGS

  • 0

    Days

  • 0

    Hours

  • 0

    Mints

  • 0

    Sec

GET OFFER

For 4 Months &
40 Free Migrations

For 4 Months &
40 Free Migrations

Upgrade Now