pbshipping 1.0.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.
@@ -0,0 +1,21 @@
1
+ #
2
+ # Copyright 2016 Pitney Bowes Inc.
3
+ #
4
+ # Licensed under the MIT License (the "License"); you may not use this file
5
+ # except in compliance with the License. You may obtain a copy of the License
6
+ # in the LICENSE file or at
7
+ # https://opensource.org/licenses/MIT
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ #
15
+ # File: ts_all_tests.rb
16
+ # Description: running all available unit tests
17
+ #
18
+
19
+ require "test/unit"
20
+ Dir["./tc_*.rb"].each {|file| require file }
21
+
@@ -0,0 +1,464 @@
1
+ #
2
+ # Copyright 2016 Pitney Bowes Inc.
3
+ #
4
+ # Licensed under the MIT License (the "License"); you may not use this file
5
+ # except in compliance with the License. You may obtain a copy of the License
6
+ # in the LICENSE file or at
7
+ # https://opensource.org/licenses/MIT
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ #
15
+ # File: tutorial.rb
16
+ # Description: a tutorial example exercising the shipping apis
17
+ #
18
+
19
+ # Tutorial
20
+ require 'optparse'
21
+ require "json"
22
+ require 'pbshipping'
23
+
24
+ module PBShippingTutorial
25
+
26
+ @api_key = nil
27
+ @api_secret = nil
28
+ @dev_id = nil
29
+ @merchant_email = nil
30
+ @merchant = nil
31
+ @from_addr = nil
32
+ @to_addr = nil
33
+ @shipment = nil
34
+ @shipment_orig_tx_id = nil
35
+ @tracking = nil
36
+ @manifest = nil
37
+ @manifest_orig_tx_id = nil
38
+ @auth_obj = nil
39
+
40
+ @my_bulk_merchant_addr = {
41
+ :addressLines => ["27 Waterview Drive"],
42
+ :cityTown => "Shelton",
43
+ :stateProvince => "Connecticut",
44
+ :postalCode => "06484",
45
+ :countryCode => "US",
46
+ :company => "Pitney Bowes",
47
+ :name => "John Doe",
48
+ :email => "dummy@pbshipping.com",
49
+ :phone => "203-792-1600",
50
+ :residential => false
51
+ }
52
+
53
+ @my_origin_addr = {
54
+ :addressLines => ["37 Executive Drive"],
55
+ :cityTown => "Danbury",
56
+ :stateProvince => "Connecticut",
57
+ :postalCode => "06810",
58
+ :countryCode => "US"
59
+ }
60
+
61
+ @my_dest_addr = {
62
+ :addressLines => ["27 Waterview Drive"],
63
+ :cityTown => "Shelton",
64
+ :stateProvince => "Connecticut",
65
+ :postalCode => "06484",
66
+ :countryCode => "US"
67
+ }
68
+
69
+ @my_parcel = {
70
+ :weight => {
71
+ :unitOfMeasurement => "OZ",
72
+ :weight => 1
73
+ },
74
+ :dimension => {
75
+ :unitOfMeasurement => "IN",
76
+ :length => 6,
77
+ :width => 0.25,
78
+ :height => 4,
79
+ :irregularParcelGirth => 0.002
80
+ }
81
+ }
82
+
83
+ @my_rate_request_carrier_usps = {
84
+ :carrier => "usps",
85
+ :serviceId => "PM",
86
+ :parcelType => "PKG",
87
+ :specialServices => [
88
+ {
89
+ :specialServiceId => "Ins",
90
+ :inputParameters => [
91
+ {
92
+ :name => "INPUT_VALUE",
93
+ :value => "50"
94
+ }
95
+ ]
96
+ },
97
+ {
98
+ :specialServiceId => "DelCon",
99
+ :inputParameters => [
100
+ {
101
+ :name => "INPUT_VALUE",
102
+ :value => "0"
103
+ }
104
+ ]
105
+ }
106
+ ],
107
+ :inductionPostalCode => "06810"
108
+ }
109
+
110
+ @my_shipment_document = {
111
+ :type => "SHIPPING_LABEL",
112
+ :contentType => "URL",
113
+ :size => "DOC_8X11",
114
+ :fileFormat => "PDF",
115
+ :printDialogOption => "NO_PRINT_DIALOG"
116
+ }
117
+
118
+ # use the current timestamp to generate a transaction id
119
+ def self.get_pb_tx_id()
120
+
121
+ Time.now().to_i.to_s
122
+ end
123
+
124
+ # obtain authentication, developer, and merchant infomration from
125
+ # command line
126
+ def self.initialize_info()
127
+
128
+ options = {}
129
+
130
+ # try environment variables ...
131
+ if ENV["PBSHIPPING_KEY"] != nil
132
+ options[:key] = ENV["PBSHIPPING_KEY"]
133
+ end
134
+ if ENV["PBSHIPPING_SECRET"] != nil
135
+ options[:secret] = ENV["PBSHIPPING_SECRET"]
136
+ end
137
+ if ENV["PBSHIPPING_DEVID"] != nil
138
+ options[:devid] = ENV["PBSHIPPING_DEVID"]
139
+ end
140
+ if ENV["PBSHIPPING_MERCHANT"] != nil
141
+ options[:merchant] = ENV["PBSHIPPING_MERCHANT"]
142
+ end
143
+
144
+ optparse = OptionParser.new do |opts|
145
+ opts.banner = "Usage: tutorial.rb [options]"
146
+ # command line arguments overwrite environment variables
147
+ opts.on('-h', '--help', 'Display help') do
148
+ puts opts
149
+ exit
150
+ end
151
+ opts.on('-k', '--key API_KEY', 'API key for authentication') do |api_key|
152
+ options[:key] = api_key
153
+ end
154
+ opts.on('-s', '--secret API_SECRET', 'API secret for authentication') do |api_secret|
155
+ options[:secret] = api_secret
156
+ end
157
+ opts.on('-d', '--devid DEVELOPER_ID', 'Pitney Bowes Developer ID') do |dev_id|
158
+ options[:devid] = dev_id
159
+ end
160
+ opts.on('-m', '--merchant MERCHANT_EMAIL', 'Merchant email') do |merchant_email|
161
+ options[:merchant] = merchant_email
162
+ end
163
+ end
164
+
165
+ begin
166
+ optparse.parse!
167
+ mandatory = [:key, :secret, :devid, :merchant]
168
+ missing = mandatory.select{ |param| options[param].nil? }
169
+ unless missing.empty?
170
+ puts "Missing options: #{missing.join(', ')}"
171
+ puts optparse
172
+ exit
173
+ end
174
+ @api_key = options[:key]
175
+ @api_secret = options[:secret]
176
+ @dev_id = options[:devid]
177
+ @merchant_email = options[:merchant]
178
+ rescue OptionParser::InvalidOption, OptionParser::MissingArgument
179
+ puts $!.to_s
180
+ puts optparse
181
+ exit
182
+ end
183
+ end
184
+
185
+ # choose sandbox or production pitney bowes shipping api server
186
+ def self.choose_environment()
187
+
188
+ puts "Choose the sandbox environment ..."
189
+ PBShipping::configuration[:is_production] = false
190
+ end
191
+
192
+ # authenticate and obtain the authentication object for subsequent use
193
+ # underlying API: POST /oauth/token
194
+ def self.authenticate()
195
+
196
+ puts 'Authenticating ...'
197
+ @auth_obj = PBShipping::AuthenticationToken.new(@api_key, @api_secret)
198
+ end
199
+
200
+ # return the list of supported countries
201
+ # underlying API: GET /countries
202
+ def self.check_carrier_supported_countries()
203
+
204
+ puts "Querying for supported countries of USPS carrier ..."
205
+ country_list = PBShipping::Carrier.getCountriesForCarrier(@auth_obj, "usps", "US")
206
+ n = country_list.length()
207
+ puts " number of supported countries is " + n.to_s
208
+ puts " one example is " + country_list[n/3].countryName
209
+ end
210
+
211
+ # managing merchant account under individual account mode
212
+ # underlying API: GET /developers/{developerId}/merchants/emails/{emailId}/
213
+ # GET /ledger/accounts/{accountNumber}/balance
214
+ def self.manage_individual_mode_merchant()
215
+
216
+ # querying for merchant information
217
+ puts "Managing merchant (individual account mode) ..."
218
+ @developer = PBShipping::Developer.new( { :developerId => @dev_id } )
219
+ @merchant = @developer.registerMerchantIndividualAccount(
220
+ @auth_obj, @merchant_email)
221
+ merchant_account_number = @merchant.paymentAccountNumber
222
+
223
+ # querying for merchant account balance
224
+ balance = PBShipping::Account.getBalanceByAccountNumber(
225
+ @auth_obj, merchant_account_number)
226
+ puts " merchant full name is " + @merchant.fullName
227
+ puts " shipper id is " + @merchant.postalReportingNumber
228
+ puts " payment account number is " + merchant_account_number
229
+ puts " current balance is " + balance.currencyCode + " " + \
230
+ balance.balance.to_s
231
+ end
232
+
233
+ # managing merchant account under bulk account mode
234
+ # underlying API: POST /developers/{developerId}/merchants/registration
235
+ def self.manage_bulk_mode_merchant()
236
+
237
+ puts "Managing merchant (bulk account mode) ..."
238
+ @developer = PBShipping::Developer.new( { :developerId => @dev_id } )
239
+ merchant_addr = PBShipping::Address.new(@my_bulk_merchant_addr)
240
+ merchant = @developer.registerMerchantBulkAccount(@auth_obj, merchant_addr)
241
+ puts merchant
242
+ end
243
+
244
+ # verifying addresses
245
+ # underlying API: POST /addresses/verify
246
+ def self.verify_addresses()
247
+
248
+ puts "Verifying origin and destination addresses ... "
249
+ @from_addr = PBShipping::Address.new(@my_origin_addr)
250
+ @from_addr.verify(@auth_obj, false)
251
+ if @from_addr.status.downcase == "validated_changed"
252
+ puts " origin address cleansed, addressLine is " + \
253
+ @from_addr.addressLines[0]
254
+ end
255
+
256
+ @to_addr = PBShipping::Address.new(@my_dest_addr)
257
+ @to_addr.verify(@auth_obj, false)
258
+ if @to_addr.status.downcase == "validated_changed"
259
+ puts " destination address cleansed, addressLine is " + \
260
+ @to_addr.addressLines[0]
261
+ end
262
+ end
263
+
264
+ # querying rates to prepare a shipment
265
+ # underlying API: POST /rates
266
+ def self.prepare_shipment()
267
+
268
+ puts "Preparing shipment and checking for shipment rates ..."
269
+ rates = [ PBShipping::Rate.new(@my_rate_request_carrier_usps) ]
270
+ parcel = PBShipping::Parcel.new(@my_parcel)
271
+ documents = [ PBShipping::Document.new(@my_shipment_document)]
272
+ shipmentOptions = [
273
+ PBShipping::ShipmentOptions.new({
274
+ :name => "SHIPPER_ID",
275
+ :value => @merchant.postalReportingNumber
276
+ }),
277
+ PBShipping::ShipmentOptions.new({
278
+ :name => "ADD_TO_MANIFEST",
279
+ :value => true
280
+ })
281
+ ]
282
+ @shipment = PBShipping::Shipment.new({
283
+ :fromAddress => @from_addr,
284
+ :toAddress => @to_addr,
285
+ :parcel => parcel,
286
+ :rates => rates,
287
+ :documents => documents,
288
+ :shipmentOptions => shipmentOptions
289
+ })
290
+ @shipment.rates = @shipment.getRates(@auth_obj, get_pb_tx_id(), true)
291
+ puts " total carrier charge: " + @shipment.rates[0][:totalCarrierCharge].to_s
292
+ end
293
+
294
+ # submit a shipment creation request and purchase a shipment label
295
+ # underlying API: POST /shipments
296
+ def self.create_and_purchase_shipment()
297
+
298
+ puts "Creating shipment and purchasing label ..."
299
+ @shipment_orig_tx_id = get_pb_tx_id()
300
+ @shipment.createAndPurchase(@auth_obj, @shipment_orig_tx_id, true)
301
+
302
+ puts " parcel tracking number is " + @shipment.parcelTrackingNumber
303
+ for doc in @shipment.documents
304
+ puts " document type is " + doc[:type]
305
+ if doc[:contentType] == "URL" && doc.key?(:contents)
306
+ puts " document URL is " + doc[:contents]
307
+ end
308
+ end
309
+ end
310
+
311
+ # reprint a shipment label
312
+ # underlying API: GET /shipments/{shipmentId}
313
+ def self.reprint_shipment()
314
+
315
+ puts "Reprinting label ..."
316
+ @shipment.reprintLabel(@auth_obj)
317
+ for doc in @shipment.documents
318
+ if doc[:contentType] == "URL" && doc.key?(:contents)
319
+ puts " document URL is " + doc[:contents]
320
+ end
321
+ end
322
+ end
323
+
324
+ # retry a shipment purchase request
325
+ # underlying API: GET /shipments?originalTransactionId
326
+ def self.retry_shipment()
327
+
328
+ puts "Retrying shipment order ..."
329
+ @shipment.retry(@auth_obj, get_pb_tx_id(), @shipment_orig_tx_id)
330
+ end
331
+
332
+ # submit a shipment cancellation request
333
+ # underlying API: DELETE /shipments/{shipmentId}
334
+ def self.cancel_shipment()
335
+
336
+ puts "Canceling shipment order ..."
337
+ cancel_result = @shipment.cancel(@auth_obj, @shipment_orig_tx_id,
338
+ @shipment.rates[0].carrier)
339
+ puts " status: " + cancel_result["status"]
340
+ end
341
+
342
+ # create a manifest
343
+ # underlying API: POST /manifests
344
+ def self.create_manifest()
345
+
346
+ puts "Creating manifest ..."
347
+ @manifest = PBShipping::Manifest.new( {
348
+ :carrier => @tracking.carrier,
349
+ :submissionDate => Time.now.utc.strftime("%Y-%m-%d"),
350
+ :parcelTrackingNumbers => [@shipment.parcelTrackingNumber],
351
+ :fromAddress => @shipment.fromAddress
352
+ } )
353
+ @manifest_orig_tx_id = get_pb_tx_id()
354
+ @manifest.create(@auth_obj, @manifest_orig_tx_id)
355
+ puts " manifest tracking number is " + @manifest.manifestTrackingNumber
356
+ puts " manifest id is " + @manifest.manifestId
357
+ end
358
+
359
+ # reputs a manifest
360
+ # underlying API: GET /manifests/{manifestId}
361
+ def self.reprint_manifest()
362
+
363
+ puts "Repringing manifest ..."
364
+
365
+ @manifest.reprint(@auth_obj)
366
+ puts " reprinted manifestId is " + @manifest.manifestId
367
+ end
368
+
369
+ # retry a mainfest
370
+ # Underly API: GET /manifests
371
+ def self.retry_manifest()
372
+
373
+ puts "Retrying manifest request ..."
374
+ @manifest.retry(@auth_obj, get_pb_tx_id(), @manifest_orig_tx_id)
375
+ puts " manifest id is " + @manifest.manifestId
376
+ end
377
+
378
+ # get tracking information
379
+ # Underlying API: GET /tracking/{trackingNumber}
380
+ def self.get_tracking_update()
381
+
382
+ puts "Get tracking status ..."
383
+ @tracking = PBShipping::Tracking.new( { :trackingNumber => @shipment.parcelTrackingNumber } )
384
+ begin
385
+ @tracking.updateStatus(@auth_obj)
386
+ rescue => e
387
+ case e
388
+ when PBShipping::ApiError
389
+ if PBShipping::configuration[:is_production] == false
390
+ puts " no tracking information in sandbox environment"
391
+ return
392
+ end
393
+ raise e
394
+ end
395
+ end
396
+ puts " status = " + _tracking.status
397
+ end
398
+
399
+ # querying for a transaction report
400
+ # Underlying API: GET /
401
+ def self.get_transaction_report()
402
+
403
+ puts "Retrieving transaction report ..."
404
+ params = {}
405
+ params[:merchantId] = @merchant.postalReportingNumber
406
+ report = PBShipping::Developer.new({:developerId => @dev_id}).getTransactionReport(
407
+ @auth_obj, params)
408
+ puts " First few entries ..."
409
+ i = 0
410
+ for next_row in report.content
411
+ txn = PBShipping::TransactionDetails.new(next_row)
412
+ txn_detail = " id: " + txn.transactionId
413
+ txn_detail += " type: " + txn.transactionType
414
+ puts txn_detail
415
+ end
416
+ end
417
+
418
+ # navgiate through different steps in shipping workflow
419
+ def self.shipping_workflow()
420
+
421
+ begin
422
+
423
+ initialize_info()
424
+
425
+ choose_environment()
426
+
427
+ authenticate()
428
+
429
+ check_carrier_supported_countries()
430
+
431
+ # choose appropriate calls for individual or bulk account mode
432
+ manage_individual_mode_merchant()
433
+ #manage_bulk_mode_merchant()
434
+
435
+ verify_addresses()
436
+
437
+ prepare_shipment()
438
+ create_and_purchase_shipment()
439
+ reprint_shipment()
440
+ retry_shipment()
441
+
442
+ get_tracking_update()
443
+
444
+ create_manifest()
445
+ reprint_manifest()
446
+ retry_manifest()
447
+
448
+ get_transaction_report()
449
+
450
+ cancel_shipment()
451
+
452
+ rescue => e
453
+ if e.is_a?(PBShipping::ApiError) && e.error_info != nil
454
+ puts e.message
455
+ puts e.error_info
456
+ else
457
+ puts "hit an exception"
458
+ puts e
459
+ end
460
+ end
461
+ end
462
+ end
463
+
464
+ PBShippingTutorial::shipping_workflow()