active_shipping 0.10.1 → 0.11.0
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.
data/README.markdown
CHANGED
@@ -79,7 +79,7 @@ Active Shipping is currently being used and improved in a production environment
|
|
79
79
|
|
80
80
|
### Track a FedEx package
|
81
81
|
|
82
|
-
fedex = FedEx.new(:login => '999999999', :password => '7777777')
|
82
|
+
fedex = FedEx.new(:login => '999999999', :password => '7777777', key: '1BXXXXXXXXXxrcB', account: '51XXXXX20')
|
83
83
|
tracking_info = fedex.find_tracking_info('tracking-number', :carrier_code => 'fedex_ground') # Ground package
|
84
84
|
|
85
85
|
tracking_info.shipment_events.each do |event|
|
@@ -46,7 +46,8 @@ module ActiveMerchant
|
|
46
46
|
"INTERNATIONAL_ECONOMY_FREIGHT" => "FedEx International Economy Freight",
|
47
47
|
"GROUND_HOME_DELIVERY" => "FedEx Ground Home Delivery",
|
48
48
|
"FEDEX_GROUND" => "FedEx Ground",
|
49
|
-
"INTERNATIONAL_GROUND" => "FedEx International Ground"
|
49
|
+
"INTERNATIONAL_GROUND" => "FedEx International Ground",
|
50
|
+
"SMART_POST" => "FedEx SmartPost"
|
50
51
|
}
|
51
52
|
|
52
53
|
PackageTypes = {
|
@@ -156,13 +157,13 @@ module ActiveMerchant
|
|
156
157
|
def build_rate_request(origin, destination, packages, options={})
|
157
158
|
imperial = ['US','LR','MM'].include?(origin.country_code(:alpha2))
|
158
159
|
|
159
|
-
xml_request = XmlNode.new('RateRequest', 'xmlns' => 'http://fedex.com/ws/rate/
|
160
|
+
xml_request = XmlNode.new('RateRequest', 'xmlns' => 'http://fedex.com/ws/rate/v13') do |root_node|
|
160
161
|
root_node << build_request_header
|
161
162
|
|
162
163
|
# Version
|
163
164
|
root_node << XmlNode.new('Version') do |version_node|
|
164
165
|
version_node << XmlNode.new('ServiceId', 'crs')
|
165
|
-
version_node << XmlNode.new('Major', '
|
166
|
+
version_node << XmlNode.new('Major', '13')
|
166
167
|
version_node << XmlNode.new('Intermediate', '0')
|
167
168
|
version_node << XmlNode.new('Minor', '0')
|
168
169
|
end
|
@@ -175,6 +176,7 @@ module ActiveMerchant
|
|
175
176
|
root_node << XmlNode.new('RequestedShipment') do |rs|
|
176
177
|
rs << XmlNode.new('ShipTimestamp', ship_timestamp(options[:turn_around_time]))
|
177
178
|
rs << XmlNode.new('DropoffType', options[:dropoff_type] || 'REGULAR_PICKUP')
|
179
|
+
#rs << XmlNode.new('ServiceType', 'SMART_POST') # use this to test responses for specific services.
|
178
180
|
rs << XmlNode.new('PackagingType', options[:packaging_type] || 'YOUR_PACKAGING')
|
179
181
|
|
180
182
|
rs << build_location_node('Shipper', (options[:shipper] || origin))
|
@@ -182,11 +184,18 @@ module ActiveMerchant
|
|
182
184
|
if options[:shipper] and options[:shipper] != origin
|
183
185
|
rs << build_location_node('Origin', origin)
|
184
186
|
end
|
185
|
-
|
187
|
+
|
188
|
+
rs << XmlNode.new('SmartPostDetail') do |spd|
|
189
|
+
spd << XmlNode.new('Indicia', options[:smart_post_indicia] || 'PARCEL_SELECT')
|
190
|
+
spd << XmlNode.new('HubId', options[:smart_post_hub_id] || 5902) # default to LA
|
191
|
+
end
|
192
|
+
|
186
193
|
rs << XmlNode.new('RateRequestTypes', 'ACCOUNT')
|
194
|
+
|
187
195
|
rs << XmlNode.new('PackageCount', packages.size)
|
188
196
|
packages.each do |pkg|
|
189
|
-
rs << XmlNode.new('
|
197
|
+
rs << XmlNode.new('RequestedPackageLineItems') do |rps|
|
198
|
+
rps << XmlNode.new('GroupPackageCount', 1)
|
190
199
|
rps << XmlNode.new('Weight') do |tw|
|
191
200
|
tw << XmlNode.new('Units', imperial ? 'LB' : 'KG')
|
192
201
|
tw << XmlNode.new('Value', [((imperial ? pkg.lbs : pkg.kgs).to_f*1000).round/1000.0, 0.1].max)
|
@@ -3,10 +3,10 @@ require 'cgi'
|
|
3
3
|
|
4
4
|
module ActiveMerchant
|
5
5
|
module Shipping
|
6
|
-
|
6
|
+
|
7
7
|
# After getting an API login from USPS (looks like '123YOURNAME456'),
|
8
8
|
# run the following test:
|
9
|
-
#
|
9
|
+
#
|
10
10
|
# usps = USPS.new(:login => '123YOURNAME456', :test => true)
|
11
11
|
# usps.valid_credentials?
|
12
12
|
#
|
@@ -14,20 +14,20 @@ module ActiveMerchant
|
|
14
14
|
# to do before they put your API key in production mode.
|
15
15
|
class USPS < Carrier
|
16
16
|
self.retry_safe = true
|
17
|
-
|
17
|
+
|
18
18
|
cattr_reader :name
|
19
19
|
@@name = "USPS"
|
20
|
-
|
20
|
+
|
21
21
|
LIVE_DOMAIN = 'production.shippingapis.com'
|
22
22
|
LIVE_RESOURCE = 'ShippingAPI.dll'
|
23
|
-
|
23
|
+
|
24
24
|
TEST_DOMAINS = { #indexed by security; e.g. TEST_DOMAINS[USE_SSL[:rates]]
|
25
25
|
true => 'secure.shippingapis.com',
|
26
26
|
false => 'testing.shippingapis.com'
|
27
27
|
}
|
28
|
-
|
28
|
+
|
29
29
|
TEST_RESOURCE = 'ShippingAPITest.dll'
|
30
|
-
|
30
|
+
|
31
31
|
API_CODES = {
|
32
32
|
:us_rates => 'RateV4',
|
33
33
|
:world_rates => 'IntlRateV2',
|
@@ -85,10 +85,10 @@ module ActiveMerchant
|
|
85
85
|
:post_card => 'POSTCARD',
|
86
86
|
:package_service => 'PACKAGESERVICE'
|
87
87
|
}
|
88
|
-
|
88
|
+
|
89
89
|
# Array of U.S. possessions according to USPS: https://www.usps.com/ship/official-abbreviations.htm
|
90
90
|
US_POSSESSIONS = ["AS", "FM", "GU", "MH", "MP", "PW", "PR", "VI"]
|
91
|
-
|
91
|
+
|
92
92
|
# TODO: figure out how USPS likes to say "Ivory Coast"
|
93
93
|
#
|
94
94
|
# Country names:
|
@@ -126,7 +126,6 @@ module ActiveMerchant
|
|
126
126
|
}
|
127
127
|
|
128
128
|
STATUS_NODE_PATTERNS = %w(
|
129
|
-
*/*/TrackSummary
|
130
129
|
Error/Description
|
131
130
|
*/TrackInfo/Error/Description
|
132
131
|
)
|
@@ -138,7 +137,7 @@ module ActiveMerchant
|
|
138
137
|
]
|
139
138
|
|
140
139
|
def find_tracking_info(tracking_number, options={})
|
141
|
-
options = @options.update(options)
|
140
|
+
options = @options.update(options)
|
142
141
|
tracking_request = build_tracking_request(tracking_number, options)
|
143
142
|
response = commit(:track, tracking_request, (options[:test] || false))
|
144
143
|
parse_tracking_response(response, options)
|
@@ -151,9 +150,9 @@ module ActiveMerchant
|
|
151
150
|
'LARGE'
|
152
151
|
end
|
153
152
|
end
|
154
|
-
|
153
|
+
|
155
154
|
# from info at http://www.usps.com/businessmail101/mailcharacteristics/parcels.htm
|
156
|
-
#
|
155
|
+
#
|
157
156
|
# package.options[:books] -- 25 lb. limit instead of 35 for books or other printed matter.
|
158
157
|
# Defaults to false.
|
159
158
|
def self.package_machinable?(package, options={})
|
@@ -167,22 +166,22 @@ module ActiveMerchant
|
|
167
166
|
package.pounds <= (package.options[:books] ? 25.0 : 35.0)
|
168
167
|
at_least_minimum && at_most_maximum
|
169
168
|
end
|
170
|
-
|
169
|
+
|
171
170
|
def requirements
|
172
171
|
[:login]
|
173
172
|
end
|
174
|
-
|
173
|
+
|
175
174
|
def find_rates(origin, destination, packages, options = {})
|
176
175
|
options = @options.merge(options)
|
177
|
-
|
176
|
+
|
178
177
|
origin = Location.from(origin)
|
179
178
|
destination = Location.from(destination)
|
180
179
|
packages = Array(packages)
|
181
|
-
|
180
|
+
|
182
181
|
#raise ArgumentError.new("USPS packages must originate in the U.S.") unless ['US',nil].include?(origin.country_code(:alpha2))
|
183
|
-
|
182
|
+
|
184
183
|
# domestic or international?
|
185
|
-
|
184
|
+
|
186
185
|
domestic_codes = US_POSSESSIONS + ['US', nil]
|
187
186
|
response = if domestic_codes.include?(destination.country_code(:alpha2))
|
188
187
|
us_rates(origin, destination, packages, options)
|
@@ -190,89 +189,17 @@ module ActiveMerchant
|
|
190
189
|
world_rates(origin, destination, packages, options)
|
191
190
|
end
|
192
191
|
end
|
193
|
-
|
192
|
+
|
194
193
|
def valid_credentials?
|
195
194
|
# Cannot test with find_rates because USPS doesn't allow that in test mode
|
196
195
|
test_mode? ? canned_address_verification_works? : super
|
197
196
|
end
|
198
|
-
|
197
|
+
|
199
198
|
def maximum_weight
|
200
199
|
Mass.new(70, :pounds)
|
201
200
|
end
|
202
|
-
|
203
|
-
protected
|
204
|
-
def response_success?(xml)
|
205
|
-
xml.get_text('/*/Response/ResponseStatusCode').to_s == '1'
|
206
|
-
end
|
207
|
-
|
208
|
-
def response_message(xml)
|
209
|
-
xml.get_text('/*/Response/Error/ErrorDescription | /*/Response/ResponseStatusDescription').to_s
|
210
|
-
end
|
211
|
-
|
212
|
-
def parse_tracking_response(response, options={})
|
213
|
-
xml = REXML::Document.new(response)
|
214
|
-
success = response_success?(xml)
|
215
|
-
message = response_message(xml)
|
216
|
-
|
217
|
-
if success
|
218
|
-
tracking_number, origin, destination = nil
|
219
|
-
shipment_events = []
|
220
|
-
|
221
|
-
first_shipment = xml.elements['/*/Shipment']
|
222
|
-
first_package = first_shipment.elements['Package']
|
223
|
-
tracking_number = first_shipment.get_text('ShipmentIdentificationNumber | Package/TrackingNumber').to_s
|
224
|
-
|
225
|
-
origin, destination = %w{Shipper ShipTo}.map do |location|
|
226
|
-
location_from_address_node(first_shipment.elements["#{location}/Address"])
|
227
|
-
end
|
228
|
-
|
229
|
-
activities = first_package.get_elements('Activity')
|
230
|
-
unless activities.empty?
|
231
|
-
shipment_events = activities.map do |activity|
|
232
|
-
description = activity.get_text('Status/StatusType/Description').to_s
|
233
|
-
zoneless_time = if (time = activity.get_text('Time')) &&
|
234
|
-
(date = activity.get_text('Date'))
|
235
|
-
time, date = time.to_s, date.to_s
|
236
|
-
hour, minute, second = time.scan(/\d{2}/)
|
237
|
-
year, month, day = date[0..3], date[4..5], date[6..7]
|
238
|
-
Time.utc(year, month, day, hour, minute, second)
|
239
|
-
end
|
240
|
-
location = location_from_address_node(activity.elements['ActivityLocation/Address'])
|
241
|
-
ShipmentEvent.new(description, zoneless_time, location)
|
242
|
-
end
|
243
|
-
|
244
|
-
shipment_events = shipment_events.sort_by(&:time)
|
245
|
-
|
246
|
-
if origin
|
247
|
-
first_event = shipment_events[0]
|
248
|
-
same_country = origin.country_code(:alpha2) == first_event.location.country_code(:alpha2)
|
249
|
-
same_or_blank_city = first_event.location.city.blank? or first_event.location.city == origin.city
|
250
|
-
origin_event = ShipmentEvent.new(first_event.name, first_event.time, origin)
|
251
|
-
if same_country and same_or_blank_city
|
252
|
-
shipment_events[0] = origin_event
|
253
|
-
else
|
254
|
-
shipment_events.unshift(origin_event)
|
255
|
-
end
|
256
|
-
end
|
257
|
-
if shipment_events.last.name.downcase == 'delivered'
|
258
|
-
shipment_events[-1] = ShipmentEvent.new(shipment_events.last.name, shipment_events.last.time, destination)
|
259
|
-
end
|
260
|
-
end
|
261
|
-
|
262
|
-
end
|
263
|
-
TrackingResponse.new(success, message, Hash.from_xml(response).values.first,
|
264
|
-
:xml => response,
|
265
|
-
:request => last_request,
|
266
|
-
:shipment_events => shipment_events,
|
267
|
-
:origin => origin,
|
268
|
-
:destination => destination,
|
269
|
-
:tracking_number => tracking_number)
|
270
|
-
end
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
201
|
|
202
|
+
protected
|
276
203
|
|
277
204
|
def build_tracking_request(tracking_number, options={})
|
278
205
|
xml_request = XmlNode.new('TrackRequest', 'USERID' => @options[:login]) do |root_node|
|
@@ -286,13 +213,13 @@ module ActiveMerchant
|
|
286
213
|
# never use test mode; rate requests just won't work on test servers
|
287
214
|
parse_rate_response origin, destination, packages, commit(:us_rates,request,false), options
|
288
215
|
end
|
289
|
-
|
216
|
+
|
290
217
|
def world_rates(origin, destination, packages, options={})
|
291
218
|
request = build_world_rate_request(packages, destination)
|
292
219
|
# never use test mode; rate requests just won't work on test servers
|
293
220
|
parse_rate_response origin, destination, packages, commit(:world_rates,request,false), options
|
294
221
|
end
|
295
|
-
|
222
|
+
|
296
223
|
# Once the address verification API is implemented, remove this and have valid_credentials? build the request using that instead.
|
297
224
|
def canned_address_verification_works?
|
298
225
|
request = "%3CCarrierPickupAvailabilityRequest%20USERID=%22#{URI.encode(@options[:login])}%22%3E%20%0A%3CFirmName%3EABC%20Corp.%3C/FirmName%3E%20%0A%3CSuiteOrApt%3ESuite%20777%3C/SuiteOrApt%3E%20%0A%3CAddress2%3E1390%20Market%20Street%3C/Address2%3E%20%0A%3CUrbanization%3E%3C/Urbanization%3E%20%0A%3CCity%3EHouston%3C/City%3E%20%0A%3CState%3ETX%3C/State%3E%20%0A%3CZIP5%3E77058%3C/ZIP5%3E%20%0A%3CZIP4%3E1234%3C/ZIP4%3E%20%0A%3C/CarrierPickupAvailabilityRequest%3E%0A"
|
@@ -301,7 +228,7 @@ module ActiveMerchant
|
|
301
228
|
xml.get_text('/CarrierPickupAvailabilityResponse/City').to_s == 'HOUSTON' &&
|
302
229
|
xml.get_text('/CarrierPickupAvailabilityResponse/Address2').to_s == '1390 Market Street'
|
303
230
|
end
|
304
|
-
|
231
|
+
|
305
232
|
# options[:service] -- One of [:first_class, :priority, :express, :bpm, :parcel,
|
306
233
|
# :media, :library, :all]. defaults to :all.
|
307
234
|
# options[:container] -- One of [:envelope, :box]. defaults to neither (this field has
|
@@ -345,14 +272,14 @@ module ActiveMerchant
|
|
345
272
|
end
|
346
273
|
URI.encode(save_request(request.to_s))
|
347
274
|
end
|
348
|
-
|
275
|
+
|
349
276
|
# important difference with international rate requests:
|
350
277
|
# * services are not given in the request
|
351
278
|
# * package sizes are not given in the request
|
352
279
|
# * services are returned in the response along with restrictions of size
|
353
280
|
# * the size restrictions are returned AS AN ENGLISH SENTENCE (!?)
|
354
281
|
#
|
355
|
-
#
|
282
|
+
#
|
356
283
|
# package.options[:mail_type] -- one of [:package, :postcard, :matter_for_the_blind, :envelope].
|
357
284
|
# Defaults to :package.
|
358
285
|
def build_world_rate_request(packages, destination)
|
@@ -389,14 +316,14 @@ module ActiveMerchant
|
|
389
316
|
end
|
390
317
|
URI.encode(save_request(request.to_s))
|
391
318
|
end
|
392
|
-
|
319
|
+
|
393
320
|
def parse_rate_response(origin, destination, packages, response, options={})
|
394
321
|
success = true
|
395
322
|
message = ''
|
396
323
|
rate_hash = {}
|
397
|
-
|
324
|
+
|
398
325
|
xml = REXML::Document.new(response)
|
399
|
-
|
326
|
+
|
400
327
|
if error = xml.elements['/Error']
|
401
328
|
success = false
|
402
329
|
message = error.elements['Description'].text
|
@@ -408,7 +335,7 @@ module ActiveMerchant
|
|
408
335
|
break
|
409
336
|
end
|
410
337
|
end
|
411
|
-
|
338
|
+
|
412
339
|
if success
|
413
340
|
rate_hash = rates_from_response_node(xml, packages)
|
414
341
|
unless rate_hash
|
@@ -416,9 +343,9 @@ module ActiveMerchant
|
|
416
343
|
message = "Unknown root node in XML response: '#{xml.root.name}'"
|
417
344
|
end
|
418
345
|
end
|
419
|
-
|
346
|
+
|
420
347
|
end
|
421
|
-
|
348
|
+
|
422
349
|
if success
|
423
350
|
rate_estimates = rate_hash.keys.map do |service_name|
|
424
351
|
RateEstimate.new(origin,destination,@@name,"USPS #{service_name}",
|
@@ -429,15 +356,15 @@ module ActiveMerchant
|
|
429
356
|
rate_estimates.reject! {|e| e.package_count != packages.length}
|
430
357
|
rate_estimates = rate_estimates.sort_by(&:total_price)
|
431
358
|
end
|
432
|
-
|
359
|
+
|
433
360
|
RateResponse.new(success, message, Hash.from_xml(response), :rates => rate_estimates, :xml => response, :request => last_request)
|
434
361
|
end
|
435
|
-
|
362
|
+
|
436
363
|
def rates_from_response_node(response_node, packages)
|
437
364
|
rate_hash = {}
|
438
365
|
return false unless (root_node = response_node.elements['/IntlRateV2Response | /RateV4Response'])
|
439
366
|
domestic = (root_node.name == 'RateV4Response')
|
440
|
-
|
367
|
+
|
441
368
|
if @options[:commercial_base]
|
442
369
|
domestic_elements = ['Postage', 'CLASSID', 'MailService', 'CommercialRate']
|
443
370
|
international_elements = ['Service', 'ID', 'SvcDescription', 'CommercialPostage']
|
@@ -446,10 +373,10 @@ module ActiveMerchant
|
|
446
373
|
international_elements = ['Service', 'ID', 'SvcDescription', 'Postage']
|
447
374
|
end
|
448
375
|
service_node, service_code_node, service_name_node, rate_node = domestic ? domestic_elements : international_elements
|
449
|
-
|
376
|
+
|
450
377
|
root_node.each_element('Package') do |package_node|
|
451
378
|
this_package = packages[package_node.attributes['ID'].to_i]
|
452
|
-
|
379
|
+
|
453
380
|
package_node.each_element(service_node) do |service_response_node|
|
454
381
|
service_name = service_response_node.get_text(service_name_node).to_s
|
455
382
|
|
@@ -470,18 +397,18 @@ module ActiveMerchant
|
|
470
397
|
package_rates = this_service[:package_rates] ||= []
|
471
398
|
this_package_rate = {:package => this_package,
|
472
399
|
:rate => Package.cents_from(service_response_node.get_text(rate_node).to_s.to_f)}
|
473
|
-
|
400
|
+
|
474
401
|
package_rates << this_package_rate if package_valid_for_service(this_package,service_response_node)
|
475
402
|
end
|
476
403
|
end
|
477
404
|
rate_hash
|
478
405
|
end
|
479
|
-
|
406
|
+
|
480
407
|
def package_valid_for_service(package, service_node)
|
481
408
|
return true if service_node.elements['MaxWeight'].nil?
|
482
409
|
max_weight = service_node.get_text('MaxWeight').to_s.to_f
|
483
410
|
name = service_node.get_text('SvcDescription | MailService').to_s.downcase
|
484
|
-
|
411
|
+
|
485
412
|
if name =~ /flat.rate.box/ #domestic or international flat rate box
|
486
413
|
# flat rate dimensions from http://www.usps.com/shipping/flatrate.htm
|
487
414
|
return (package_valid_for_max_dimensions(package,
|
@@ -507,7 +434,7 @@ module ActiveMerchant
|
|
507
434
|
#
|
508
435
|
# 'Max. length 46", width 35", height 46" and max. length plus girth 108"'
|
509
436
|
# 'Max. length 24", Max. length, height, depth combined 36"'
|
510
|
-
#
|
437
|
+
#
|
511
438
|
sentence = CGI.unescapeHTML(service_node.get_text('MaxDimensions').to_s)
|
512
439
|
tokens = sentence.downcase.split(/[^\d]*"/).reject {|t| t.empty?}
|
513
440
|
max_dimensions = {:weight => max_weight}
|
@@ -515,7 +442,7 @@ module ActiveMerchant
|
|
515
442
|
tokens.each do |token|
|
516
443
|
axis_sum = [/length/,/width/,/height/,/depth/].sum {|regex| (token =~ regex) ? 1 : 0}
|
517
444
|
unless axis_sum == 0
|
518
|
-
value = token[/\d+$/].to_f
|
445
|
+
value = token[/\d+$/].to_f
|
519
446
|
if axis_sum == 3
|
520
447
|
max_dimensions[:length_plus_width_plus_height] = value
|
521
448
|
elsif token =~ /girth/ and axis_sum == 1
|
@@ -532,7 +459,7 @@ module ActiveMerchant
|
|
532
459
|
return package_valid_for_max_dimensions(package, max_dimensions)
|
533
460
|
end
|
534
461
|
end
|
535
|
-
|
462
|
+
|
536
463
|
def package_valid_for_max_dimensions(package,dimensions)
|
537
464
|
valid = ((not ([:length,:width,:height].map {|dim| dimensions[dim].nil? || dimensions[dim].to_f >= package.inches(dim).to_f}.include?(false))) and
|
538
465
|
(dimensions[:weight].nil? || dimensions[:weight] >= package.pounds) and
|
@@ -549,18 +476,18 @@ module ActiveMerchant
|
|
549
476
|
def parse_tracking_response(response, options)
|
550
477
|
xml = REXML::Document.new(response)
|
551
478
|
root_node = xml.elements['TrackResponse']
|
552
|
-
|
479
|
+
|
553
480
|
success = response_success?(xml)
|
554
481
|
message = response_message(xml)
|
555
|
-
|
482
|
+
|
556
483
|
if success
|
557
484
|
tracking_number, origin, destination = nil
|
558
485
|
shipment_events = []
|
559
486
|
tracking_details = xml.elements.collect('*/*/TrackDetail'){ |e| e }
|
560
|
-
|
487
|
+
|
561
488
|
tracking_summary = xml.elements.collect('*/*/TrackSummary'){ |e| e }.first
|
562
489
|
tracking_details << tracking_summary
|
563
|
-
|
490
|
+
|
564
491
|
tracking_number = root_node.elements['TrackInfo'].attributes['ID'].to_s
|
565
492
|
|
566
493
|
tracking_details.each do |event|
|
@@ -584,7 +511,7 @@ module ActiveMerchant
|
|
584
511
|
end
|
585
512
|
shipment_events = shipment_events.sort_by(&:time)
|
586
513
|
end
|
587
|
-
|
514
|
+
|
588
515
|
TrackingResponse.new(success, message, Hash.from_xml(response),
|
589
516
|
:carrier => @@name,
|
590
517
|
:xml => response,
|
@@ -594,40 +521,66 @@ module ActiveMerchant
|
|
594
521
|
:tracking_number => tracking_number
|
595
522
|
)
|
596
523
|
end
|
597
|
-
|
598
|
-
def
|
524
|
+
|
525
|
+
def track_summary_node(document)
|
526
|
+
document.elements['*/*/TrackSummary']
|
527
|
+
end
|
528
|
+
|
529
|
+
def error_description_node(document)
|
599
530
|
STATUS_NODE_PATTERNS.each do |pattern|
|
600
531
|
if node = document.elements[pattern]
|
601
532
|
return node
|
602
533
|
end
|
603
534
|
end
|
604
535
|
end
|
605
|
-
|
536
|
+
|
537
|
+
def response_status_node(document)
|
538
|
+
track_summary_node(document) || error_description_node(document)
|
539
|
+
end
|
540
|
+
|
541
|
+
def has_error?(document)
|
542
|
+
!!document.elements['Error']
|
543
|
+
end
|
544
|
+
|
545
|
+
def no_record?(document)
|
546
|
+
summary_node = track_summary_node(document)
|
547
|
+
if summary_node
|
548
|
+
summary = summary_node.get_text.to_s
|
549
|
+
RESPONSE_ERROR_MESSAGES.detect { |re| summary =~ re }
|
550
|
+
summary =~ /There is no record of that mail item/ || summary =~ /This Information has not been included in this Test Server\./
|
551
|
+
else
|
552
|
+
false
|
553
|
+
end
|
554
|
+
end
|
555
|
+
|
556
|
+
def tracking_info_error?(document)
|
557
|
+
document.elements['*/TrackInfo/Error']
|
558
|
+
end
|
559
|
+
|
606
560
|
def response_success?(document)
|
607
|
-
|
608
|
-
!RESPONSE_ERROR_MESSAGES.detect { |re| summary =~ re }
|
561
|
+
!(has_error?(document) || no_record?(document) || tracking_info_error?(document))
|
609
562
|
end
|
610
|
-
|
563
|
+
|
611
564
|
def response_message(document)
|
612
565
|
response_node = response_status_node(document)
|
613
|
-
|
566
|
+
response_node.get_text.to_s
|
614
567
|
end
|
615
|
-
|
568
|
+
|
616
569
|
def commit(action, request, test = false)
|
617
570
|
ssl_get(request_url(action, request, test))
|
618
571
|
end
|
619
|
-
|
572
|
+
|
620
573
|
def request_url(action, request, test)
|
621
574
|
scheme = USE_SSL[action] ? 'https://' : 'http://'
|
622
575
|
host = test ? TEST_DOMAINS[USE_SSL[action]] : LIVE_DOMAIN
|
623
576
|
resource = test ? TEST_RESOURCE : LIVE_RESOURCE
|
624
577
|
"#{scheme}#{host}/#{resource}?API=#{API_CODES[action]}&XML=#{request}"
|
625
578
|
end
|
626
|
-
|
579
|
+
|
627
580
|
def strip_zip(zip)
|
628
581
|
zip.to_s.scan(/\d{5}/).first || zip
|
629
582
|
end
|
630
|
-
|
583
|
+
|
631
584
|
end
|
632
585
|
end
|
633
586
|
end
|
metadata
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_shipping
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.11.0
|
4
5
|
prerelease:
|
5
|
-
version: 0.10.1
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- James MacAulay
|
@@ -12,168 +12,168 @@ authors:
|
|
12
12
|
autorequire:
|
13
13
|
bindir: bin
|
14
14
|
cert_chain: []
|
15
|
-
date: 2013-
|
15
|
+
date: 2013-09-15 00:00:00.000000000 Z
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
|
-
prerelease: false
|
19
18
|
name: activesupport
|
20
|
-
|
21
|
-
|
19
|
+
requirement: !ruby/object:Gem::Requirement
|
20
|
+
none: false
|
22
21
|
requirements:
|
23
22
|
- - ! '>='
|
24
23
|
- !ruby/object:Gem::Version
|
25
24
|
version: 2.3.5
|
25
|
+
type: :runtime
|
26
|
+
prerelease: false
|
27
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
28
|
none: false
|
27
|
-
requirement: !ruby/object:Gem::Requirement
|
28
29
|
requirements:
|
29
30
|
- - ! '>='
|
30
31
|
- !ruby/object:Gem::Version
|
31
32
|
version: 2.3.5
|
32
|
-
none: false
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
|
-
prerelease: false
|
35
34
|
name: i18n
|
36
|
-
|
37
|
-
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
none: false
|
38
37
|
requirements:
|
39
38
|
- - ! '>='
|
40
39
|
- !ruby/object:Gem::Version
|
41
40
|
version: '0'
|
41
|
+
type: :runtime
|
42
|
+
prerelease: false
|
43
|
+
version_requirements: !ruby/object:Gem::Requirement
|
42
44
|
none: false
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
45
|
requirements:
|
45
46
|
- - ! '>='
|
46
47
|
- !ruby/object:Gem::Version
|
47
48
|
version: '0'
|
48
|
-
none: false
|
49
49
|
- !ruby/object:Gem::Dependency
|
50
|
-
prerelease: false
|
51
50
|
name: active_utils
|
52
|
-
|
53
|
-
|
51
|
+
requirement: !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
54
53
|
requirements:
|
55
54
|
- - ! '>='
|
56
55
|
- !ruby/object:Gem::Version
|
57
56
|
version: 1.0.1
|
57
|
+
type: :runtime
|
58
|
+
prerelease: false
|
59
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
60
|
none: false
|
59
|
-
requirement: !ruby/object:Gem::Requirement
|
60
61
|
requirements:
|
61
62
|
- - ! '>='
|
62
63
|
- !ruby/object:Gem::Version
|
63
64
|
version: 1.0.1
|
64
|
-
none: false
|
65
65
|
- !ruby/object:Gem::Dependency
|
66
|
-
prerelease: false
|
67
66
|
name: builder
|
68
|
-
|
69
|
-
|
67
|
+
requirement: !ruby/object:Gem::Requirement
|
68
|
+
none: false
|
70
69
|
requirements:
|
71
70
|
- - ! '>='
|
72
71
|
- !ruby/object:Gem::Version
|
73
72
|
version: '0'
|
73
|
+
type: :runtime
|
74
|
+
prerelease: false
|
75
|
+
version_requirements: !ruby/object:Gem::Requirement
|
74
76
|
none: false
|
75
|
-
requirement: !ruby/object:Gem::Requirement
|
76
77
|
requirements:
|
77
78
|
- - ! '>='
|
78
79
|
- !ruby/object:Gem::Version
|
79
80
|
version: '0'
|
80
|
-
none: false
|
81
81
|
- !ruby/object:Gem::Dependency
|
82
|
-
prerelease: false
|
83
82
|
name: json
|
84
|
-
|
85
|
-
|
83
|
+
requirement: !ruby/object:Gem::Requirement
|
84
|
+
none: false
|
86
85
|
requirements:
|
87
86
|
- - ! '>='
|
88
87
|
- !ruby/object:Gem::Version
|
89
88
|
version: 1.5.1
|
89
|
+
type: :runtime
|
90
|
+
prerelease: false
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
90
92
|
none: false
|
91
|
-
requirement: !ruby/object:Gem::Requirement
|
92
93
|
requirements:
|
93
94
|
- - ! '>='
|
94
95
|
- !ruby/object:Gem::Version
|
95
96
|
version: 1.5.1
|
96
|
-
none: false
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
|
-
prerelease: false
|
99
98
|
name: minitest
|
100
|
-
|
101
|
-
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
none: false
|
102
101
|
requirements:
|
103
102
|
- - ~>
|
104
103
|
- !ruby/object:Gem::Version
|
105
104
|
version: 4.7.5
|
105
|
+
type: :development
|
106
|
+
prerelease: false
|
107
|
+
version_requirements: !ruby/object:Gem::Requirement
|
106
108
|
none: false
|
107
|
-
requirement: !ruby/object:Gem::Requirement
|
108
109
|
requirements:
|
109
110
|
- - ~>
|
110
111
|
- !ruby/object:Gem::Version
|
111
112
|
version: 4.7.5
|
112
|
-
none: false
|
113
113
|
- !ruby/object:Gem::Dependency
|
114
|
-
prerelease: false
|
115
114
|
name: rake
|
116
|
-
|
117
|
-
|
115
|
+
requirement: !ruby/object:Gem::Requirement
|
116
|
+
none: false
|
118
117
|
requirements:
|
119
118
|
- - ! '>='
|
120
119
|
- !ruby/object:Gem::Version
|
121
120
|
version: '0'
|
121
|
+
type: :development
|
122
|
+
prerelease: false
|
123
|
+
version_requirements: !ruby/object:Gem::Requirement
|
122
124
|
none: false
|
123
|
-
requirement: !ruby/object:Gem::Requirement
|
124
125
|
requirements:
|
125
126
|
- - ! '>='
|
126
127
|
- !ruby/object:Gem::Version
|
127
128
|
version: '0'
|
128
|
-
none: false
|
129
129
|
- !ruby/object:Gem::Dependency
|
130
|
-
prerelease: false
|
131
130
|
name: mocha
|
132
|
-
|
133
|
-
|
131
|
+
requirement: !ruby/object:Gem::Requirement
|
132
|
+
none: false
|
134
133
|
requirements:
|
135
134
|
- - ~>
|
136
135
|
- !ruby/object:Gem::Version
|
137
136
|
version: 0.14.0
|
137
|
+
type: :development
|
138
|
+
prerelease: false
|
139
|
+
version_requirements: !ruby/object:Gem::Requirement
|
138
140
|
none: false
|
139
|
-
requirement: !ruby/object:Gem::Requirement
|
140
141
|
requirements:
|
141
142
|
- - ~>
|
142
143
|
- !ruby/object:Gem::Version
|
143
144
|
version: 0.14.0
|
144
|
-
none: false
|
145
145
|
- !ruby/object:Gem::Dependency
|
146
|
-
prerelease: false
|
147
146
|
name: timecop
|
148
|
-
|
149
|
-
|
147
|
+
requirement: !ruby/object:Gem::Requirement
|
148
|
+
none: false
|
150
149
|
requirements:
|
151
150
|
- - ! '>='
|
152
151
|
- !ruby/object:Gem::Version
|
153
152
|
version: '0'
|
153
|
+
type: :development
|
154
|
+
prerelease: false
|
155
|
+
version_requirements: !ruby/object:Gem::Requirement
|
154
156
|
none: false
|
155
|
-
requirement: !ruby/object:Gem::Requirement
|
156
157
|
requirements:
|
157
158
|
- - ! '>='
|
158
159
|
- !ruby/object:Gem::Version
|
159
160
|
version: '0'
|
160
|
-
none: false
|
161
161
|
- !ruby/object:Gem::Dependency
|
162
|
-
prerelease: false
|
163
162
|
name: nokogiri
|
164
|
-
|
165
|
-
|
163
|
+
requirement: !ruby/object:Gem::Requirement
|
164
|
+
none: false
|
166
165
|
requirements:
|
167
166
|
- - ! '>='
|
168
167
|
- !ruby/object:Gem::Version
|
169
168
|
version: '0'
|
169
|
+
type: :development
|
170
|
+
prerelease: false
|
171
|
+
version_requirements: !ruby/object:Gem::Requirement
|
170
172
|
none: false
|
171
|
-
requirement: !ruby/object:Gem::Requirement
|
172
173
|
requirements:
|
173
174
|
- - ! '>='
|
174
175
|
- !ruby/object:Gem::Version
|
175
176
|
version: '0'
|
176
|
-
none: false
|
177
177
|
description: Get rates and tracking info from various shipping carriers.
|
178
178
|
email:
|
179
179
|
- james@shopify.com
|
@@ -237,20 +237,20 @@ rdoc_options: []
|
|
237
237
|
require_paths:
|
238
238
|
- lib
|
239
239
|
required_ruby_version: !ruby/object:Gem::Requirement
|
240
|
+
none: false
|
240
241
|
requirements:
|
241
242
|
- - ! '>='
|
242
243
|
- !ruby/object:Gem::Version
|
244
|
+
version: '0'
|
243
245
|
segments:
|
244
246
|
- 0
|
245
|
-
hash:
|
246
|
-
version: '0'
|
247
|
-
none: false
|
247
|
+
hash: -2134793660123218894
|
248
248
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
249
|
+
none: false
|
249
250
|
requirements:
|
250
251
|
- - ! '>='
|
251
252
|
- !ruby/object:Gem::Version
|
252
253
|
version: 1.3.6
|
253
|
-
none: false
|
254
254
|
requirements: []
|
255
255
|
rubyforge_project: active_shipping
|
256
256
|
rubygems_version: 1.8.23
|