trackerific 0.7.1 → 0.7.2

Sign up to get free protection for your applications and to get access to all the features.
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>