active_shipping 0.12.6 → 1.0.0.pre1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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