active_shipping 0.12.6 → 1.0.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +2 -0
  3. data.tar.gz.sig +0 -0
  4. data/{CHANGELOG → CHANGELOG.md} +6 -2
  5. data/CONTRIBUTING.md +32 -0
  6. data/{README.markdown → README.md} +45 -61
  7. data/lib/active_shipping.rb +20 -28
  8. data/lib/active_shipping/carrier.rb +82 -0
  9. data/lib/active_shipping/carriers.rb +33 -0
  10. data/lib/active_shipping/carriers/benchmark_carrier.rb +31 -0
  11. data/lib/active_shipping/carriers/bogus_carrier.rb +12 -0
  12. data/lib/active_shipping/carriers/canada_post.rb +253 -0
  13. data/lib/active_shipping/carriers/canada_post_pws.rb +870 -0
  14. data/lib/active_shipping/carriers/fedex.rb +579 -0
  15. data/lib/active_shipping/carriers/kunaki.rb +164 -0
  16. data/lib/active_shipping/carriers/new_zealand_post.rb +262 -0
  17. data/lib/active_shipping/carriers/shipwire.rb +181 -0
  18. data/lib/active_shipping/carriers/stamps.rb +861 -0
  19. data/lib/active_shipping/carriers/ups.rb +648 -0
  20. data/lib/active_shipping/carriers/usps.rb +642 -0
  21. data/lib/active_shipping/errors.rb +7 -0
  22. data/lib/active_shipping/label_response.rb +23 -0
  23. data/lib/active_shipping/location.rb +149 -0
  24. data/lib/active_shipping/package.rb +241 -0
  25. data/lib/active_shipping/rate_estimate.rb +64 -0
  26. data/lib/active_shipping/rate_response.rb +13 -0
  27. data/lib/active_shipping/response.rb +41 -0
  28. data/lib/active_shipping/shipment_event.rb +17 -0
  29. data/lib/active_shipping/shipment_packer.rb +73 -0
  30. data/lib/active_shipping/shipping_response.rb +12 -0
  31. data/lib/active_shipping/tracking_response.rb +52 -0
  32. data/lib/active_shipping/version.rb +1 -1
  33. data/lib/vendor/quantified/test/length_test.rb +2 -2
  34. data/lib/vendor/xml_node/test/test_parsing.rb +1 -1
  35. metadata +58 -36
  36. metadata.gz.sig +0 -0
  37. data/lib/active_shipping/shipping/base.rb +0 -13
  38. data/lib/active_shipping/shipping/carrier.rb +0 -84
  39. data/lib/active_shipping/shipping/carriers.rb +0 -23
  40. data/lib/active_shipping/shipping/carriers/benchmark_carrier.rb +0 -33
  41. data/lib/active_shipping/shipping/carriers/bogus_carrier.rb +0 -14
  42. data/lib/active_shipping/shipping/carriers/canada_post.rb +0 -257
  43. data/lib/active_shipping/shipping/carriers/canada_post_pws.rb +0 -874
  44. data/lib/active_shipping/shipping/carriers/fedex.rb +0 -581
  45. data/lib/active_shipping/shipping/carriers/kunaki.rb +0 -166
  46. data/lib/active_shipping/shipping/carriers/new_zealand_post.rb +0 -262
  47. data/lib/active_shipping/shipping/carriers/shipwire.rb +0 -184
  48. data/lib/active_shipping/shipping/carriers/stamps.rb +0 -864
  49. data/lib/active_shipping/shipping/carriers/ups.rb +0 -650
  50. data/lib/active_shipping/shipping/carriers/usps.rb +0 -649
  51. data/lib/active_shipping/shipping/errors.rb +0 -9
  52. data/lib/active_shipping/shipping/label_response.rb +0 -25
  53. data/lib/active_shipping/shipping/location.rb +0 -152
  54. data/lib/active_shipping/shipping/package.rb +0 -243
  55. data/lib/active_shipping/shipping/rate_estimate.rb +0 -66
  56. data/lib/active_shipping/shipping/rate_response.rb +0 -15
  57. data/lib/active_shipping/shipping/response.rb +0 -43
  58. data/lib/active_shipping/shipping/shipment_event.rb +0 -19
  59. data/lib/active_shipping/shipping/shipment_packer.rb +0 -75
  60. data/lib/active_shipping/shipping/shipping_response.rb +0 -14
  61. data/lib/active_shipping/shipping/tracking_response.rb +0 -54
