Magento 1 Checkout – Resolving ‘Please Specify a Shipping Method’ Exception

checkoutmagento-1shipping

I've been getting exception logs for this error in production, but I'm unable to reproduce the issue in my local or staging environment, so it's been pretty hard to troubleshoot.

The error originates in Mage_Sales_Model_Service_Quote::_validate() because the $rate returned by $rate = $address->getShippingRateByCode($method) is empty.

I've added in some logging to try and get a better idea of what was going on, and I can see that $method contains the correct shipping method.

My best guess is that at some point in the process, the shipping rates are being deleted prior to when they should be.

I've noticed that every time this exception occurs, it happens immediately after a legitimate exception, such as an invalid credit card. I've tried to reproduce the issue by using an invalid credit card, then a valid one, but it doesn't reproduce for me – in staging, production, or local.

My initial hunch was that maybe the shipping method was getting lost somewhere after the first valid exception, but that's not the case, because I see that $method has the correct value at the time that this exception is thrown.

The checkout module that I'm using is AwesomeCheckout – it doesn't really to my knowledge have any custom logic when creating orders that should cause problems here, but might be related.

UPDATE: I've added in some code to attempt to recollect the rates if they are missing.

protected function _validate()
{
    if (!$this->getQuote()->isVirtual()) {
        $address = $this->getQuote()->getShippingAddress();
        $addressValidation = $address->validate();
        if ($addressValidation !== true) {
            Mage::throwException(
                Mage::helper('sales')->__('Please check shipping address information. %s', implode(' ', $addressValidation))
            );
        }
        $method= $address->getShippingMethod();
        $rate  = $address->getShippingRateByCode($method);

        /**
         * Start Customization
         */
        if (!$this->getQuote()->isVirtual() && !$rate) {
            Mage::logException(new Exception("Rate was empty inside quote validate method, trying to forcefully recalculate"));
            $this->getQuote()->getShippingAddress()->setCollectShippingRates(true);
            $this->getQuote()->setTotalsCollectedFlag(false);
            $this->getQuote()->collectTotals();
            $rate  = $address->getShippingRateByCode($method);
        }
        /** End Customization **/             

        if (!$this->getQuote()->isVirtual() && (!$method || !$rate)) {
            Mage::throwException(Mage::helper('sales')->__('Please specify a shipping method.'));
        }
    }

Best Answer

You have to understand how the rates work and how they are requested. Basically rates are requested when ->setCollectShippingRates(true) is set on shippinAddress object and it result rates to be collected and stored in rates table. This table is emptied afterwards and filled again on new rate request.

what is happening in your case is that error is thrown and request is repeated and rates are not requested but are expected to be there. So try and force the rates collection over

getQuote()->getShippingAddress()->setCollectShippingRates(true);

and then try to recollect totals as well if it does not work

getQuote()->setTotalsCollectedFlag(false)->collectTotals();

be warned that calling collectTotals multiple times can mess up your totals if some extension does not implement totals objects correctly (a common flaw)