UPS Negotitated Rates and Magento

We’re in the process of implementing Magento for another client and throughout the process I’ve been finding information scattered here and there about various pitfalls and problems that may come up when migrating an e-commerce site to Magento. I’m going to start posting them to the blog so that not only can our team find the answers, workarounds and fixes for these Magento issues quickly and easily in the future, but hopefully it’ll save someone else out there quite a bit of time banging their head on their keyboard trying to diagnose these problems!
We’re having trouble getting UPS rates to work via the XML interface; they worked great via Magento’s default UPS integration, but this particular customer needed integration via the XML interface so that they could pull their negotiated rates from UPS.
To do this, you change the API type to UPS XML in the Config section of Magento. However, there’s a few “gotchas”, as documented in this forum post – first of all, the URL’s that Magento uses by default are old and deprecated. The new URL’s should be:
Rates: https://onlinetools.ups.com/ups.app/xml/Rate
Tracking: https://onlinetools.ups.com/ups.app/xml/Track
Second, according to that post, there may be issues with the UPS SSL certificate on that server. I don’t see any issues here, but if you encounter problems, the workaround recommended by that developer is to add the following two lines just about both places “$xmlResponse = curl_exec($ch);” appears in app/code/core/Mage/Usa/Model/Shipping/Carrier/Ups.php” – the lines to add are:
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
I am NOT a fan of this workaround, since it disables the checks to ensure the SSL certificate the server you have connected to is valid and you haven’t been connected to an imposter.
Finally, after changing the XML URLs, we were still having trouble. By default, Magento does very little logging, but if you have logging enabled and the directory var/log exists and is writeable by your webserver’s user, you can send your own output to the Magento log with the command Mage::log(); i.e., Mage::log(“This is an error! ” . $error);
We added Mage::log($xmlResponse); after the call to curl in app/code/core/Mage/Usa/Model/Shipping/Carrier/Ups.php and discovered the following response from UPS in our log:
<ErrorDescription>Packages must weigh more than zero pounds.</ErrorDescription>
Apparently the items in the user’s cart had a weight of 0.01, which Magento was sending as 0.01 but UPS wasseeing as 0. A quick hack in app/code/core/Mage/Usa/Model/Shipping/Carrier/Ups.php fixed this, by defaulting any package weighing less than 1 pound to a weight of 1 pound.
Specifically, we changed this code:
protected function _getXmlQuotes()
{
$url = $this->getConfigData(‘gateway_xml_url’);
$this->setXMLAccessRequest();
$xmlRequest=$this->_xmlAccessRequest;
$r = $this->_rawRequest;
$params = array(
‘accept_UPS_license_agreement’ => ‘yes’,
’10_action’ => $r->getAction(),
’13_product’ => $r->getProduct(),
’14_origCountry’ => $r->getOrigCountry(),
’15_origPostal’ => $r->getOrigPostal(),
‘origCity’ => $r->getOrigCity(),
‘origRegionCode’ => $r->getOrigRegionCode(),
’19_destPostal’ => $r->getDestPostal(),
’22_destCountry’ => $r->getDestCountry(),
‘destRegionCode’ => $r->getDestRegionCode(),
’23_weight’ => $r->getWeight(),
’47_rate_chart’ => $r->getPickup(),
’48_container’ => $r->getContainer(),
’49_residential’ => $r->getDestType(),
);
To this (changes in bold)…
protected function _getXmlQuotes()
{
$url = $this->getConfigData(‘gateway_xml_url’);
$this->setXMLAccessRequest();
$xmlRequest=$this->_xmlAccessRequest;
$r = $this->_rawRequest;
$weight = $r->getWeight();
if ( ($weight < 1) OR (is_null($weight)) )
$weight = 1;
$params = array(
‘accept_UPS_license_agreement’ => ‘yes’,
’10_action’ => $r->getAction(),
’13_product’ => $r->getProduct(),
’14_origCountry’ => $r->getOrigCountry(),
’15_origPostal’ => $r->getOrigPostal(),
‘origCity’ => $r->getOrigCity(),
‘origRegionCode’ => $r->getOrigRegionCode(),
’19_destPostal’ => $r->getDestPostal(),
’22_destCountry’ => $r->getDestCountry(),
‘destRegionCode’ => $r->getDestRegionCode(),
’23_weight’ => $weight,
’47_rate_chart’ => $r->getPickup(),
’48_container’ => $r->getContainer(),
’49_residential’ => $r->getDestType(),
);
Hope you found this helpful! If you still have trouble, leave a question in the comments and we’ll try to help out as best we can.