@@ -1,864 +0,0 @@
1
- require 'cgi'
2
- require 'builder'
3
-
4
- module ActiveMerchant
5
- module Shipping
6
- # Stamps.com integration for rating, tracking, address validation, and label generation
7
- # Integration ID can be requested from Stamps.com
8
-
9
- class Stamps < Carrier
10
- self.ssl_version = :SSLv3
11
-
12
- cattr_reader :name
13
- @@name = 'Stamps'
14
-
15
- attr_reader :last_swsim_method
16
-
17
- # TODO: Update to latest API. Documentation for the latest WSDL version is available here: http://support.stamps.com/outgoing/swsimv39doc.zip
18
- LIVE_URL = 'https://swsim.stamps.com/swsim/swsimv34.asmx'
19
- TEST_URL = 'https://swsim.testing.stamps.com/swsim/swsimv34.asmx'
20
- NAMESPACE = 'http://stamps.com/xml/namespace/2014/01/swsim/swsimv34'
21
-
22
- REQUIRED_OPTIONS = [:integration_id, :username, :password].freeze
23
-
24
- PACKAGE = [
25
- 'Postcard',
26
- 'Letter',
27
- 'Large Envelope or Flat',
28
- 'Thick Envelope',
29
- 'Package',
30
- 'Flat Rate Box',
31
- 'Small Flat Rate Box',
32
- 'Large Flat Rate Box',
33
- 'Flat Rate Envelope',
34
- 'Flat Rate Padded Envelope',
35
- 'Large Package',
36
- 'Oversized Package',
37
- 'Regional Rate Box A',
38
- 'Regional Rate Box B',
39
- 'Regional Rate Box C',
40
- 'Legal Flat Rate Envelope'
41
- ].freeze
42
-
43
- US_POSSESSIONS = %w(AS FM GU MH MP PW PR VI)
44
-
45
- SERVICE_TYPES = {
46
- 'US-FC' => 'USPS First-Class Mail',
47
- 'US-MM' => 'USPS Media Mail',
48
- 'US-PM' => 'USPS Priority Mail',
49
- 'US-BP' => 'USPS BP',
50
- 'US-LM' => 'USPS LM',
51
- 'US-XM' => 'USPS Express Mail',
52
- 'US-EMI' => 'USPS Express Mail International',
53
- 'US-PMI' => 'USPS Priority Mail International',
54
- 'US-FCI' => 'USPS First Class Mail International',
55
- 'US-CM' => 'USPS Critical Mail',
56
- 'US-PS' => 'USPS Parcel Select'
57
- }
58
-
59
- ADD_ONS = {
60
- 'SC-A-HP' => 'Hidden Postage',
61
- 'SC-A-INS' => 'Insurance',
62
- 'SC-A-INSRM' => 'Insurance for Registered Mail',
63
- 'US-A-CM' => 'Certified Mail',
64
- 'US-A-COD' => 'Collect on Delivery',
65
- 'US-A-COM' => 'Certificate of Mailing',
66
- 'US-A-DC' => 'USPS Delivery Confirmation',
67
- 'US-A-ESH' => 'USPS Express - Sunday / Holiday Guaranteed',
68
- 'US-A-INS' => 'USPS Insurance',
69
- 'US-A-NDW' => 'USPS Express - No Delivery on Saturdays',
70
- 'US-A-RD' => 'Restricted Delivery',
71
- 'US-A-REG' => 'Registered Mail',
72
- 'US-A-RR' => 'Return Receipt Requested',
73
- 'US-A-RRM' => 'Return Receipt for Merchandise',
74
- 'US-A-SC' => 'USPS Signature Confirmation',
75
- 'US-A-SH' => 'Special Handling',
76
- 'US-A-NND' => 'Notice of non-delivery',
77
- 'US-A-SR' => 'Unknow Service Name SR',
78
- 'US-A-RRE' => 'Unknow Service Name RRE'
79
- }
80
-
81
- CARRIER_PICKUP_LOCATION = {
82
- 'FrontDoor' => 'Packages are at front door',
83
- 'BackDoor' => 'Packages are at back door',
84
- 'SideDoor' => 'Packages are at side door',
85
- 'KnockOnDoorOrRingBell' => 'Knock on door or ring bell',
86
- 'MailRoom' => 'Packages are in mail room',
87
- 'Office' => 'Packages are in office',
88
- 'Reception' => 'Packages are at reception area',
89
- 'InOrAtMailbox' => 'Packages are in mail box',
90
- 'Other' => 'Other Location'
91
- }
92
-
93
- PRINT_LAYOUTS = [
94
- 'Normal',
95
- 'NormalLeft',
96
- 'NormalRight',
97
- 'Normal4X6',
98
- 'Normal6X4',
99
- 'Normal75X2',
100
- 'NormalReceipt',
101
- 'NormalCN22',
102
- 'NormalCP72',
103
- 'Normal4X6CN22',
104
- 'Normal6X4CN22',
105
- 'Normal4X6CP72',
106
- 'Normal6X4CP72',
107
- 'Normal4X675',
108
- 'Normal4X675CN22',
109
- 'Normal4X675CP72',
110
- 'Return',
111
- 'ReturnCN22',
112
- 'ReturnCP72',
113
- 'Return4X675',
114
- 'Return4X675CN22',
115
- 'Return4X675CP72',
116
- 'SDC3510',
117
- 'SDC3520',
118
- 'SDC3530',
119
- 'SDC3610',
120
- 'SDC3710',
121
- 'SDC3810',
122
- 'SDC3820',
123
- 'SDC3910',
124
- 'Envelope9',
125
- 'Envelope10',
126
- 'Envelope11',
127
- 'Envelope12',
128
- 'EnvelopePersonal',
129
- 'EnvelopeMonarch',
130
- 'EnvelopeInvitation',
131
- 'EnvelopeGreeting'
132
- ]
133
-
134
- IMAGE_TYPE = %w(Auto Epl Gif Jpg Pdf Png Zpl)
135
-
136
- def account_info
137
- request = build_get_account_info_request
138
- commit(:GetAccountInfo, request)
139
- end
140
-
141
- def purchase_postage(purchase_amount, control_total)
142
- request = build_purchase_postage_request(purchase_amount, control_total)
143
- commit(:PurchasePostage, request)
144
- end
145
-
146
- def purchase_status(transaction_id)
147
- request = build_get_purchase_status(transaction_id)
148
- commit(:GetPurchaseStatus, request)
149
- end
150
-
151
- def validate_address(address, options = {})
152
- address = standardize_address(address)
153
- request = build_cleanse_address_request(address)
154
- commit(:CleanseAddress, request)
155
- end
156
-
157
- def find_rates(origin, destination, package, options = {})
158
- origin = standardize_address(origin)
159
- destination = standardize_address(destination)
160
- request = build_rate_request(origin, destination, package, options)
161
- commit(:GetRates, request)
162
- end
163
-
164
- def create_shipment(origin, destination, package, line_items = [], options = {})
165
- origin = standardize_address(origin)
166
- destination = standardize_address(destination)
167
- request = build_create_indicium_request(origin, destination, package, line_items, options)
168
- commit(:CreateIndicium, request)
169
- end
170
-
171
- def find_tracking_info(shipment_id, options = {})
172
- request = build_track_shipment_request(shipment_id, options)
173
- commit(:TrackShipment, request)
174
- end
175
-
176
- def namespace
177
- NAMESPACE
178
- end
179
-
180
- def clear_authenticator
181
- @authenticator = nil
182
- end
183
-
184
- private
185
-
186
- def requirements
187
- REQUIRED_OPTIONS
188
- end
189
-
190
- def save_swsim_method(swsim_method)
191
- @last_swsim_method = swsim_method
192
- end
193
-
194
- def international?(address)
195
- ! (['US', nil] + US_POSSESSIONS).include?(address.country_code)
196
- end
197
-
198
- def standardize_address(address)
199
- if US_POSSESSIONS.include?(address.country_code)
200
- new_address = address.to_hash
201
- new_address[:province] = new_address[:country]
202
- new_address[:country] = 'US'
203
- Location.new(new_address)
204
- else
205
- address
206
- end
207
- end
208
-
209
- def domestic?(address)
210
- address.country_code(:alpha2) == 'US' || address.country_code(:alpha2).nil?
211
- end
212
-
213
- def authenticator
214
- get_authenticator unless @authenticator
215
- @authenticator
216
- end
217
-
218
- def renew_authenticator(request)
219
- old_authenticator = authenticator
220
- clear_authenticator
221
- request.sub(old_authenticator, authenticator)
222
- end
223
-
224
- def get_authenticator
225
- request = build_authenticate_user_request
226
- commit(:AuthenticateUser, request)
227
- end
228
-
229
- def build_header
230
- xml = Builder::XmlMarkup.new
231
- xml.instruct!
232
- xml.soap(:Envelope,
233
- 'xmlns:soap' => 'http://schemas.xmlsoap.org/soap/envelope/',
234
- 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
235
- 'xmlns:xsd' => 'http://www.w3.org/2001/XMLSchema',
236
- 'xmlns:tns' => 'http://stamps.com/xml/namespace/2014/01/swsim/swsimv34'
237
- ) do
238
- xml.soap :Body do
239
- yield(xml)
240
- end
241
- end
242
- end
243
-
244
- def build_authenticate_user_request
245
- build_header do |xml|
246
- xml.tns :AuthenticateUser do
247
- xml.tns :Credentials do
248
- xml.tns(:IntegrationID, @options[:integration_id])
249
- xml.tns(:Username, @options[:username])
250
- xml.tns(:Password, @options[:password])
251
- end
252
- end
253
- end
254
- end
255
-
256
- def build_get_account_info_request
257
- build_header do |xml|
258
- xml.tns :GetAccountInfo do
259
- xml.tns(:Authenticator, authenticator)
260
- end
261
- end
262
- end
263
-
264
- def build_purchase_postage_request(purchase_amount, control_total)
265
- build_header do |xml|
266
- xml.tns :PurchasePostage do
267
- xml.tns(:Authenticator, authenticator)
268
- xml.tns(:PurchaseAmount, purchase_amount)
269
- xml.tns(:ControlTotal, control_total)
270
- end
271
- end
272
- end
273
-
274
- def build_get_purchase_status(transaction_id)
275
- build_header do |xml|
276
- xml.tns :GetPurchaseStatus do
277
- xml.tns(:Authenticator, authenticator)
278
- xml.tns(:TransactionID, transaction_id)
279
- end
280
- end
281
- end
282
-
283
- def build_cleanse_address_request(address)
284
- build_header do |xml|
285
- xml.tns :CleanseAddress do
286
- xml.tns(:Authenticator, authenticator)
287
- add_address(xml, address)
288
- end
289
- end
290
- end
291
-
292
- def add_address(xml, address, object_type = :Address)
293
- xml.tns object_type do
294
- xml.tns(:FullName, address.name) unless address.name.blank?
295
- xml.tns(:Company, address.company) unless address.company.blank?
296
- xml.tns(:Address1, address.address1)
297
- xml.tns(:Address2, address.address2) unless address.address2.blank?
298
- xml.tns(:Address3, address.address3) unless address.address3.blank?
299
- xml.tns(:City, address.city) unless address.city.blank?
300
- if domestic?(address)
301
- xml.tns(:State, address.state) unless address.state.blank?
302
-
303
- zip = (address.postal_code || '').match(/^(\d{5})?-?(\d{4})?$/)
304
- xml.tns(:ZIPCode, zip[1]) unless zip[1].nil?
305
- xml.tns(:ZIPCodeAddOn, zip[2]) unless zip[2].nil?
306
- else
307
- xml.tns(:Province, address.province) unless address.province.blank?
308
- xml.tns(:PostalCode, address.postal_code) unless address.postal_code.blank?
309
- end
310
- xml.tns(:Country, address.country_code) unless address.country_code.blank?
311
- xml.tns(:PhoneNumber, address.phone) unless address.phone.blank?
312
- end
313
- end
314
-
315
- def build_rate_request(origin, destination, package, options)
316
- build_header do |xml|
317
- xml.tns :GetRates do
318
- xml.tns(:Authenticator, authenticator)
319
- add_rate(xml, origin, destination, package, options)
320
- end
321
- end
322
- end
323
-
324
- def add_rate(xml, origin, destination, package, options)
325
- value = package.value ? '%.2f' % (package.value.to_f / 100) : nil
326
- options[:insured_value] ||= value
327
- options[:declared_value] ||= value if international?(destination)
328
-
329
- xml.tns :Rate do
330
- xml.tns(:FromZIPCode, origin.postal_code) unless origin.postal_code.blank?
331
- xml.tns(:ToZIPCode, destination.postal_code) unless destination.postal_code.blank?
332
- xml.tns(:ToCountry, destination.country_code) unless destination.country_code.blank?
333
- xml.tns(:ServiceType, options[:service]) unless options[:service].blank?
334
- xml.tns(:PrintLayout, options[:print_layout]) unless options[:print_layout].blank?
335
- xml.tns(:WeightOz, [package.ounces, 1].max)
336
- xml.tns(:PackageType, options[:package_type] || 'Package')
337
- xml.tns(:Length, package.inches(:length)) if package.inches(:length)
338
- xml.tns(:Width, package.inches(:width)) if package.inches(:width)
339
- xml.tns(:Height, package.inches(:height)) if package.inches(:height)
340
- xml.tns(:ShipDate, options[:ship_date] || Date.today)
341
- xml.tns(:InsuredValue, options[:insured_value]) unless options[:insured_value].blank?
342
- xml.tns(:CODValue, options[:cod_value]) unless options[:cod_value].blank?
343
- xml.tns(:DeclaredValue, options[:declared_value]) unless options[:declared_value].blank?
344
-
345
- machinable = if package.options.has_key?(:machinable)
346
- package.options[:machinable] ? true : false
347
- else
348
- USPS.package_machinable?(package)
349
- end
350
-
351
- xml.tns(:NonMachinable, true) unless machinable
352
-
353
- xml.tns(:RectangularShaped, !package.cylinder?)
354
- xml.tns(:GEMNotes, options[:gem_notes]) unless options[:gem_notes].blank?
355
-
356
- add_ons = Array(options[:add_ons])
357
- unless add_ons.empty?
358
- xml.tns(:AddOns) do
359
- add_ons.each do |add_on|
360
- xml.tns(:AddOnV5) do
361
- xml.tns(:AddOnType, add_on)
362
- end
363
- end
364
- end
365
- end
366
-
367
- xml.tns(:ToState, destination.province) unless destination.province.blank?
368
- end
369
- end
370
-
371
- def build_create_indicium_request(origin, destination, package, line_items, options)
372
- build_header do |xml|
373
- xml.tns :CreateIndicium do
374
- xml.tns(:Authenticator, authenticator)
375
- xml.tns(:IntegratorTxID, options[:integrator_tx_id] || SecureRandom::uuid)
376
-
377
- add_rate(xml, origin, destination, package, options)
378
- add_address(xml, origin, :From)
379
- add_address(xml, destination, :To)
380
- add_customs(xml, line_items, options) unless options[:content_type].blank?
381
-
382
- xml.tns(:SampleOnly, options[:sample_only]) unless options[:sample_only].blank?
383
- xml.tns(:ImageType, options[:image_type]) unless options[:image_type].blank?
384
- xml.tns(:EltronPrinterDPIType, options[:label_resolution]) unless options[:label_resolution].blank?
385
- xml.tns(:memo, options[:memo]) unless options[:memo].blank?
386
- xml.tns(:deliveryNotification, options[:delivery_notification]) unless options[:delivery_notification].blank?
387
-
388
- add_shipment_notification(xml, options) unless options[:email].blank?
389
-
390
- xml.tns(:horizontalOffset, options[:horizontal_offset]) unless options[:horizontal_offest].blank?
391
- xml.tns(:verticalOffset, options[:vertical_offset]) unless options[:vertical_offest].blank?
392
- xml.tns(:printDensity, options[:print_density]) unless options[:print_density].blank?
393
- xml.tns(:rotationDegrees, options[:rotation]) unless options[:rotation].blank?
394
- xml.tns(:printMemo, options[:print_memo]) unless options[:print_memo].blank?
395
- xml.tns(:printInstructions, options[:print_instructions]) unless options[:print_instructions].blank?
396
- xml.tns(:ReturnImageData, options[:return_image_data]) unless options[:return_image_data].blank?
397
- xml.tns(:InternalTransactionNumber, options[:internal_transaction_number]) unless options[:internal_transaction_number].blank?
398
- xml.tns(:PaperSize, options[:paper_size]) unless options[:paper_size].blank?
399
-
400
- add_label_recipient_info(xml, options) unless options[:label_email_address].blank?
401
- end
402
- end
403
- end
404
-
405
- def add_shipment_notification(xml, options)
406
- xml.tns :ShipmentNotification do
407
- xml.tns(:Email, options[:email])
408
- xml.tns(:CCToAccountHolder, options[:cc_to_account_holder]) unless options[:cc_to_account_holder].blank?
409
- xml.tns(:UseCompanyNameInFromLine, options[:use_company_name_in_from_name]) unless options[:use_company_name_in_from_line].blank?
410
- xml.tns(:UseCompanyNameInSubject, options[:use_company_name_in_subject]) unless options[:use_company_name_in_subject].blank?
411
- end
412
- end
413
-
414
- def add_customs(xml, line_items, options)
415
- xml.tns :Customs do
416
- xml.tns(:ContentType, options[:content_type])
417
- xml.tns(:Comments, options[:comments]) unless options[:comments].blank?
418
- xml.tns(:LicenseNumber, options[:license_number]) unless options[:license_number].blank?
419
- xml.tns(:CertificateNumber, options[:certificate_number]) unless options[:certificate_number].blank?
420
- xml.tns(:InvoiceNumber, options[:invoice_number]) unless options[:invoice_number].blank?
421
- xml.tns(:OtherDescribe, options[:other_describe]) unless options[:other_describe].blank?
422
-
423
- xml.tns :CustomsLines do
424
- line_items.each do |customs_line|
425
- xml.tns :CustomsLine do
426
- xml.tns(:Description, customs_line.name)
427
- xml.tns(:Quantity, customs_line.quantity)
428
- xml.tns(:Value, '%.2f' % (customs_line.value.to_f / 100))
429
- xml.tns(:WeightOz, customs_line.ounces) unless customs_line.ounces.blank?
430
- xml.tns(:HSTariffNumber, customs_line.hs_code.tr('.', '')[0..5]) unless customs_line.hs_code.blank?
431
- xml.tns(:CountryOfOrigin, customs_line.options[:country]) unless customs_line.options[:country].blank?
432
- end
433
- end
434
- end
435
- end
436
- end
437
-
438
- def add_label_recipient_info(xml, options)
439
- xml.tns :LabelRecipientInfo do
440
- xml.tns(:EmailAddress, options[:label_email_address])
441
- xml.tns(:Name, options[:name]) unless options[:name].blank?
442
- xml.tns(:Note, options[:note]) unless options[:note].blank?
443
- xml.tns(:CopyToOriginator, options[:copy_to_originator]) unless options[:copy_to_originator].blank?
444
- end
445
- end
446
-
447
- def build_track_shipment_request(shipment_id, options)
448
- build_header do |xml|
449
- xml.tns :TrackShipment do
450
- xml.tns(:Authenticator, authenticator)
451
- xml.tns(options[:stamps_tx_id] ? :StampsTxID : :TrackingNumber, shipment_id)
452
- end
453
- end
454
- end
455
-
456
- def commit(swsim_method, request)
457
- save_request(request)
458
- save_swsim_method(swsim_method)
459
- parse(ssl_post(request_url, request, 'Content-Type' => 'text/xml', 'SOAPAction' => soap_action(swsim_method)))
460
- rescue ActiveMerchant::ResponseError => e
461
- parse(e.response.body)
462
- end
463
-
464
- def request_url
465
- test_mode? ? TEST_URL : LIVE_URL
466
- end
467
-
468
- def soap_action(method)
469
- [NAMESPACE, method].join('/')
470
- end
471
-
472
- def parse(xml)
473
- response_options = {}
474
- response_options[:xml] = xml
475
- response_options[:request] = last_request
476
- response_options[:test] = test_mode?
477
-
478
- document = REXML::Document.new(xml)
479
- child_element = document.get_elements('//soap:Body/*').first
480
- parse_method = 'parse_' + child_element.name.underscore
481
- if respond_to?(parse_method, true)
482
- send(parse_method, child_element, response_options)
483
- else
484
- Response.new(false, "Unknown response object #{child_element.name}", response_options)
485
- end
486
- end
487
-
488
- def parse_fault(fault, response_options)
489
- @authenticator = fault.get_text('detail/authenticator').value if fault.get_text('detail/authenticator')
490
-
491
- error_code = if fault.elements['detail/stamps_exception']
492
- fault.elements['detail/stamps_exception'].attributes['code']
493
- elsif fault.elements['detail/sdcerror']
494
- fault.elements['detail/sdcerror'].attributes['code']
495
- else
496
- nil
497
- end
498
-
499
- # Renew the Authenticator if it has expired and retry the request
500
- if error_code and error_code.downcase == '002b0202'
501
- request = renew_authenticator(last_request)
502
- commit(last_swsim_method, request)
503
- else
504
- raise ResponseError.new(fault.get_text('faultstring').to_s)
505
- end
506
- end
507
-
508
- def parse_authenticate_user_response(authenticate_user, response_options)
509
- parse_authenticator(authenticate_user)
510
- end
511
-
512
- def parse_authenticator(response)
513
- @authenticator = response.get_text('Authenticator').value
514
- end
515
-
516
- def parse_get_account_info_response(account_info_response, response_options)
517
- parse_authenticator(account_info_response)
518
-
519
- account_info = account_info_response.elements['AccountInfo']
520
- response_options[:customer_id] = account_info.get_text('CustomerID').to_s
521
- response_options[:meter_number] = account_info.get_text('MeterNumber').to_s
522
- response_options[:user_id] = account_info.get_text('UserID').to_s
523
- response_options[:max_postage_balance] = account_info.get_text('MaxPostageBalance').to_s
524
- response_options[:lpo_city] = account_info.get_text('LPOCity').to_s
525
- response_options[:lpo_state] = account_info.get_text('LPOState').to_s
526
- response_options[:lpo_zip] = account_info.get_text('LPOZip').to_s
527
-
528
- postage_balance_node = account_info.elements['PostageBalance']
529
- response_options[:available_postage] = postage_balance_node.get_text('AvailablePostage').to_s
530
- response_options[:control_total] = postage_balance_node.get_text('ControlTotal').to_s
531
-
532
- capabilities_node = account_info.elements['Capabilities']
533
- response_options[:can_print_shipping] = capabilities_node.get_text('CanPrintShipping').to_s == 'true'
534
- response_options[:can_use_cost_codes] = capabilities_node.get_text('CanUseCostCodes').to_s == 'true'
535
- response_options[:can_use_hidden_postage] = capabilities_node.get_text('CanUseHiddenPostage').to_s == 'true'
536
- response_options[:can_purchase_sdc_insurance] = capabilities_node.get_text('CanPurchaseSDCInsurance').to_s == 'true'
537
- response_options[:can_print_memo] = capabilities_node.get_text('CanPrintMemoOnShippingLabel').to_s == 'true'
538
- response_options[:can_print_international] = capabilities_node.get_text('CanPrintInternational').to_s == 'true'
539
- response_options[:can_purchase_postage] = capabilities_node.get_text('CanPurchasePostage').to_s == 'true'
540
- response_options[:can_edit_cost_codes] = capabilities_node.get_text('CanEditCostCodes').to_s == 'true'
541
- response_options[:must_use_cost_codes] = capabilities_node.get_text('MustUseCostCodes').to_s == 'true'
542
- response_options[:can_view_online_reports] = capabilities_node.get_text('CanViewOnlineReports').to_s == 'true'
543
- response_options[:per_print_limit] = capabilities_node.get_text('PerPrintLimit').to_s
544
-
545
- StampsAccountInfoResponse.new(true, '', {}, response_options)
546
- end
547
-
548
- def parse_purchase_postage_response(postage, response_options)
549
- parse_authenticator(postage)
550
-
551
- response_options[:purchase_status] = postage.get_text('PurchaseStatus').to_s
552
- response_options[:rejection_reason] = postage.get_text('RejectionReason').to_s if postage.get_text('RejectionReason')
553
- response_options[:transaction_id] = postage.get_text('TransactionID').to_s if postage.get_text('TransactionID')
554
-
555
- balance = postage.elements['PostageBalance']
556
- response_options[:available_postage] = balance.get_text('AvailablePostage').to_s
557
- response_options[:control_total] = balance.get_text('ControlTotal').to_s if balance.get_text('ControlTotal')
558
-
559
- StampsPurchasePostageResponse.new(true, '', {}, response_options)
560
- end
561
- alias_method :parse_get_purchase_status_response, :parse_purchase_postage_response
562
-
563
- def parse_cleanse_address_response(cleanse_address, response_options)
564
- parse_authenticator(cleanse_address)
565
-
566
- response_options[:address_match] = cleanse_address.get_text('AddressMatch').to_s == 'true'
567
- response_options[:city_state_zip_ok] = cleanse_address.get_text('CityStateZipOK').to_s == 'true'
568
-
569
- address = cleanse_address.elements['Address']
570
- response_options[:cleanse_hash] = address.get_text('CleanseHash').to_s if address.get_text('CleanseHash')
571
- response_options[:override_hash] = address.get_text('OverrideHash').to_s if address.get_text('OverrideHash')
572
-
573
- address_node = cleanse_address.elements['Address']
574
- indicator_node = cleanse_address.get_text('ResidentialDeliveryIndicatorType').to_s
575
- po_box_node = cleanse_address.get_text('IsPOBox').to_s
576
- response_options[:address] = parse_address(address_node, indicator_node, po_box_node)
577
-
578
- candidate_addresses = cleanse_address.get_elements('CandidateAddresses/Address')
579
- response_options[:candidate_addresses] = candidate_addresses.map do |candidate_address|
580
- parse_address(candidate_address)
581
- end
582
-
583
- StampsCleanseAddressResponse.new(true, '', {}, response_options)
584
- end
585
-
586
- def parse_address(address_node, residential_indicator_node = nil, po_box_node = nil)
587
- address = {}
588
-
589
- address[:name] = address_node.get_text('FullName').to_s if address_node.get_text('FullName')
590
- address[:company] = address_node.get_text('Company').to_s if address_node.get_text('Company')
591
- address[:address1] = address_node.get_text('Address1').to_s if address_node.get_text('Address1')
592
- address[:address2] = address_node.get_text('Address2').to_s if address_node.get_text('Address2')
593
- address[:address3] = address_node.get_text('Address3').to_s if address_node.get_text('Address3')
594
- address[:city] = address_node.get_text('City').to_s if address_node.get_text('City')
595
- address[:country] = address_node.get_text('Country').to_s if address_node.get_text('Country')
596
- address[:phone] = address_node.get_text('PhoneNumber').to_s if address_node.get_text('PhoneNumber')
597
-
598
- if address[:country] == 'US' || address[:country].nil?
599
- address[:state] = address_node.get_text('State').to_s if address_node.get_text('State')
600
-
601
- address[:postal_code] = address_node.get_text('ZIPCode').to_s if address_node.get_text('ZIPCode')
602
- address[:postal_code] += '-' + address_node.get_text('ZIPCodeAddOn').to_s if address_node.get_text('ZIPCodeAddOn')
603
- else
604
- address[:province] = address_node.get_text('Province').to_s if address_node.get_text('Province')
605
- address[:postal_code] = address_node.get_text('PostalCode').to_s if address_node.get_text('PostalCode')
606
- end
607
-
608
- address[:address_type] = if residential_indicator_node == 'Yes'
609
- 'residential'
610
- elsif residential_indicator_node == 'No'
611
- 'commercial'
612
- elsif po_box_node == 'true'
613
- 'po_box'
614
- else
615
- nil
616
- end
617
-
618
- Location.new(address)
619
- end
620
-
621
- def parse_get_rates_response(get_rates, response_options)
622
- parse_authenticator(get_rates)
623
-
624
- response_options[:estimates] = get_rates.get_elements('Rates/Rate').map do |rate|
625
- parse_rate(rate)
626
- end
627
-
628
- RateResponse.new(true, '', {}, response_options)
629
- end
630
-
631
- def parse_rate(rate)
632
- rate_options = {}
633
-
634
- origin = Location.new(zip: rate.get_text('FromZIPCode').to_s)
635
-
636
- location_values = {}
637
- location_values[:zip] = rate.get_text('ToZIPCode').to_s if rate.get_text('ToZIPCode')
638
- location_values[:country] = rate.get_text('ToCountry').to_s if rate.get_text('ToCountry')
639
- destination = Location.new(location_values)
640
-
641
- service_name = SERVICE_TYPES[rate.get_text('ServiceType').to_s]
642
-
643
- rate_options[:service_code] = rate.get_text('ServiceType').to_s
644
- rate_options[:currency] = 'USD'
645
- rate_options[:shipping_date] = Date.parse(rate.get_text('ShipDate').to_s)
646
-
647
- if delivery_days = rate.get_text('DeliverDays')
648
- delivery_days = delivery_days.to_s.split('-')
649
- rate_options[:delivery_range] = delivery_days.map { |day| rate_options[:shipping_date] + day.to_i.days }
650
- end
651
-
652
- rate_options[:total_price] = rate.get_text('Amount').to_s
653
-
654
- rate_options[:add_ons] = parse_add_ons(rate)
655
- rate_options[:packages] = parse_package(rate)
656
-
657
- add_ons = rate_options[:add_ons]
658
- if add_ons['SC-A-INS'] and add_ons['SC-A-INS'][:amount]
659
- rate_options[:insurance_price] = add_ons['SC-A-INS'][:amount]
660
- elsif add_ons['US-A-INS'] and add_ons['US-A-INS'][:amount]
661
- rate_options[:insurance_price] = add_ons['US-A-INS'][:amount]
662
- end
663
-
664
- StampsRateEstimate.new(origin, destination, @@name, service_name, rate_options)
665
- end
666
-
667
- def parse_add_ons(rate)
668
- add_ons = {}
669
- rate.get_elements('AddOns/AddOnV5').each do |add_on|
670
- add_on_type = add_on.get_text('AddOnType').to_s
671
-
672
- add_on_details = {}
673
- add_on_details[:missing_data] = add_on.get_text('MissingData').to_s if add_on.get_text('MissingData')
674
- add_on_details[:amount] = add_on.get_text('Amount').to_s if add_on.get_text('Amount')
675
-
676
- prohibited_with = add_on.get_elements('ProhibitedWithAnyOf/AddOnTypeV5').map(&:text)
677
- add_on_details[:prohibited_with] = prohibited_with unless prohibited_with.empty?
678
-
679
- add_ons[add_on_type] = add_on_details
680
- end
681
-
682
- add_ons
683
- end
684
-
685
- def parse_package(rate)
686
- weight = rate.get_text('WeightOz').to_s.to_f
687
-
688
- dimensions = %w(Length Width Height).map do |dim|
689
- rate.get_text(dim) ? rate.get_text(dim).to_s.to_f : nil
690
- end
691
- dimensions.compact!
692
-
693
- package_options = { units: :imperial }
694
-
695
- if value = rate.get_text('InsuredValue') || rate.get_text('DeclaredValue')
696
- package_options[:value] = value.to_s.to_f
697
- package_options[:currency] = 'USD'
698
- end
699
-
700
- Package.new(weight, dimensions, package_options)
701
- end
702
-
703
- def parse_create_indicium_response(indicium, response_options)
704
- parse_authenticator(indicium)
705
-
706
- response_options[:shipping_id] = indicium.get_text('IntegratorTxID').to_s
707
- response_options[:tracking_number] = indicium.get_text('TrackingNumber').to_s if indicium.get_text('TrackingNumber')
708
- response_options[:stamps_tx_id] = indicium.get_text('StampsTxID').to_s
709
- response_options[:label_url] = indicium.get_text('URL').to_s if indicium.get_text('URL')
710
- response_options[:available_postage] = indicium.get_text('PostageBalance/AvailablePostage').to_s
711
- response_options[:control_total] = indicium.get_text('PostageBalance/ControlTotal').to_s
712
- response_options[:image_data] = Base64.decode64(indicium.get_text('ImageData/base64Binary').to_s) if indicium.get_text('ImageData/base64Binary')
713
- response_options[:rate] = parse_rate(indicium.elements['Rate'])
714
-
715
- StampsShippingResponse.new(true, '', {}, response_options)
716
- end
717
-
718
- def parse_track_shipment_response(track_shipment, response_options)
719
- parse_authenticator(track_shipment)
720
-
721
- response_options[:carrier] = @@name
722
-
723
- shipment_events = track_shipment.get_elements('TrackingEvents/TrackingEvent').map do |event|
724
- unless response_options[:status]
725
- response_options[:status_code] = event.get_text('TrackingEventType').to_s
726
- response_options[:status] = response_options[:status_code].underscore.to_sym
727
- end
728
-
729
- response_options[:delivery_signature] = event.get_text('SignedBy').to_s if event.get_text('SignedBy')
730
-
731
- description = event.get_text('Event').to_s
732
-
733
- timestamp = event.get_text('Timestamp').to_s
734
- date, time = timestamp.split('T')
735
- year, month, day = date.split('-')
736
- hour, minute, second = time.split(':')
737
- zoneless_time = Time.utc(year, month, day, hour, minute, second)
738
-
739
- location = Location.new(
740
- city: event.get_text('City').to_s,
741
- state: event.get_text('State').to_s,
742
- zip: event.get_text('Zip').to_s,
743
- country: event.get_text('Country').to_s
744
- )
745
-
746
- ShipmentEvent.new(description, zoneless_time, location)
747
- end
748
-
749
- response_options[:shipment_events] = shipment_events.sort_by(&:time)
750
- response_options[:delivered] = response_options[:status] == :delivered
751
-
752
- TrackingResponse.new(true, '', {}, response_options)
753
- end
754
- end
755
-
756
- class StampsAccountInfoResponse < Response
757
- attr_reader :customer_id, :meter_number, :user_id, :available_postage, :control_total, :max_postage_balance, :lpo
758
- attr_reader :can_print_shipping, :can_use_cost_codes, :can_use_hidden_postage, :can_purchase_sdc_insurance, :can_print_international
759
- attr_reader :can_print_memo, :can_purchase_postage, :can_edit_cost_codes, :must_use_cost_codes, :can_view_online_reports, :per_print_limit
760
-
761
- alias_method :can_print_shipping?, :can_print_shipping
762
- alias_method :can_use_cost_codes?, :can_use_cost_codes
763
- alias_method :can_use_hidden_postage?, :can_use_hidden_postage
764
- alias_method :can_purchase_sdc_insurance?, :can_purchase_sdc_insurance
765
- alias_method :can_print_international?, :can_print_international
766
- alias_method :can_print_memo?, :can_print_memo
767
- alias_method :can_purchase_postage?, :can_purchase_postage
768
- alias_method :can_edit_cost_codes?, :can_edit_cost_codes
769
- alias_method :must_use_cost_codes?, :must_use_cost_codes
770
- alias_method :can_view_online_reports?, :can_view_online_reports
771
-
772
- def initialize(success, message, params = {}, options = {})
773
- super
774
- @customer_id = options[:customer_id]
775
- @meter_number = options[:meter_number]
776
- @user_id = options[:user_id]
777
- @available_postage = options[:available_postage]
778
- @control_total = options[:control_total]
779
- @max_postage_balance = options[:max_postage_balance]
780
- @lpo = Location.new(
781
- city: options[:lpo_city],
782
- state: options[:lpo_state],
783
- zip: options[:lpo_zip]
784
- )
785
- @can_print_shipping = options[:can_print_shipping]
786
- @can_use_cost_codes = options[:can_use_cost_codes]
787
- @can_use_hidden_postage = options[:can_use_hidden_postage]
788
- @can_purchase_sdc_insurance = options[:can_purchase_sdc_insurance]
789
- @can_print_memo = options[:can_print_memo]
790
- @can_print_international = options[:can_print_international]
791
- @can_purchase_postage = options[:can_purchase_postage]
792
- @can_edit_cost_codes = options[:can_edit_cost_codes]
793
- @must_use_cost_codes = options[:must_use_cost_codes]
794
- @can_view_online_reports = options[:can_view_online_reports]
795
- @per_print_limit = options[:per_print_limit]
796
- end
797
- end
798
-
799
- class StampsPurchasePostageResponse < Response
800
- attr_reader :purchase_status, :transaction_id, :available_postage, :control_total, :rejection_reason
801
-
802
- def initialize(success, message, params = {}, options = {})
803
- super
804
- @purchase_status = options[:purchase_status]
805
- @transaction_id = options[:transaction_id]
806
- @available_postage = options[:available_postage]
807
- @control_total = options[:control_total]
808
- @rejection_reason = options[:rejection_reason]
809
- end
810
- end
811
-
812
- class StampsCleanseAddressResponse < Response
813
- attr_reader :address, :address_match, :city_state_zip_ok, :candidate_addresses, :cleanse_hash, :override_hash
814
-
815
- alias_method :address_match?, :address_match
816
- alias_method :city_state_zip_ok?, :city_state_zip_ok
817
-
818
- def initialize(success, message, params = {}, options = {})
819
- super
820
- @address = options[:address]
821
- @address_match = options[:address_match]
822
- @city_state_zip_ok = options[:city_state_zip_ok]
823
- @candidate_addresses = options[:candidate_addresses]
824
- @cleanse_hash = options[:cleanse_hash]
825
- @override_hash = options[:override_hash]
826
- end
827
- end
828
-
829
- class StampsRateEstimate < RateEstimate
830
- attr_reader :add_ons
831
-
832
- def initialize(origin, destination, carrier, service_name, options = {})
833
- super
834
- @add_ons = options[:add_ons]
835
- end
836
-
837
- def available_add_ons
838
- add_ons.keys
839
- end
840
- end
841
-
842
- class StampsShippingResponse < ShippingResponse
843
- include PostsData
844
-
845
- self.ssl_version = :SSLv3
846
-
847
- attr_reader :rate, :stamps_tx_id, :label_url, :available_postage, :control_total
848
-
849
- def initialize(success, message, params = {}, options = {})
850
- super
851
- @rate = options[:rate]
852
- @stamps_tx_id = options[:stamps_tx_id]
853
- @label_url = options[:label_url]
854
- @image_data = options[:image_data]
855
- @available_postage = options[:available_postage]
856
- @control_total = options[:control_total]
857
- end
858
-
859
- def image
860
- @image_data ||= ssl_get(label_url)
861
- end
862
- end
863
- end
864
- end