trackerific 0.7.1 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +6 -0
  3. data/Gemfile.lock +43 -1
  4. data/README.rdoc +58 -60
  5. data/lib/trackerific.rb +17 -20
  6. data/lib/trackerific/builders/base/soap.rb +21 -0
  7. data/lib/trackerific/builders/base/xml.rb +38 -0
  8. data/lib/trackerific/builders/fedex.rb +46 -0
  9. data/lib/trackerific/builders/ups.rb +32 -0
  10. data/lib/trackerific/builders/usps.rb +19 -0
  11. data/lib/trackerific/environment.rb +11 -0
  12. data/lib/trackerific/error.rb +3 -1
  13. data/lib/trackerific/parsers/base.rb +35 -0
  14. data/lib/trackerific/parsers/fedex.rb +59 -0
  15. data/lib/trackerific/parsers/ups.rb +66 -0
  16. data/lib/trackerific/parsers/usps.rb +51 -0
  17. data/lib/trackerific/services/base.rb +23 -10
  18. data/lib/trackerific/services/concerns/soap.rb +45 -0
  19. data/lib/trackerific/services/concerns/xml.rb +44 -0
  20. data/lib/trackerific/services/fedex.rb +9 -79
  21. data/lib/trackerific/services/mock_service.rb +4 -12
  22. data/lib/trackerific/services/ups.rb +11 -95
  23. data/lib/trackerific/services/usps.rb +21 -181
  24. data/lib/trackerific/soap/wsdl.rb +17 -0
  25. data/lib/trackerific/version.rb +1 -1
  26. data/spec/fixtures/fedex/error.xml +1 -10
  27. data/spec/fixtures/fedex/success.xml +1 -74
  28. data/spec/fixtures/ups/malformed.xml +10 -0
  29. data/spec/fixtures/ups/request.xml +1 -0
  30. data/spec/fixtures/usps/malformed.xml +2 -0
  31. data/spec/fixtures/usps/request.xml +1 -0
  32. data/spec/lib/trackerific/builders/base/soap_spec.rb +29 -0
  33. data/spec/lib/trackerific/builders/base/xml_spec.rb +35 -0
  34. data/spec/lib/trackerific/builders/fedex_spec.rb +70 -0
  35. data/spec/lib/trackerific/builders/ups_spec.rb +11 -0
  36. data/spec/lib/trackerific/builders/usps_spec.rb +11 -0
  37. data/spec/lib/trackerific/environment_spec.rb +45 -0
  38. data/spec/lib/trackerific/parsers/base_spec.rb +62 -0
  39. data/spec/lib/trackerific/parsers/ups_spec.rb +71 -0
  40. data/spec/lib/trackerific/parsers/usps_spec.rb +77 -0
  41. data/spec/lib/trackerific/services/base_spec.rb +56 -0
  42. data/spec/lib/trackerific/services/concerns/soap_spec.rb +71 -0
  43. data/spec/lib/trackerific/services/concerns/xml_spec.rb +65 -0
  44. data/spec/lib/trackerific/services/fedex_spec.rb +43 -45
  45. data/spec/lib/trackerific/services/ups_spec.rb +23 -2
  46. data/spec/lib/trackerific/services/usps_spec.rb +44 -34
  47. data/spec/lib/trackerific/services_spec.rb +7 -0
  48. data/spec/lib/trackerific/soap/wsdl_spec.rb +29 -0
  49. data/spec/lib/trackerific/version_spec.rb +1 -1
  50. data/spec/lib/trackerific_spec.rb +2 -11
  51. data/spec/spec_helper.rb +17 -1
  52. data/spec/support/reload.rb +6 -0
  53. data/spec/support/trackerific.rb +7 -0
  54. data/trackerific.gemspec +2 -0
  55. data/vendor/wsdl/fedex/TrackService_v8.wsdl +2284 -0
  56. metadata +81 -7
  57. data/lib/trackerific/configuration.rb +0 -7
  58. data/spec/fixtures/usps/city_state_lookup.xml +0 -8
  59. data/spec/lib/trackerific/configuration_spec.rb +0 -13
@@ -6,22 +6,14 @@ module Trackerific
6
6
 
7
7
  self.register :mock_service
8
8
 
9
- # Regular expression matchers for mocked Trackerific service
10
- # @return [Array, Regexp] the regular expression
11
- # @api private
9
+ def self.credentials
10
+ {}
11
+ end
12
+
12
13
  def self.package_id_matchers
13
14
  [ /XXXXXXXXXX/, /XXXxxxxxxx/ ]
14
15
  end
15
16
 
