Writing a WooCommerce Shipping Method

Lately, we’ve been working on a custom DHL integration for WooCommerce. As far as we see, Woo really did a great job according to extending the base shipping functionality. Let’s write a custom shipping method.

When is it appropriate to have a custom shipping method?

Basically, you can find a plugin nearly for everything nowadays. But there may be some cases when you need to bring your own, custom logic to calculate the shipping fee. So we will cover the main features here to present how widely you can integrate your own logic with WooCommerce.

Extending the Shipping Methods

Everything starts with the hook, that you need to call to add your shipping method. We just push our custom class to the existing shipping methods’ array.

add_filter('woocommerce_shipping_methods', function ($methods) {
    $methods['custom_shipping'] = CustomShipping::class;

    return $methods;
});
You may create a plugin for the shipping method. In case of that, you should put this code in your plugin’s codebase.

The Shipping Method

Let’s see the whole code first, then everything will be explained. But make it clear at the very beginning, this code shows only the very basics. We will talk about other possibilities at the end of the post.

class Shipping extends WC_Shipping_Method
{
    /**
     * The ID of the shipping method.
     *
     * @var string
     */
    public $id = 'custom_shipping';

    /**
     * The title of the method.
     *
     * @var string
     */
    public $method_title = 'Custom Shipping';

    /**
     * The description of the method.
     *
     * @var string
     */
    public $method_description = 'Custom Shipping';

    /**
     * The supported features.
     *
     * @var array
     */
    public $supports = [
        'settings',
    ];

    /**
     * Initialize a new shipping method instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->init_form_fields();
        $this->init_settings();
        $this->registerHooks();

        $this->enabled = isset($this->settings['enabled']) ? $this->settings['enabled'] : 'no';
        $this->title = isset($this->settings['title']) ? $this->settings['title'] : 'Custom Shipping';
    }

    /**
     * Initialize the form fields.
     *
     * @return void
     */
    public function init_form_fields()
    {
        $this->form_fields = [
            'enabled' => [
                'title' => __('Enable'),
                'type' => 'checkbox',
                'description' => __('Enable this shipping method.'),
                'default' => 'yes',
            ],
           'title' => [
              'title' => __('Title'),
                'type' => 'text',
                'description' => __('Title to be display on site.'),
                'default' => __('Custom Shipping'),
            ],
        ];
    }

    /**
     * Calculate the shipping fees.
     *
     * @param  array  $package
     * @return void
     */
    public function calculate_shipping($package = [])
    {
        $ids = [];

        foreach ($package['contents'] as $id => $product) {
            $ids[] = $product['product_id'];
        }

        $this->add_rate([
            'id' => $this->id,
            'label' => $this->title,
            'cost' => in_array(12, $ids) ? 0 : 100,
        ]);
    }

    /**
     * Register the shipping method hooks.
     *
     * @return void
     */
    public function registerHooks()
    {
        add_action("woocommerce_update_options_shipping_{$this->id}", [$this, 'process_admin_options']);
    }
}

First of all, we define the $id, $method_title, $method_description and the $supports properties. These should hardcoded since these will be used for on the admin settings.

In the __construct() method, we initialize the form fields and the settings API as well. It means, from this point we can load the saved settings and fetch it to the instance. Then, we register the hooks with the callbacks.

Note, the registerHooks() is a custom method. It’s a good practice, to put your hooks in one place, especially if you have multiple.

Now, the main part, the calculate_shipping() method. So, this is the place, where the custom calculating logic comes. It accepts an array as a parameter, which contains all the information according to the customer and the cart. The addresses (shipping, billing) and the products are there, and we can work with them.

In this example code, we have a custom logic, that says, if there is a which has an ID of 12, the shipping is free, otherwise it costs 100. Is there any point? No, but it shows, you can basically do anything. It’s really up to you.

So, really you have lots of choices that help you to integrate. For example, you can set up zone and instance support for your method – with their own settings for every instance. It also increases flexibility.

Also, be brave to think out of the box. You can make an API call to request a shipping fee, or implement any logic that you need to give a proper price to the customer.

 

Special thanks for the following recource(s): Icon made by Eucalyp from www.flaticon.com
Need a web developer? Maybe we can help, get in touch!