How to fix the flaw in the WP Ecommerce table rate shipping module

January 25th, 2012 by Sean Leave a reply »

Catchy title eh! It’s difficult to name such a post but I have just diagnosed a client’s site and realised that there was a bit of an error in the WPEC (WPSC) table rate shipping module.

Reading the code it looks like the table rate module is designed to be used exclusively or in conjunction with a secondary service. The flaw I have found is that table rate is coded to always return a price regardless of whether you want one or not.

Basically my client wanted to use the weight shipping module for all postage except if the cart amount was more than £200 in which case free postage would be offered. Simple right?.. just add a layer (row) into the table rate settings with a minimum price of 200 and a value of 0. This would mean that weight based is used until the table rate conditional kicks in at £200 and offers cheaper postage (free in fact).

No.. sadly not correct :(

Table rate is written so that is always returns a price regardless of whether you want it to or not. Basically if you have a single row (layer) in the settings for the module it will show it. The system works well at the top end where price is greater than or equal to but there must always be a base price in the module (value of 0.00 or 0.01 if it doesn’t save) to catch those which fail the PHP logic. Note the following function to get a quote (taken directly from tablerate.php):

    function getQuote() {

        global $wpdb, $wpsc_cart;
        if (isset($_SESSION['nzshpcrt_cart'])) {
            $shopping_cart = $_SESSION['nzshpcrt_cart'];
        }
        if (is_object($wpsc_cart)) {
            $price = $wpsc_cart->calculate_subtotal(true);
        }

        $layers = get_option('table_rate_layers');

        if ($layers != '') {

            // At some point we should probably remove this as the sorting should be
            // done when we save the data to the database. But need to leave it here
            // for people who have non-sorted settings in their database
            krsort($layers);

            foreach ($layers as $key => $shipping) {

                if ($price >= (float)$key) {

                    if (stristr($shipping, '%')) {

                        // Shipping should be a % of the cart total
                        $shipping = str_replace('%', '', $shipping);
                        $shipping_amount = $price * ( $shipping / 100 );

                    } else {

                        // Shipping is an absolute value
                        $shipping_amount = $shipping;

                    }

                    return array("Table Rate"=>$shipping_amount);

                }

            }

            $shipping = array_shift($layers);

            if (stristr($shipping, '%')) {
                $shipping = str_replace('%', '', $shipping);
                $shipping_amount = $price * ( $shipping / 100 );
            } else {
                $shipping_amount = $shipping;
            }

            return array("Table Rate"=>$shipping_amount);

        }
    }

If you read through the code it basically loops through all of the layers to find the conditional price point that matches and returns the price. The oddity is the next set of code which, I suppose, is a fall back. Sadly the fall back is entirely unnecessary and causes this issue. The code starts from the following onwards:

$shipping = array_shift($layers);

In fact the simplest way to ‘fix’ the function is to put a return  statement just before that line:

return false;
$shipping = array_shift($layers);

Once that is in place you are free to use the module properly once again. I might write this as a separate shipping module for people to download as changing the name of each layer would be a nice idea as well as setting a maximum price point for each layer.

Leave a Reply