16
- # Sets up a mocked package details
17
- # @param [String] package_id the package identifier
18
- # @return [Trackerific::Details] the tracking details
19
- # @raise [Trackerific::Error] raised when the server returns an error
20
- # @example Track a package
21
- # service = Trackerific::Services::MockedService.new
22
- # details = service.track("XXXXXXXXXX") # => valid response
23
- # details = service.track("XXXxxxxxxx") # => throws a Trackerific::Error exception
24
- # @api public
25
17
  def track(id)
26
18
  if id == "XXXXXXXXXX"
27
19
  Trackerific::Details.new(id, "Your package was delivered.",
@@ -1,111 +1,27 @@
1
- require 'httparty'
2
- require 'builder'
3
- require 'date'
4
-
5
1
  module Trackerific
6
2
  module Services
7
- # Provides package tracking support for UPS.
8
3
  class UPS < Base
9
- self.register :ups
4
+ require 'trackerific/builders/ups'
5
+ require 'trackerific/parsers/ups'
6
+
7
+ include Concerns::XML, HTTParty
8
+
9
+ register :ups
10
+
11
+ self.xml_endpoint = '/Track'
12
+ self.xml_parser = Parsers::UPS
13
+ self.xml_builder = Builders::UPS
10
14
 
11
- include ::HTTParty
12
15
  format :xml
13
16
 
14
- base_uri case (ENV['RAILS_ENV'] || 'production')
17
+ base_uri case Trackerific.env
15
18
  when 'production' then 'https://www.ups.com/ups.app/xml'
16
19
  else 'https://wwwcie.ups.com/ups.app/xml'
17
20
  end
18
21
 
19
- def initialize(options={})
20
- @options = options
21
- end
22
-
23
- # An Array of Regexp that matches valid UPS package IDs
24
- # @return [Array, Regexp] the regular expression
25
- # @api private
26
22
  def self.package_id_matchers
27
23
  [ /^.Z/, /^[HK].{10}$/ ]
28
24
  end
29
-
30
- # Tracks a UPS package
31
- # @param [String] package_id the package identifier
32
- # @return [Trackerific::Details] the tracking details
33
- # @raise [Trackerific::Error] raised when the server returns an error (invalid credentials, tracking package, etc.)
34
- # @example Track a package
35
- # ups = Trackerific::UPS.new key: 'api key', user_id: 'user', password: 'secret'
36
- # details = ups.track_package("1Z12345E0291980793")
37
- # @api public
38
- def track(id)
39
- @package_id = id
40
- # connect to UPS via HTTParty
41
- http_response = self.class.post('/Track', body: build_xml_request)
42
- # throw any HTTP errors
43
- http_response.error! unless http_response.code == 200
44
- # Check the response for errors, return a Trackerific::Error, or parse
45
- # the response from UPS and return a Trackerific::Details
46
- case http_response['TrackResponse']['Response']['ResponseStatusCode']
47
- when "0" then raise Trackerific::Error, parse_error_response(http_response)
48
- when "1" then return parse_success_response(http_response)
49
- else raise Trackerific::Error, "Invalid response code returned from server."
50
- end
51
- end
52
-
53
- protected
54
-
55
- # Parses the response from UPS
56
- # @return [Trackerific::Details]
57
- # @api private
58
- def parse_success_response(http_response)
59
- # get the activity from the UPS response
60
- activity = http_response['TrackResponse']['Shipment']['Package']['Activity']
61
- # if there's only one activity in the list, we need to put it in an array
62
- activity = [activity] if activity.is_a? Hash
63
- # UPS does not provide a summary, so we'll just use the last tracking status
64
- summary = activity.first['Status']['StatusType']['Description']
65
- events = []
66
- activity.each do |a|
67
- # the time format from UPS is HHMMSS, which cannot be directly converted
68
- # to a Ruby time.
69
- hours = a['Time'][0..1]
70
- minutes = a['Time'][2..3]
71
- seconds = a['Time'][4..5]
72
- date = Date.parse(a['Date'])
73
- date = DateTime.parse("#{date} #{hours}:#{minutes}:#{seconds}")
74
- desc = a['Status']['StatusType']['Description']
75
- loc = a['ActivityLocation']['Address'].map {|k,v| v}.join(" ")
76
- events << Trackerific::Event.new(date, desc, loc)
77
- end
78
-
79
- Trackerific::Details.new(@package_id, summary, events)
80
- end
81
-
82
- # Parses a UPS tracking response, and returns any errors
83
- # @return [String] the UPS tracking error
84
- # @api private
85
- def parse_error_response(http_response)
86
- http_response['TrackResponse']['Response']['Error']['ErrorDescription']
87
- end
88
-
89
- # Builds the XML request to send to UPS for tracking a package
90
- # @return [String] the XML request
91
- # @api private
92
- def build_xml_request
93
- xml = ""
94
- builder = ::Builder::XmlMarkup.new(:target => xml)
95
- builder.AccessRequest do |ar|
96
- ar.AccessLicenseNumber @options[:key]
97
- ar.UserId @options[:user_id]
98
- ar.Password @options[:password]
99
- end
100
- builder.TrackRequest do |tr|
101
- tr.Request do |r|
102
- r.RequestAction 'Track'
103
- r.RequestOption 'activity'
104
- end
105
- tr.TrackingNumber @package_id
106
- end
107
- return xml
108
- end
109
25
  end
110
26
  end
111
27
  end
@@ -1,200 +1,40 @@
1
- require 'active_support/core_ext/object/to_query'
2
- require 'httparty'
3
- require 'builder'
4
- require 'date'
5
-
6
1
  module Trackerific
7
2
  module Services
8
- # Provides package tracking support for USPS.
9
3
  class USPS < Base
10
- include HTTParty
11
- format :xml
4
+ require 'trackerific/builders/usps'
5
+ require 'trackerific/parsers/usps'
12
6
 
13
- base_uri case (ENV['RAILS_ENV'] || 'production')
14
- when 'production' then "http://production.shippingapis.com"
15
- else "http://testing.shippingapis.com"
16
- end
7
+ include Concerns::XML, HTTParty
17
8
 
18
- def initialize(options={})
19
- @options = options
20
- end
9
+ register :usps
21
10
 
22
- # An Array of Regexp that matches valid USPS package IDs
23
- # @return [Array, Regexp] the regular expression
24
- # @api private
25
- def self.package_id_matchers
26
- [ /^E\D{1}\d{9}\D{2}$|^9\d{15,21}$/ ]
27
- end
28
-
29
- # Tracks a USPS package
30
- # @param [String] package_id the package identifier
31
- # @return [Trackerific::Details] the tracking details
32
- # @raise [Trackerific::Error] raised when the server returns an error (invalid credentials, tracking package, etc.)
33
- # @example Track a package
34
- # usps = Trackerific::USPS.new user_id: 'user'
35
- # details = ups.track_package("EJ958083578US")
36
- # @api public
37
- def track(id)
38
- @package_id = id
39
- # connect to the USPS shipping API via HTTParty
40
- response = self.class.get(
41
- env == 'production' ? "/ShippingAPI.dll" : "/ShippingAPITest.dll",
42
- query: { :API => 'TrackV2', :XML => build_tracking_xml_request }.to_query
43
- )
44
- # raise any errors
45
- error = check_response_for_errors(response, :TrackV2)
46
- raise error unless error.nil?
47
- # get the tracking information from the response
48
- tracking_info = response['TrackResponse']['TrackInfo']
49
- events = []
50
- # parse the tracking events out of the USPS tracking info
51
- tracking_info['TrackDetail'].each do |d|
52
- events << Trackerific::Event.new(
53
- date_of_event(d),
54
- description_of_event(d),
55
- location_of_event(d)
56
- )
57
- end unless tracking_info['TrackDetail'].nil?
58
- # return the details
59
- Trackerific::Details.new(
60
- tracking_info['ID'],
61
- tracking_info['TrackSummary'],
62
- events
63
- )
64
- end
11
+ self.xml_parser = Parsers::USPS
12
+ self.xml_builder = Builders::USPS
65
13
 
66
- # Gets the city/state of a zipcode - requires access to USPS address APIs
67
- # @param [String] zipcode The zipcode to find the city/state for
68
- # @return [Hash] { zip: 'the zipcode, 'city: "the city", state: "the state" }
69
- # @example Lookup zipcode for Beverly Hills, CA
70
- # usps = Trackerific::USPS.new :user_id => 'youruserid'
71
- # city_state = usps.city_state_lookup(90210)
72
- # city_state[:city] # => BEVERLY HILLS
73
- # city_state[:state] # => CA
74
- # city_state[:zip] # => 90210
75
- # @api public
76
- def city_state_lookup(zipcode)
77
- response = self.class.get(
78
- env == 'production' ? "/ShippingAPI.dll" : "/ShippingAPITest.dll",
79
- query: {
80
- :API => 'CityStateLookup',
81
- :XML => build_city_state_xml_request(zipcode)
82
- }.to_query
83
- )
84
- # raise any errors
85
- error = check_response_for_errors(response, :CityStateLookup)
86
- raise error unless error.nil?
87
- # return the city, state, and zip
88
- response = response['CityStateLookupResponse']['ZipCode']
89
- {
90
- :city => response['City'],
91
- :state => response['State'],
92
- :zip => response['Zip5']
93
- }
14
+ case Trackerific.env
15
+ when 'production'
16
+ self.xml_endpoint = '/ShippingAPI.dll'
17
+ base_uri 'http://production.shippingapis.com'
18
+ else
19
+ self.xml_endpoint = '/ShippingAPITest.dll'
20
+ base_uri 'http://testing.shippingapis.com'
94
21
  end
95
22
 
96
- private
97
-
98
- def env
99
- @rails_env ||= ENV['RAILS_ENV'] || 'production'
100
- end
101
-
102
- # Parses a USPS tracking event, and returns its date
103
- # @param [String] event The tracking event to parse
104
- # @return [DateTime] The date / time of the event
105
- # @api private
106
- def date_of_event(event)
107
- # get the date out of
108
- # Mon DD HH:MM am/pm THE DESCRIPTION CITY STATE ZIP.
109
- d = event.split(" ")
110
- DateTime.parse(d[0..3].join(" "))
111
- end
112
-
113
- # Parses a USPS tracking event, and returns its description
114
- # @param [String] event The tracking event to parse
115
- # @return [DateTime] The description of the event
116
- # @api private
117
- def description_of_event(event)
118
- # get the description out of
119
- # Mon DD HH:MM am/pm THE DESCRIPTION CITY STATE ZIP.
120
- d = event.split(" ")
121
- d[4..d.length-4].join(" ").capitalize
122
- end
23
+ format :xml
123
24
 
124
- # Parses a USPS tracking event, and returns its location
125
- # @param [String] event The tracking event to parse
126
- # @return The location of the event
127
- # @api private
128
- def location_of_event(event)
129
- # remove periods, and split by spaces
130
- d = event.gsub(".", "").split(" ")
131
- l = d[d.length-3, d.length] # => ['city', 'state', 'zip']
132
- # this is the location from the USPS tracking XML. it is not guaranteed
133
- # to be completely accurate, since there's no way to know if it will
134
- # always be the last 3 words.
135
- city = l[0]
136
- state = l[1]
137
- zip = l[2]
138
- # for greater accuracy, we can use the city/state lookup API from USPS
139
- if @options[:use_city_state_lookup]
140
- l = city_state_lookup(zip)
141
- # these will be nil if USPS does not have the zipcode in their database
142
- city = l[:city] unless l[:city].nil?
143
- state = l[:state] unless l[:state].nil?
144
- zip = l[:zip] unless l[:zip].nil?
145
- end
146
- "#{city}, #{state} #{zip}"
25
+ def self.package_id_matchers
26
+ [ /^E\D{1}\d{9}\D{2}$|^9\d{15,21}$/ ]
147
27
  end
148
28
 
149
- # Checks a HTTParty response for USPS, or HTTP errors
150
- # @param [HTTParty::Response] response The HTTParty response to check
151
- # @return The exception to raise, or nil
152
- # @api private
153
- def check_response_for_errors(response, api)
154
- # return any HTTP errors
155
- return response.error unless response.code == 200
156
- # return a Trackerific::Error if there is an error in the response, or if
157
- # the tracking response is malformed
158
- return Trackerific::Error.new(response['Error']['Description']) unless response['Error'].nil?
159
- return Trackerific::Error.new("Tracking information not found in response from server.") if response['TrackResponse'].nil? && api == :TrackV2
160
- return Trackerific::Error.new("City / state information not found in response from server.") if response['CityStateLookupResponse'].nil? && api == :CityStateLookup
161
- return nil # no errors to report
162
- end
29
+ protected
163
30
 
164
- # Builds an XML city/state lookup request
165
- # @param [String] zipcode The zipcode to find the city/state for
166
- # @return [String] the xml request
167
- # @api private
168
- def build_city_state_xml_request(zipcode)
169
- xml = ""
170
- # set up the Builder
171
- builder = ::Builder::XmlMarkup.new(:target => xml)
172
- # add the XML header
173
- builder.instruct! :xml, :version => "1.0", :encoding => "UTF-8"
174
- # build the request
175
- builder.CityStateLookupRequest(:USERID => @options[:user_id]) do |request|
176
- request.ZipCode(:ID => "5") do |zip|
177
- zip.Zip5 zipcode
178
- end
179
- end
31
+ def http_response(id)
32
+ http_response = self.class.get(self.class.xml_endpoint, http_query(id))
180
33
  end
181
34
 
182
- # Builds an XML tracking request
183
- # @return [String] the xml request
184
- # @api private
185
- def build_tracking_xml_request
186
- xml = ""
187
- # set up the Builder
188
- builder = ::Builder::XmlMarkup.new(:target => xml)
189
- # build the request
190
- builder.TrackRequest(:USERID => @options[:user_id]) do |t|
191
- t.TrackID(:ID => @package_id)
192
- end
193
- # return the XML
194
- xml
35
+ def http_query(id)
36
+ { query: { :API => 'TrackV2', :XML => builder(id).xml }.to_query }
195
37
  end
196
-
197
38
  end
198
-
199
39
  end
200
40
  end
@@ -0,0 +1,17 @@
1
+ module Trackerific
2
+ module SOAP
3
+ module WSDL
4
+ ROOT = File.expand_path("../../../../vendor/wsdl", __FILE__)
5
+
6
+ def self.path(name)
7
+ path = File.join(ROOT, "#{name}.wsdl")
8
+
9
+ unless File.exists?(path)
10
+ raise IOError, "WSDL not found #{name}", caller
11
+ end
12
+
13
+ path
14
+ end
15
+ end
16
+ end
17
+ end
@@ -1,3 +1,3 @@
1
1
  module Trackerific
2
- VERSION = "0.7.1"
2
+ VERSION = "0.7.2"
3
3
  end
@@ -1,10 +1 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <FDXTrack2Reply xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
3
- <Error>
4
- <Code>6035</Code>
5
- <Message>Invalid tracking numbers. Please check the following numbers and resubmit.</Message>
6
- <LocalLanguageMessage>Invalid tracking numbers. Please check the following numbers and resubmit.</LocalLanguageMessage>
7
- </Error>
8
- <DuplicateWaybill>false</DuplicateWaybill>
9
- <MoreData>false</MoreData>
10
- </FDXTrack2Reply>
1
+ <SOAP-ENV:Envelope xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'><SOAP-ENV:Header/><SOAP-ENV:Body><TrackReply xmlns='http://fedex.com/ws/track/v8'><HighestSeverity>ERROR</HighestSeverity><Notifications><Severity>ERROR</Severity><Source>trck</Source><Code>5508</Code><Message>Invalid tracking number.</Message><LocalizedMessage>Invalid tracking number.</LocalizedMessage></Notifications><TransactionDetail><CustomerTransactionId>7fe240d748c68b6e</CustomerTransactionId></TransactionDetail><Version><ServiceId>trck</ServiceId><Major>8</Major><Intermediate>0</Intermediate><Minor>0</Minor></Version></TrackReply></SOAP-ENV:Body></SOAP-ENV:Envelope>
@@ -1,74 +1 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <FDXTrack2Reply xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
3
- <DuplicateWaybill>false</DuplicateWaybill>
4
- <MoreData>false</MoreData>
5
- <Package>
6
- <TrackingNumber>183689015000001</TrackingNumber>
7
- <TrackingNumberUniqueIdentifier>12010~183689015000001</TrackingNumberUniqueIdentifier>
8
- <StatusCode>DL</StatusCode>
9
- <StatusDescription>Delivered</StatusDescription>
10
- <CarrierCode>FDXG</CarrierCode>
11
- <OtherIdentifier>
12
- <Value>770-886-6395</Value>
13
- <Type>CUR</Type>
14
- </OtherIdentifier>
15
- <Service>FedEx Home Delivery</Service>
16
- <Weight>19.8</Weight>
17
- <WeightUnits>LBS</WeightUnits>
18
- <PackagingDescription>Package</PackagingDescription>
19
- <PackageSequenceNumber>1</PackageSequenceNumber>
20
- <PackageCount>1</PackageCount>
21
- <OriginLocationAddress>
22
- <City>ANAHEIM</City>
23
- <StateOrProvinceCode>CA</StateOrProvinceCode>
24
- <CountryCode>US</CountryCode>
25
- </OriginLocationAddress>
26
- <ShipDate>2010-06-25</ShipDate>
27
- <DestinationAddress>
28
- <City>Gainesville</City>
29
- <StateOrProvinceCode>GA</StateOrProvinceCode>
30
- <CountryCode>US</CountryCode>
31
- </DestinationAddress>
32
- <DeliveredDate>2010-07-01</DeliveredDate>
33
- <DeliveredTime>10:43:51</DeliveredTime>
34
- <SignatureProofOfDeliveryAvailable>false</SignatureProofOfDeliveryAvailable>
35
- <Event>
36
- <Date>2010-07-01</Date>
37
- <Time>10:43:51</Time>
38
- <Type>DL</Type>
39
- <Description>Delivered</Description>
40
- <StatusExceptionCode>014</StatusExceptionCode>
41
- <StatusExceptionDescription>Left at garage. Signature Service not requested.</StatusExceptionDescription>
42
- <Address>
43
- <City>Gainesville</City>
44
- <StateOrProvinceCode>GA</StateOrProvinceCode>
45
- <PostalCode>30506</PostalCode>
46
- <CountryCode>US</CountryCode>
47
- </Address>
48
- </Event>
49
- <Event>
50
- <Date>2010-07-01</Date>
51
- <Time>08:48:00</Time>
52
- <Type>OD</Type>
53
- <Description>On FedEx vehicle for delivery</Description>
54
- <Address>
55
- <City>ATHENS</City>
56
- <StateOrProvinceCode>GA</StateOrProvinceCode>
57
- <PostalCode>30601</PostalCode>
58
- <CountryCode>US</CountryCode>
59
- </Address>
60
- </Event>
61
- <Event>
62
- <Date>2010-07-01</Date>
63
- <Time>05:07:00</Time>
64
- <Type>AR</Type>
65
- <Description>At local FedEx facility</Description>
66
- <Address>
67
- <City>ATHENS</City>
68
- <StateOrProvinceCode>GA</StateOrProvinceCode>
69
- <PostalCode>30601</PostalCode>
70
- <CountryCode>US</CountryCode>
71
- </Address>
72
- </Event>
73
- </Package>
74
- </FDXTrack2Reply>
1
+ <SOAP-ENV:Envelope xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'><SOAP-ENV:Header/><SOAP-ENV:Body><TrackReply xmlns='http://fedex.com/ws/track/v8'><HighestSeverity>SUCCESS</HighestSeverity><Notifications><Severity>SUCCESS</Severity><Source>trck</Source><Code>0</Code><Message>Request was successfully processed.</Message><LocalizedMessage>Request was successfully processed.</LocalizedMessage></Notifications><TransactionDetail><CustomerTransactionId>d7200b02a2d97651</CustomerTransactionId></TransactionDetail><Version><ServiceId>trck</ServiceId><Major>8</Major><Intermediate>0</Intermediate><Minor>0</Minor></Version><CompletedTrackDetails><HighestSeverity>SUCCESS</HighestSeverity><DuplicateWaybill>true</DuplicateWaybill><MoreData>false</MoreData><TrackDetailsCount>0</TrackDetailsCount><TrackDetails><Notification><Severity>SUCCESS</Severity><Source>trck</Source><Code>0</Code><Message>Request was successfully processed.</Message><LocalizedMessage>Request was successfully processed.</LocalizedMessage></Notification><TrackingNumber>123456789012</TrackingNumber><TrackingNumberUniqueIdentifier>22013~123456789012~FDEG</TrackingNumberUniqueIdentifier><CarrierCode>FDXG</CarrierCode><OperatingCompanyOrCarrierDescription>FedEx Ground</OperatingCompanyOrCarrierDescription><PackageSequenceNumber>0</PackageSequenceNumber><PackageCount>0</PackageCount><DeliveryAttempts>0</DeliveryAttempts><TotalUniqueAddressCountInConsolidation>0</TotalUniqueAddressCountInConsolidation></TrackDetails><TrackDetails><Notification><Severity>SUCCESS</Severity><Source>trck</Source><Code>0</Code><Message>Request was successfully processed.</Message><LocalizedMessage>Request was successfully processed.</LocalizedMessage></Notification><TrackingNumber>123456789012</TrackingNumber><TrackingNumberUniqueIdentifier>2456464000~123456789012~FX</TrackingNumberUniqueIdentifier><CarrierCode>FDXE</CarrierCode><OperatingCompanyOrCarrierDescription>FedEx Express</OperatingCompanyOrCarrierDescription><PackageSequenceNumber>0</PackageSequenceNumber><PackageCount>0</PackageCount><ShipTimestamp>2013-06-25T00:00:00</ShipTimestamp><DestinationAddress><City>new york</City><StateOrProvinceCode>NY</StateOrProvinceCode><CountryCode>US</CountryCode><CountryName>United States</CountryName><Residential>false</Residential></DestinationAddress><DeliveryAttempts>0</DeliveryAttempts><TotalUniqueAddressCountInConsolidation>0</TotalUniqueAddressCountInConsolidation></TrackDetails><TrackDetails><Notification><Severity>SUCCESS</Severity><Source>trck</Source><Code>0</Code><Message>Request was successfully processed.</Message><LocalizedMessage>Request was successfully processed.</LocalizedMessage></Notification><TrackingNumber>123456789012</TrackingNumber><TrackingNumberUniqueIdentifier>2456470000~123456789012~FX</TrackingNumberUniqueIdentifier><CarrierCode>FDXE</CarrierCode><OperatingCompanyOrCarrierDescription>FedEx Express</OperatingCompanyOrCarrierDescription><PackageSequenceNumber>0</PackageSequenceNumber><PackageCount>0</PackageCount><ShipTimestamp>2013-06-27T00:00:00</ShipTimestamp><DestinationAddress><City>Memphis</City><StateOrProvinceCode>TN</StateOrProvinceCode><CountryCode>US</CountryCode><CountryName>United States</CountryName><Residential>false</Residential></DestinationAddress><DeliveryAttempts>0</DeliveryAttempts><TotalUniqueAddressCountInConsolidation>0</TotalUniqueAddressCountInConsolidation></TrackDetails><TrackDetails><Notification><Severity>SUCCESS</Severity><Source>trck</Source><Code>0</Code><Message>Request was successfully processed.</Message><LocalizedMessage>Request was successfully processed.</LocalizedMessage></Notification><TrackingNumber>123456789012</TrackingNumber><TrackingNumberUniqueIdentifier>2456490000~123456789012~FX</TrackingNumberUniqueIdentifier><CarrierCode>FDXE</CarrierCode><OperatingCompanyOrCarrierDescription>FedEx Express</OperatingCompanyOrCarrierDescription><PackageSequenceNumber>0</PackageSequenceNumber><PackageCount>0</PackageCount><ShipTimestamp>2013-07-16T00:00:00</ShipTimestamp><DestinationAddress><Residential>false</Residential></DestinationAddress><DeliveryAttempts>0</DeliveryAttempts><TotalUniqueAddressCountInConsolidation>0</TotalUniqueAddressCountInConsolidation></TrackDetails><TrackDetails><Notification><Severity>SUCCESS</Severity><Source>trck</Source><Code>0</Code><Message>Request was successfully processed.</Message><LocalizedMessage>Request was successfully processed.</LocalizedMessage></Notification><TrackingNumber>123456789012</TrackingNumber><TrackingNumberUniqueIdentifier>2456492000~123456789012~FX</TrackingNumberUniqueIdentifier><CarrierCode>FDXE</CarrierCode><OperatingCompanyOrCarrierDescription>FedEx Express</OperatingCompanyOrCarrierDescription><PackageSequenceNumber>0</PackageSequenceNumber><PackageCount>0</PackageCount><ShipTimestamp>2013-07-17T00:00:00</ShipTimestamp><DestinationAddress><Residential>false</Residential></DestinationAddress><DeliveryAttempts>0</DeliveryAttempts><TotalUniqueAddressCountInConsolidation>0</TotalUniqueAddressCountInConsolidation></TrackDetails><TrackDetails><Notification><Severity>SUCCESS</Severity><Source>trck</Source><Code>0</Code><Message>Request was successfully processed.</Message><LocalizedMessage>Request was successfully processed.</LocalizedMessage></Notification><TrackingNumber>123456789012</TrackingNumber><TrackingNumberUniqueIdentifier>2456493000~123456789012~FX</TrackingNumberUniqueIdentifier><CarrierCode>FDXE</CarrierCode><OperatingCompanyOrCarrierDescription>FedEx Express</OperatingCompanyOrCarrierDescription><PackageSequenceNumber>0</PackageSequenceNumber><PackageCount>0</PackageCount><ShipTimestamp>2013-07-19T00:00:00</ShipTimestamp><DestinationAddress><Residential>false</Residential></DestinationAddress><DeliveryAttempts>0</DeliveryAttempts><TotalUniqueAddressCountInConsolidation>0</TotalUniqueAddressCountInConsolidation></TrackDetails><TrackDetails><Notification><Severity>SUCCESS</Severity><Source>trck</Source><Code>0</Code><Message>Request was successfully processed.</Message><LocalizedMessage>Request was successfully processed.</LocalizedMessage></Notification><TrackingNumber>123456789012</TrackingNumber><TrackingNumberUniqueIdentifier>2456514000~123456789012~FX</TrackingNumberUniqueIdentifier><CarrierCode>FDXE</CarrierCode><OperatingCompanyOrCarrierDescription>FedEx Express</OperatingCompanyOrCarrierDescription><PackageSequenceNumber>0</PackageSequenceNumber><PackageCount>0</PackageCount><ShipTimestamp>2013-08-09T00:00:00</ShipTimestamp><DestinationAddress><Residential>false</Residential></DestinationAddress><DeliveryAttempts>0</DeliveryAttempts><TotalUniqueAddressCountInConsolidation>0</TotalUniqueAddressCountInConsolidation></TrackDetails><TrackDetails><Notification><Severity>SUCCESS</Severity><Source>trck</Source><Code>0</Code><Message>Request was successfully processed.</Message><LocalizedMessage>Request was successfully processed.</LocalizedMessage></Notification><TrackingNumber>123456789012</TrackingNumber><TrackingNumberUniqueIdentifier>2456525000~123456789012~FX</TrackingNumberUniqueIdentifier><CarrierCode>FDXE</CarrierCode><OperatingCompanyOrCarrierDescription>FedEx Express</OperatingCompanyOrCarrierDescription><PackageSequenceNumber>0</PackageSequenceNumber><PackageCount>0</PackageCount><ShipTimestamp>2013-08-20T00:00:00</ShipTimestamp><DestinationAddress><City>CITY OF INDUSTRY</City><StateOrProvinceCode>CA</StateOrProvinceCode><CountryCode>US</CountryCode><CountryName>United States</CountryName><Residential>false</Residential></DestinationAddress><DeliveryAttempts>0</DeliveryAttempts><TotalUniqueAddressCountInConsolidation>0</TotalUniqueAddressCountInConsolidation></TrackDetails><TrackDetails><Notification><Severity>SUCCESS</Severity><Source>trck</Source><Code>0</Code><Message>Request was successfully processed.</Message><LocalizedMessage>Request was successfully processed.</LocalizedMessage></Notification><TrackingNumber>123456789012</TrackingNumber><TrackingNumberUniqueIdentifier>2456539000~123456789012~FX</TrackingNumberUniqueIdentifier><CarrierCode>FDXE</CarrierCode><OperatingCompanyOrCarrierDescription>FedEx Express</OperatingCompanyOrCarrierDescription><PackageSequenceNumber>0</PackageSequenceNumber><PackageCount>0</PackageCount><ShipTimestamp>2013-09-03T00:00:00</ShipTimestamp><DestinationAddress><Residential>false</Residential></DestinationAddress><DeliveryAttempts>0</DeliveryAttempts><TotalUniqueAddressCountInConsolidation>0</TotalUniqueAddressCountInConsolidation></TrackDetails><TrackDetails><Notification><Severity>SUCCESS</Severity><Source>trck</Source><Code>0</Code><Message>Request was successfully processed.</Message><LocalizedMessage>Request was successfully processed.</LocalizedMessage></Notification><TrackingNumber>123456789012</TrackingNumber><TrackingNumberUniqueIdentifier>2456540000~123456789012~FX</TrackingNumberUniqueIdentifier><CarrierCode>FDXE</CarrierCode><OperatingCompanyOrCarrierDescription>FedEx Express</OperatingCompanyOrCarrierDescription><PackageSequenceNumber>0</PackageSequenceNumber><PackageCount>0</PackageCount><ShipTimestamp>2013-08-24T00:00:00</ShipTimestamp><DestinationAddress><Residential>false</Residential></DestinationAddress><DeliveryAttempts>0</DeliveryAttempts><TotalUniqueAddressCountInConsolidation>0</TotalUniqueAddressCountInConsolidation></TrackDetails><TrackDetails><Notification><Severity>SUCCESS</Severity><Source>trck</Source><Code>0</Code><Message>Request was successfully processed.</Message><LocalizedMessage>Request was successfully processed.</LocalizedMessage></Notification><TrackingNumber>123456789012</TrackingNumber><TrackingNumberUniqueIdentifier>2456560000~123456789012~FX</TrackingNumberUniqueIdentifier><CarrierCode>FDXE</CarrierCode><OperatingCompanyOrCarrierDescription>FedEx Express</OperatingCompanyOrCarrierDescription><PackageSequenceNumber>0</PackageSequenceNumber><PackageCount>0</PackageCount><ShipTimestamp>2013-10-10T00:00:00</ShipTimestamp><DestinationAddress><City>ST JACKSON</City><StateOrProvinceCode>MS</StateOrProvinceCode><CountryCode>US</CountryCode><CountryName>United States</CountryName><Residential>false</Residential></DestinationAddress><DeliveryAttempts>0</DeliveryAttempts><TotalUniqueAddressCountInConsolidation>0</TotalUniqueAddressCountInConsolidation></TrackDetails><TrackDetails><Notification><Severity>SUCCESS</Severity><Source>trck</Source><Code>0</Code><Message>Request was successfully processed.</Message><LocalizedMessage>Request was successfully processed.</LocalizedMessage></Notification><TrackingNumber>123456789012</TrackingNumber><TrackingNumberUniqueIdentifier>2456570000~123456789012~FX</TrackingNumberUniqueIdentifier><CarrierCode>FDXE</CarrierCode><OperatingCompanyOrCarrierDescription>FedEx Express</OperatingCompanyOrCarrierDescription><PackageSequenceNumber>0</PackageSequenceNumber><PackageCount>0</PackageCount><ShipTimestamp>2013-10-03T00:00:00</ShipTimestamp><DestinationAddress><City>Ontonagon</City><StateOrProvinceCode>MI</StateOrProvinceCode><CountryCode>US</CountryCode><CountryName>United States</CountryName><Residential>false</Residential></DestinationAddress><DeliveryAttempts>0</DeliveryAttempts><TotalUniqueAddressCountInConsolidation>0</TotalUniqueAddressCountInConsolidation></TrackDetails></CompletedTrackDetails></TrackReply></SOAP-ENV:Body></SOAP-ENV:Envelope>