5 real-world WooCommerce snippets inspired by merchant support requests

At Woo, our Happiness Engineers spend a lot of time in the trenches with merchants, answering support tickets, debugging custom setups, and building quick solutions to unblock workflows. Sometimes, what starts as a one-off request turns into something reusable and worth sharing.

Here are five real-world snippets our Happiness Engineer, Shameem Reza, originally wrote to solve specific merchant problems. If you manage or support WooCommerce stores, these might come in handy.

1. 🚚 Show estimated shipping time on product pages

The problem:
A merchant wanted to display estimated shipping times for each product or variation without needing a heavy plugin.

The solution:
A lightweight plugin that adds a custom “Shipping Time” field to products and variations. It automatically updates on the product page when a customer selects a variation.

Features:

  • Adds a shipping time input field in the product editor.
  • Dynamically shows the shipping estimate on the frontend.
  • Compatible with variable products and default variations.
See the code 👇
<?php
/*
* Plugin Name: WooCommerce Shipping Time Display
* Plugin URI: https://wcwiz.com/how-to-show-shipping-time-on-woocommerce-product-page/
* Description: Adds a custom shipping time field to WooCommerce products and displays it on the product page.
* Version: 1.9
* Author: Shameem Reza
* Author URI: https://shameem.dev
* License: GPL2
* Text Domain: wc-shipping-time
* Domain Path: /languages
* WC requires at least: 5.0
* WC tested up to: 9.6.2
* Requires PHP: 7.3
* Requires Plugins: woocommerce
*/

if (!defined('ABSPATH')) {
    exit; // Exit if accessed directly.
}

// Declare HPOS compatibility
add_action('before_woocommerce_init', function () {
    if (class_exists('AutomatticWooCommerceUtilitiesFeaturesUtil')) {
        AutomatticWooCommerceUtilitiesFeaturesUtil::declare_compatibility('custom_order_tables', __FILE__, true);
    }
});

/**
 * Add shipping time field to WooCommerce simple products.
 */
function wcst_add_shipping_time_field() {
    woocommerce_wp_text_input([
        'id'          => '_shipping_time',
        'label'       => __('Shipping Time', 'wc-shipping-time'),
        'description' => __('Enter shipping time, e.g., 24H, 48H, 3 days', 'wc-shipping-time'),
        'desc_tip'    => true,
    ]);
}
add_action('woocommerce_product_options_general_product_data', 'wcst_add_shipping_time_field');

/**
 * Save the shipping time field data for simple products.
 */
function wcst_save_shipping_time_field($post_id) {
    if (isset($_POST['_shipping_time'])) {
        update_post_meta($post_id, '_shipping_time', sanitize_text_field($_POST['_shipping_time']));
    }
}
add_action('woocommerce_process_product_meta', 'wcst_save_shipping_time_field');

/**
 * Add shipping time field to variations.
 */
function wcst_add_shipping_time_to_variations($loop, $variation_data, $variation) {
    woocommerce_wp_text_input([
        'id'          => "variation_shipping_time_$variation->ID",
        'label'       => __('Shipping Time', 'wc-shipping-time'),
        'description' => __('Enter shipping time for this variation, e.g., 24H, 48H, 3 days', 'wc-shipping-time'),
        'desc_tip'    => true,
        'type'        => 'text',
        'value'       => get_post_meta($variation->ID, '_shipping_time', true),
    ]);
}
add_action('woocommerce_variation_options_pricing', 'wcst_add_shipping_time_to_variations', 10, 3);

/**
 * Save shipping time for variations.
 */
function wcst_save_shipping_time_variation($variation_id) {
    if (isset($_POST["variation_shipping_time_$variation_id"])) {
        update_post_meta($variation_id, '_shipping_time', sanitize_text_field($_POST["variation_shipping_time_$variation_id"]));
    }
}
add_action('woocommerce_save_product_variation', 'wcst_save_shipping_time_variation', 10, 2);

/**
 * Pass variation shipping time data to WooCommerce variation system.
 */
function wcst_add_variation_shipping_time_data($data, $product, $variation) {
    if (!is_array($data)) {
        $data = [];
    }
    $shipping_time = get_post_meta($variation->get_id(), '_shipping_time', true);
    $data['shipping_time'] = !empty($shipping_time) ? $shipping_time : '';
    
    return $data;
}
add_filter('woocommerce_available_variation', 'wcst_add_variation_shipping_time_data', 10, 3);

