Accessing FedEx Web Services From Ruby/Rails
I recently played around with adding FedEx shipping services to the active_shipping plugin and found a bit of confusion online in regards to how to hit FedEx depending upon the style of service being exposed. Somewhat confusingly, FedEx has non-SOAP services as well as SOAP services that you can hit restfully in an HTTPS Post. For the SOAP service, FedEx has very specific requirements about how to formulate the payload and HTTP headers that took a bit of tinkering to get working. Non-SOAP FedEx support was recently added to active_shipping prior to the completion of my work, but I figured I would post if for those who want to use what I think are newer SOAP services.
Without much further ado, here are the basics for creating a tracking request:
TEST_DOMAIN = 'gatewaybeta.fedex.com'
def find_tracking_info(tracking_number, options={})
options = @options.update(options)
tracking_request = build_tracking_request(tracking_number, options)
response = commit(:track, save_request(tracking_request),
(options[:test] || false))
parse_tracking_response(response, options)
end
protected
def build_access_request
xml_request = XmlNode.new('ns:WebAuthenticationDetail') do |access|
access << XmlNode.new('ns:UserCredential') do |user|
user << XmlNode.new('ns:Key', @options[:key])
user << XmlNode.new('ns:Password', @options[:password])
end
end
xml_request
end
def build_client_detail
xml_request = XmlNode.new('ns:ClientDetail') do |client_detail|
client_detail << XmlNode.new('ns:AccountNumber', @options[:account])
client_detail << XmlNode.new('ns:MeterNumber', @options[:meter])
end
xml_request
end
def build_tracking_request(tracking_number, options={})
xml_request = XmlNode.new('ns:TrackRequest',
:'xmlns:ns'=>"http://fedex.com/ws/track/v2",
:'xmlns:xsi'=>"http://www.w3.org/2001/XMLSchema-instance",
:'xsi:schemaLocation'=>"http://fedex.com/ws/track/v2") do |root_node|
root_node << build_access_request
root_node << build_client_detail
root_node << XmlNode.new('ns:TransactionDetail') do |detail|
detail << XmlNode.new('ns:CustomerTransactionId', 'Ground Track')
end
root_node << XmlNode.new('ns:Version') do |version|
version << XmlNode.new('ns:ServiceId', 'trck')
version << XmlNode.new('ns:Major', '2')
version << XmlNode.new('ns:Intermediate', '0')
version << XmlNode.new('ns:Minor', '0')
end
root_node << XmlNode.new('ns:PackageIdentifier') do |package|
package << XmlNode.new('ns:Value', tracking_number.to_s)
package << XmlNode.new('ns:Type', "TRACKING_NUMBER_OR_DOORTAG")
end
end
xml_request.to_xml
end
def commit(action, request, test = false)
http = Net::HTTP.new((test ? TEST_DOMAIN : LIVE_DOMAIN),
(USE_SSL[action] ? 443 : 80 ))
http.use_ssl = USE_SSL[action]
headers = {
'Referer' => 'me',
'Port' => '443',
'Accept' => "image/gif, image/jpeg, image/pjpeg, text/plain, text/html, */*",
'Content-Type' => 'image/gif'
}
http.verify_mode = OpenSSL::SSL::VERIFY_NONE if USE_SSL[action]
response = http.start do |http|
http.request_post ('/xml', request, headers)
end
response.body
end




