Warning: THIS POST IS DEPRECATED! I gave up trying to hack OrderShip and wrote my own extension (http://www.illapps.com/shipsync.html).. I'm leaving the post here for reference, but I strongly suggest going with a commercial solution.
For some time now FedEx has been phasing out their legacy DC API, and encouraging developers to migrate to the new Web Services API. I had already starting dabbling in the new API to help automate shipping, but the fire was put under my tail yesterday when I received this Magento error report ->
Warning: Simplexml_load_string() function.simplexml-load-string] Entity: line 1 parser error string not closed expecting “ or “ in /app/code/core/Mage/Usa/Model/Shipping/Carrier/Fedex.php on line 432
Uh oh. It looks like a truncated or corrupted xml response. So I did a bit of googling, and discovered that others are currently having the same issues with FedEx. But, as much as I'd like to point the finger at the big guy, it turns out that Magento is using FedEx's legacy API, and this service will likely be decommissioned in the near future. So, it's not surprising that we're having trouble with this old crap. FedEx now offers a new web services API, and it is much much cooler. Thus, the solution here is to update Magento to support the new FedEx web services. Additionally, Magento needs failover shipping support! Here's the scoop ->
Disclaimers
This solution is just a hack, but you may find it useful, or as a starting point for a more elegant solution. If enough people take interest I may do more with it.
Warning : Do not attempt this process on a live site. Try it on a staging site first. If you must do it on a live site, backup everything before proceeding.
Step 1 - Install Zenprint XAXJAX Integration
Install Zenprint XAXJAX Integration (Zenprint_Xajax)
I'm not really interested in another AJAX library, but it looks like this is only getting called during the shipping process, so it's not a ton of overheard. Either way, it's required for Zenprint's Shipping module, so for now we need it. Install via Magento Connect.
Note: this says 1.2 in the package manager, but seems to be working fine in 1.3.x
Step 2 - Install Zenprint API Shipping Module
Install Zenprint API Shipping module (Zenprint_Ordership)
This module adds auto shipping (with tracking numbers and PDF shipping labels). Install via Magento Connect.
Note: this one also says 1.2, but installs fine in 1.3.x
Help!
On a shared host? >>> Having permissions problems with Magento Connect? Click here
On a shared host? >>> You may need to ask your hosting provider to install needed dependencies
Step 3 - Signup for FedEx Developer Resources, and setup Zenprint
At this point you need a FedEx account, key, password, and meter. Speak to your FedEx representative or check out FedEx Developer Resources to get started.
Once you get your keys and such, fill in the data in the Magento admin backend (System->Configuration->Shipping Methods->FedEx). You must also enter your address in the shipping settings configuration (System->Configuration->Shipping Settings). Now take a moment to read the Zenprint_Ordership Documentation, and apply any relevant tweaks to your system.
Save everything, and clear your cache for good measure.
Step 4 - Update WSDL to v6
Now we get our hands dirty. First, download the shipping WSDL (ShipService_v6) and the rate WSDL (RateService_v6) from FedEx Developer Resources. Put both of these files in the following directory :
/magento/app/code/community/Zenprint/Ordership/Model/Shipping/Carrier/wsdl/
Note: When you first receive these files, the WSDL URL is set to "https://gatewaybeta.fedex.com:443/web-services." You will want to change this to the live URL ("https://gateway.fedex.com:443/web-services") once you begin using the production keys.
Step 5 - Load Fedex.php
Now load up the following file in your favorite editor :
/magento/app/code/community/Zenprint/Ordership/Model/Shipping/Carrier/Fedex.php
# You made that backup already, right???
Step 6 - Find and Replace
find↓
protected $_gatewayUrl = 'https://gateway.fedex.com/GatewayDC';
replace ↓
protected $_gatewayUrl = 'https://gateway.fedex.com/web-services';
find↓
$wsdlpath = dirname(__FILE__).'/wsdl/ShipService_v5.wsdl';
replace↓
$wsdlpath = dirname(__FILE__).'/wsdl/ShipService_v6.wsdl';
find↓
# 07/19/09 - Oops! Forgot this part--Added
$request['Version'] = array('ServiceId' => 'ship', 'Major' => 5, 'Intermediate' => 0, 'Minor' => 0);
replace↓
$request['Version'] = array('ServiceId' => 'ship', 'Major' => 6, 'Intermediate' => 0, 'Minor' => 0);
find↓
protected function _getXmlQuotes() { .. .. .. }
replace↓
# The following function makes the API call. The SOAP request is slow. Caching may help.
protected /************************************************************\ * \************************************************************/ function _getXmlQuotes() { $r = $this->_rawRequest; # Retrieve Magento data $newline = ""; # Define HTML break $path_to_wsdl = dirname(__FILE__).'/wsdl/RateService_v6.wsdl'; # WSDL path try { ini_set("soap.wsdl_cache_enabled", "0"); $client = new SoapClient($path_to_wsdl); # Authenticate FedEx Key, and Password $request['WebAuthenticationDetail'] = array( 'UserCredential' => array( 'Key' => Mage::getStoreConfig('carriers/fedex/key'), 'Password' => Mage::getStoreConfig('carriers/fedex/password'))); # Fedex Account and Meter $request['ClientDetail'] = array( 'AccountNumber' => Mage::getStoreConfig('carriers/fedex/account'), 'MeterNumber' => Mage::getStoreConfig('carriers/fedex/meter')); $request['TransactionDetail'] = array( 'CustomerTransactionId' => ' *** Rate Available Services Request v6 using PHP ***'); $request['Version'] = array('ServiceId' => 'crs', 'Major' => '6', 'Intermediate' => '0', 'Minor' => '0'); # DropoffType, ShipTimestamp, and PackagingType $request['RequestedShipment']['DropoffType'] = $this-> getUnderscoreCodeFromCode(Mage::getStoreConfig('carriers/fedex/dropoff')); $request['RequestedShipment']['ShipTimestamp'] = date('c'); $request['RequestedShipment']['PackagingType'] = $this-> getUnderscoreCodeFromCode(Mage::getStoreConfig('carriers/fedex/packaging')); if(Mage::getStoreConfig('carriers/fedex/third_party') == 1) { $shipping_charges_payment = array('PaymentType' => 'THIRD_PARTY', 'Payor' => array('AccountNumber' => Mage::getStoreConfig('carriers/fedex/third_party_fedex_account'), 'CountryCode' => Mage::getStoreConfig('carriers/fedex/third_party_fedex_account_country'))); } else { $shipping_charges_payment = array('PaymentType' => 'SENDER', 'Payor' => array('AccountNumber' => Mage::getStoreConfig('carriers/fedex/account'), 'CountryCode' => Mage::getStoreConfig('carriers/fedex/account_country'))); } # Generate shipping street $shipperstreetlines = array(Mage::getStoreConfig('shipping/origin/address1')); if(Mage::getStoreConfig('shipping/origin/address2') != '') { $shipperstreetlines[] = Mage::getStoreConfig('shipping/origin/address2'); } if(Mage::getStoreConfig('shipping/origin/address3') != '') { $shipperstreetlines[] = Mage::getStoreConfig('shipping/origin/address3'); } # Set Shipper and Recipient $request['RequestedShipment']['Shipper'] = array('Address' => array( 'StreetLines' => $shipperstreetlines, #Origin details 'City' => Mage::getStoreConfig('shipping/origin/city'), 'StateOrProvinceCode' => Mage::getModel('directory/region')-> load(Mage::getStoreConfig('shipping/origin/region_id'))-> getCode(), 'PostalCode' => Mage::getStoreConfig('shipping/origin/postcode'), 'CountryCode' => Mage::getStoreConfig('shipping/origin/country_id'))); $request['RequestedShipment']['Recipient'] = array('Address' => array( 'PostalCode' => $r-> getDestPostal(), 'CountryCode' => $r-> getDestCountry())); $request['RequestedShipment']['ShippingChargesPayment'] = $shipping_charges_payment; $request['RequestedShipment']['RateRequestTypes'] = 'LIST'; $request['RequestedShipment']['PackageCount'] = '1'; $request['RequestedShipment']['PackageDetail'] = 'INDIVIDUAL_PACKAGES'; $request['RequestedShipment']['RequestedPackages'] = array('0' => array('SequenceNumber' => '1', 'InsuredValue' => array( 'Amount' => $r-> getValue(), 'Currency' => Mage::app()-> getBaseCurrencyCode()), 'Weight' => array('Value' => $r-> getWeight(), 'Units' => 'LB'))); $response = $client->getRates($request); } catch (SoapFault $fault) { return $this->_parseXmlResponse(NULL); } if ($response->HighestSeverity == 'FAILURE') { return $this->_parseXmlResponse(NULL); } else { return $this->_parseXmlResponse($response); } }
replace↓
# This parses the response, and sets up a failsafe flat rate if FedEx is unavailable.
protected /************************************************************\ * \************************************************************/ function _parseXmlResponse($response) { # Retrieve Magento data # Check for allowed shipping methods $allowedMethods = explode(",", $this-> getConfigData('allowed_methods')); foreach ($allowedMethods as $method) { $parsedAllowedMethods[] = $this-> getUnderscoreCodeFromCode($method); } $result = Mage::getModel('shipping/rate_result'); $defaults = $this-> getDefaults(); if ($response == NULL) { # Set flat rate $rate = Mage::getModel('shipping/rate_result_method'); $rate-> setCarrier('fedex'); $rate-> setCarrierTitle($this-> getConfigData('title')); $rate-> setMethod('FEDEX_GROUND'); # Shipping method $rate-> setMethodTitle('FedEx Ground (Flat)'); # Customer view $rate-> setCost(7); # $7.00 $rate-> setPrice(7); # $7.00 $result-> append($rate); return $result; } elseif ($response->HighestSeverity == 'ERROR') { $msg = ''; if(is_array($response->Notifications)) { foreach ($response->Notifications as $notification) { $msg .= $notification->Severity.': '.$notification->Message.$newline; } } else { $msg .= $response->Notifications->Severity.': '.$response->Notifications->Message.$newline; } $error = Mage::getModel('shipping/rate_result_error'); $error->setCarrier('fedex'); $error->setCarrierTitle($this->getConfigData('title')); $error->setErrorMessage($msg); $result->append($error); return $result; } else { # Iterate through retrieved FedEx responses foreach ($response->RateReplyDetails as $rateReply) { # Now update shipping rates for allowed methods if (in_array($rateReply-> ServiceType, $parsedAllowedMethods)) { $_serviceType = str_replace('_', '', $rateReply->ServiceType); $rate = Mage::getModel('shipping/rate_result_method'); $rate-> setCarrier('fedex'); $rate-> setCarrierTitle($this-> getConfigData('title')); $rate-> setMethod($_serviceType); $rate-> setMethodTitle($this-> getCode('method', $rateReply-> ServiceType, true)); $rate-> setCost( $rateReply-> RatedShipmentDetails[0]-> ShipmentRateDetail-> TotalNetCharge-> Amount); $rate-> setPrice($rateReply-> RatedShipmentDetails[0]-> ShipmentRateDetail-> TotalNetCharge-> Amount); $result-> append($rate); } } return $result; } }
Step 7 - Cleanup
I had to insert a definition for 2nd day air to get the text label to show up ->
public function getCode($type, $code='', $underscore=false)
{
..
..
..
//Needed since XML requests have no underscore, while SOAP interface includes them (nice one Fedex)
$codes_underscore = array(
'method'=>array(
'PRIORITY_OVERNIGHT' => Mage::helper('usa')->__('Priority Overnight'),
'STANDARD_OVERNIGHT' => Mage::helper('usa')->__('Standard Overnight'),
'FIRST_OVERNIGHT' => Mage::helper('usa')->__('First Overnight'),
'FEDEX_2DAY' => Mage::helper('usa')->__('2Day'),
'FEDEX_2_DAY' => Mage::helper('usa')->__('2Day'), # Added
'FEDEX_EXPRESS_SAVER' => Mage::helper('usa')->__('Express Saver'),
'INTERNATIONAL_PRIORITY' => Mage::helper('usa')->__('International Priority'),
'INTERNATIONAL_ECONOMY' => Mage::helper('usa')->__('International Economy'),
'INTERNATIONAL_FIRST' => Mage::helper('usa')->__('International First'),
'FEDEX_1DAY_FREIGHT' => Mage::helper('usa')->__('1 Day Freight'),
'FEDEX_2DAY_FREIGHT' => Mage::helper('usa')->__('2 Day Freight'),
'FEDEX_3DAY_FREIGHT' => Mage::helper('usa')->__('3 Day Freight'),
'FEDEX_GROUND' => Mage::helper('usa')->__('Ground'),
'GROUND_HOME_DELIVERY' => Mage::helper('usa')->__('Home Delivery'),
'INTERNATIONAL_PRIORITY_FREIGHT' => Mage::helper('usa')->__('Intl Priority Freight'),
'INTERNATIONAL_ECONOMY_FREIGHT' => Mage::helper('usa')->__('Intl Economy Freight'),
'EUROPE_FIRST_INTERNATIONAL_PRIORITY' => Mage::helper('usa')->__('Europe First Priority'),
),
..
..
..
}
# 07/20/09 - Added better error handling and fixed a few bugs, updated code snippets above
Step 8 - Test
-> Test rate requests
-> Test auto ship functionality
-> Test PDF label printing
-> Test tracking number generation
Once you have everything working, FedEx will give you a production key and you can start shipping orders.
Conclusion
It's a hack, but it works. Enjoy!
..
..
..












#1 by Ryan at August 6th, 2009
| Quote
Great work, have you had a chance to update the UPS shipping options to work with 1.3 as well?
#2 by Peter at August 15th, 2009
| Quote
i’m currently workiing on magento car trying to impliment this hack on 1.3.2.3. Shipping ground works, shipping home ground gives me an error telling me:
ERROR: Shipments for Home Delivery Service must be designated as Residential Delivery also
I talked to fedex, and they told me i needed to flag the residential as “y.” you have any idea on how to go about doing that?
#3 by justin at November 13th, 2009
| Quote
for the life of me I can’t get zenprint to create a label (extensions installed fine, and I can hit ‘autoship’) not sure why. Orange wheel spins a bit and thats it. I need some help getting Fedex working in Magento, any tips? I’m paying
#4 by admin at December 17th, 2009
| Quote
hey justin, try turning of SSL on the back-end and see if that fixes it. there’s a bug in zenprint that causes problem with https… i’ve fixed it on my install, but haven’t had time to update this post. also, due to the interest in this plugin i’m going to go ahead and package it all together with my most recent updates, so hopefully there’ll be a turn-key solution shortly. give me a shout at ‘d at kernelhack dot com’ if you want some help.
#5 by admin at December 17th, 2009
| Quote
hey, i’m working on the residential issue right now. i’ll have a new plugin up soon..
#6 by admin at December 17th, 2009
| Quote
ryan, no plans to implement UPS at the moment, but if the price is right i could be convinced to dig into it..
#7 by Sri Moxsie at January 5th, 2010
| Quote
Trying to get this working with v7, and I am unable to get the Free shipping working. The Ground quotes always come back with the price although the admin config is set to make Ground free.
Any ideas?
For other folks: to get v7 quotes, please change
$request['RequestedShipment']['RequestedPackages']
to
$request['RequestedShipment']['RequestedPackageLineItems']
#8 by admin at January 6th, 2010
| Quote
heya sri, i haven’t touched v7 yet, so no advice there unfortunately.. i suspect there are a few changes to fedex’s naming conventions..
regarding ground quotes, if the plugin is unable to retrieve quotes it defaults to a flat fee via ground. it’s likely that you’re not getting rate results, so the plugin is defaulting to this failover method.
#9 by Don V. at January 26th, 2010
| Quote
Do I need to apply this hack in order to use your ShipSync plug-in?
#10 by admin at February 16th, 2010
| Quote
No, this stuff is unrelated to ShipSync… I should probably archive this post to alleviate confusion..
#11 by Magento Expert at July 12th, 2010
| Quote
Good post. Aside from breaking my foot last week, I have been going through Magento hell..