/**
 * Display the shipping time on the product page and support default variations.
 */
function wcst_display_shipping_time() {
    global $product;

    if (!$product || !is_a($product, 'WC_Product')) {
        return;
    }

    $shipping_time = '';

    // Handle default variation selection
    if ($product->is_type('variable')) {
        $default_attributes = $product->get_default_attributes();
        foreach ($product->get_available_variations() as $variation) {
            $match = true;
            foreach ($default_attributes as $attribute => $value) {
                if ($variation['attributes']['attribute_' . $attribute] !== $value) {
                    $match = false;
                    break;
                }
            }
            if ($match) {
                $variation_id = $variation['variation_id'];
                $shipping_time = get_post_meta($variation_id, '_shipping_time', true);
                break;
            }
        }
    }

    // Fallback to parent product shipping time if no variation has shipping time
    if (empty($shipping_time)) {
        $shipping_time = get_post_meta($product->get_id(), '_shipping_time', true);
    }

    ?>
    <p id="wcst-shipping-time" style="display: <?php echo empty($shipping_time) ? 'none' : 'block'; ?>;">
        <strong><?php echo esc_html__('Shipping Time:', 'wc-shipping-time'); ?></strong>
        <span><?php echo esc_html($shipping_time); ?></span>
    </p>

    <script type="text/javascript">
        jQuery(document).ready(function($) {
            $('form.variations_form').on('found_variation', function(event, variation) {
                if (variation.shipping_time) {
                    $('#wcst-shipping-time').show();
                    $('#wcst-shipping-time span').text(variation.shipping_time);
                } else {
                    $('#wcst-shipping-time').hide();
                }
            });

            $('form.variations_form').on('reset_data', function() {
                $('#wcst-shipping-time').hide();
            });
        });
    </script>
    <?php
}
add_action('woocommerce_single_product_summary', 'wcst_display_shipping_time', 20);

2. 🎁 Stop auto-renewal for gifted subscriptions

The problem:
Could gifted subscriptions expire after a year without charging the gift giver again?

The solution:
This snippet:

  • Automatically sets them to manual renewal.
  • Cancels any scheduled payments in the background.

Bonus: It logs everything to help with debugging.

See the code 👇
// Hook into the subscription creation process
add_action( 'woocommerce_checkout_subscription_created', 'set_gifted_subscription_to_manual_renewal', 10, 2 );

function set_gifted_subscription_to_manual_renewal( $subscription, $order ) {
    // Check if the subscription is gifted using the gifting plugin's functionality
    if ( class_exists( 'WCS_Gifting' ) && WCS_Gifting::is_gifted_subscription( $subscription ) ) {
        // Set the subscription to manual renewal
        $subscription->set_requires_manual_renewal( true );

        // Cancel any scheduled renewal actions
        as_unschedule_all_actions( 'woocommerce_scheduled_subscription_payment', [ 'subscription_id' => $subscription->get_id() ] );

        // Log the action for debugging
        wc_get_logger()->info(
            'Gifted subscription ID ' . $subscription->get_id() . ' renewals canceled and set to manual.',
            [ 'source' => 'woocommerce' ]
        );

        // Save the subscription changes
        $subscription->save();
    }
}

// Additional safeguard: Ensure gifted subscriptions remain manual during payment
add_action( 'woocommerce_subscription_payment_complete', 'cancel_gifted_subscription_renewals', 10, 1 );

function cancel_gifted_subscription_renewals( $subscription ) {
    // Verify gifted subscription using the gifting plugin
    if ( class_exists( 'WCS_Gifting' ) && WCS_Gifting::is_gifted_subscription( $subscription ) ) {
        // Cancel scheduled renewal actions
        as_unschedule_all_actions( 'woocommerce_scheduled_subscription_payment', [ 'subscription_id' => $subscription->get_id() ] );

        // Log the cancellation for confirmation
        wc_get_logger()->info(
            'Gifted subscription ID ' . $subscription->get_id() . ' scheduled renewals canceled post-payment.',
            [ 'source' => 'woocommerce' ]
        );
    }
}

3. 📦 Restock items when subscriptions are canceled

The problem:
A developer needed inventory to reflect real-time changes when subscriptions are canceled so that products could be resold right away.

The solution:
This function:

  • Restocks items for both simple and variable products.
  • Adjusts stock based on the quantity ordered.

It’s one of those “set it and forget it” helpers that improves inventory accuracy.

See the code 👇
add_action('woocommerce_subscription_status_cancelled', 'adjust_inventory_on_subscription_cancel', 10, 1);

function adjust_inventory_on_subscription_cancel($subscription) {
    if (!$subscription) return;

    foreach ($subscription->get_items() as $item) {
        $product = $item->get_product();
        $variation_id = $item->get_variation_id();

        // Ensure the product is variable and has stock management enabled
        if ($product && $product->managing_stock()) {
            $current_stock = $product->get_stock_quantity();
            $item_quantity = $item->get_quantity();

            // Adjust stock for the specific variation or parent product
            $new_stock = $current_stock + $item_quantity;

            if ($variation_id) {
                $variation = wc_get_product($variation_id);
                if ($variation && $variation->managing_stock()) {
                    $variation->set_stock_quantity($new_stock);
                    $variation->save();
                }
            } else {
                $product->set_stock_quantity($new_stock);
                $product->save();
            }
        }
    }
}

4. 🚫 Instantly cancel ‘pending-cancel’ subscriptions

The problem:
Some merchants want subscriptions to be canceled immediately when a customer hits “cancel,” not after the billing period ends.

The solution:
A simple listener that:

  • Detects when a subscription status changes to pending-cancel.
  • Immediately updates it to cancelled.
  • Logs the change for traceability.
See the code 👇
add_action( 'woocommerce_subscription_status_updated', 'auto_cancel_pending_cancellations', 10, 3 );

function auto_cancel_pending_cancellations( $subscription, $new_status, $old_status ) {
    // Ensure we are dealing with a valid subscription object
    if ( is_a( $subscription, 'WC_Subscription' ) ) {
        // Check if the new status is 'pending-cancel'
        if ( 'pending-cancel' === $new_status ) {
            // Attempt to cancel the subscription
            try {
                $subscription->update_status( 'cancelled' );
                // Optionally log the cancellation
                error_log( 'Subscription ID ' . $subscription->get_id() . ' has been automatically cancelled.' );
            } catch ( Exception $e ) {
                // Log any errors
                error_log( 'Error cancelling subscription ID ' . $subscription->get_id() . ': ' . $e->getMessage() );
            }
        }
    }
}

5. 🙈 Hide bundles when required products are out of stock

The problem:
A merchant wanted bundled products to disappear if any required item inside the bundle was out of stock.

The solution:
A visibility filter that:

  • Checks the stock status of each bundled item.
  • Hides the parent bundle if any required product is unavailable.
See the code 👇

//Hide the Bundle When a product is Out of Stock
add_filter( 'woocommerce_product_is_visible', 'hide_bundle_when_required_product_is_out_of_stock', 10, 2 );

function hide_bundle_when_required_product_is_out_of_stock( $visible, $product_id ) {
    $product = wc_get_product( $product_id );

    // Check if the product is a bundle
    if ( $product && $product->is_type( 'bundle' ) ) {
        $bundled_items = $product->get_bundled_items();

        // Loop through all bundled items
        foreach ( $bundled_items as $bundled_item ) {
            // Check if it's a required product
            if ( ! $bundled_item->is_optional() ) {
                $bundled_product = $bundled_item->get_product();

                // If the required product is out of stock, hide the bundle
                if ( $bundled_product && ! $bundled_product->is_in_stock() ) {
                    return false;
                }
            }
        }
    }

    return $visible;
}


4 responses to “5 real-world WooCommerce snippets inspired by merchant support requests”

  1. Really helpful! But shouldn’t number 3 and 5 be standard behaviour of WooCommerce?

    1. Barry Hughes Avatar
      Barry Hughes

      That’s a great question!

      In relation to the third snippet (“restock items when subscriptions are canceled”), I’m not sure the behavior it encapsulates would always be ideal—simply because cancellation of the subscription doesn’t mean the merchant recovered (or even expects to recover) the stock.

      The customer might have been quite happy to pay for and receive the first shipment, but could then decide that they don’t want the subscription to continue. In these cases, it doesn’t really make sense to automatically re-stock.

      However, perhaps this could be introduced as a setting. We can certainly think about that 👍🏼

  2. Mahfuzur Rahman Avatar
    Mahfuzur Rahman

    This one was great! Would love to see more content like this one!

  3. The first point, “shipping time” snippet, is a lifesaver! Publish more articles like this from the support part.

Leave a Reply

Your email address will not be published. Required fields are marked *