File Coverage

lib/WebService/Shippo.pm
Criterion Covered Total %
statement 26 26 100.0
branch 2 4 50.0
condition 1 3 33.3
subroutine 8 8 100.0
pod n/a
total 37 41 90.2


line stmt bran cond sub pod time code
1 7     7   319273 use strict;
  7         16  
  7         169  
2 7     7   33 use warnings;
  7         11  
  7         430  
3              
4             package WebService::Shippo;
5             # ABSTRACT: A Shippo Perl API wrapper
6             our $VERSION = '0.0.20';
7             require WebService::Shippo::Entities;
8 7     7   37 use boolean ':all';
  7         12  
  7         43  
9 7     7   6296 use Params::Callbacks ( 'callbacks', 'callback' );
  7         7227692  
  7         556  
10 7     7   60 use base ( 'Exporter' );
  7         14  
  7         1385  
11              
12             our @EXPORT = qw(
13             true
14             false
15             boolean
16             callback
17             );
18              
19             sub import
20             {
21 7     7   79 my ( $class ) = @_;
22 7         34 WebService::Shippo::Config->config;
23             WebService::Shippo::Resource->api_credentials(
24             @ENV{ 'SHIPPO_USER', 'SHIPPO_PASS' } )
25 7 50 33     44 if $ENV{SHIPPO_USER} && !$ENV{SHIPPO_TOKEN};
26             WebService::Shippo::Resource->api_key( $ENV{SHIPPO_TOKEN} )
27 7 50       39 if $ENV{SHIPPO_TOKEN};
28 7         23955 goto &Exporter::import;
29             }
30              
31             BEGIN {
32 7     7   38 no warnings 'once';
  7         14  
  7         512  
33 7     7   25 *api_key = *WebService::Shippo::Resource::api_key;
34 7         14 *api_credentials = *WebService::Shippo::Resource::api_credentials;
35 7         990 *Shippo:: = *WebService::Shippo::;
36             }
37              
38             1;
39              
40             =pod
41              
42             =encoding utf8
43              
44             =head1 UNDER CONSTRUCTION
45              
46             B
47             documented and should, therefore, be considered a work in progress.>
48              
49             =head1 NAME
50              
51             WebService::Shippo - Shippo API Client
52              
53             =head1 VERSION
54              
55             version 0.0.20
56              
57             =head1 SYNOPIS
58              
59             B: though scripts and modules must always C
60             to import the client software, the C portion of that package
61             namespace may be dropped when subsequently referring to the main package
62             or any of its resource classes. For example, C
63             and C refer to the same class.
64              
65             To compel the developer to continue using the C prefix does
66             seem like an unreasonable form of torture, besides which, it probably
67             doesn't leave much scope for indenting code as some class names would be
68             very long. Use it, or don't use it. It's entirely up to you.
69              
70             use strict;
71             use LWP::UserAgent;
72             use WebService::Shippo;
73            
74             # If you aren't using a config file or the environment (SHIPPO_TOKEN=...)
75             # to supply your API key, you can do so here:
76            
77             Shippo->api_key('PASTE YOUR AUTH TOKEN HERE')
78             unless Shippo->api_key;
79            
80             # Complete example illustrating the the process of Shipment creation
81             # through to label generation.
82             #
83             # Create a Shipment object:
84            
85             my $shipment = Shippo::Shipment->create(
86             object_purpose => 'PURCHASE',
87             address_from => {
88             object_purpose => 'PURCHASE',
89             name => 'Shawn Ippotle',
90             company => 'Shippo',
91             street1 => '215 Clayton St.',
92             city => 'San Francisco',
93             state => 'CA',
94             zip => '94117',
95             country => 'US',
96             phone => '+1 555 341 9393',
97             email => 'shippotle@goshippo.com'
98             },
99             address_to => {
100             object_purpose => 'PURCHASE',
101             name => 'Mr Hippo',
102             company => '',
103             street1 => 'Broadway 1',
104             street2 => '',
105             city => 'New York',
106             state => 'NY',
107             zip => '10007',
108             country => 'US',
109             phone => '+1 555 341 9393',
110             email => 'mrhippo@goshippo.com'
111             },
112             parcel => {
113             length => '5',
114             width => '5',
115             height => '5',
116             distance_unit => 'in',
117             weight => '2',
118             mass_unit => 'lb'
119             }
120             );
121            
122             # Retrieve shipping rates and select preferred rate:
123            
124             my $rates = Shippo::Shipment->get_shipping_rates( $shipment->object_id );
125             my $preferred_rate = $rates->item(2);
126            
127             # Purchase label at the preferred rate:
128            
129             my $transaction = Shippo::Transaction->create(
130             rate => $preferred_rate->object_id,
131             label_file_type => 'PNG',
132             );
133            
134             # Get the shipping label:
135            
136             my $label_url = Shippo::Transaction->get_shipping_label( $transaction->object_id );
137             my $browser = LWP::UserAgent->new;
138             $browser->get( $transaction->label_url, ':content_file' => './sample.png' );
139            
140             # Print the transaction object...
141            
142             print "Transaction:\n", $transaction;
143              
144             --[content dumped to console]--
145             Transaction:
146             {
147             "commercial_invoice_url" : null,
148             "customs_note" : "",
149             "label_url" : "https://shippo-delivery-east.s3.amazonaws.com/da2e68fe85f94a9ebca458d9f9d
150             2446b.PNG?Signature=BjD2JMQt0ATd5jUWAKm%2B6FHcBPM%3D&Expires=1477323662&AWSAccessKeyId=AKIA
151             JGLCC5MYLLWIG42A",
152             "messages" : [],
153             "metadata" : "",
154             "notification_email_from" : false,
155             "notification_email_other" : "",
156             "notification_email_to" : false,
157             "object_created" : "2015-10-25T15:41:01.182Z",
158             "object_id" : "da2e68fe85f94a9ebca458d9f9d2446b",
159             "object_owner" : "******@*********.***",
160             "object_state" : "VALID",
161             "object_status" : "SUCCESS",
162             "object_updated" : "2015-10-25T15:41:02.494Z",
163             "order" : null,
164             "pickup_date" : null,
165             "rate" : "3c76e81733d7417b9a801ce957f4219d",
166             "submission_note" : "",
167             "tracking_history" : [],
168             "tracking_number" : "9499907123456123456781",
169             "tracking_status" : {
170             "object_created" : "2015-10-25T15:41:02.451Z",
171             "object_id" : "02ce6dbd6d5a48cfb764fdeb0cb6e404",
172             "object_updated" : "2015-10-25T15:41:02.451Z",
173             "status" : "UNKNOWN",
174             "status_date" : null,
175             "status_details" : ""
176             },
177             "tracking_url_provider" : "https://tools.usps.com/go/TrackConfirmAction_input?origTrackN
178             um=9499907123456123456781",
179             "was_test" : true
180             }
181             --[end of content]--
182              
183             The sample code in this synopsis produced the following label:
184              
185             =over 2
186              
187             =item * L
188              
189             =back
190              
191             =head1 DESCRIPTION
192              
193             Shippo connects you with multiple shipping providers (USPS, UPS and Fedex,
194             for example) through one interface, offering you great discounts on a
195             selection of shipping rates. You can sign-up for an account at
196             L.
197              
198             The Shippo API can be used to automate and customize shipping capabilities
199             for your e-commerce store or marketplace, enabling you to retrieve shipping
200             rates, create and purchase shipping labels, track packages, and much more.
201              
202             Though Shippo I offer official API clients for a bevy of major languages,
203             the venerable Perl 5 was not included in that list. This community offering
204             attempts to correct that omission ;-)
205              
206             =head2 API Resources
207              
208             Access to all Shippo API resources is via URLs relative to the same encrypted
209             API endpoint (https://api.goshippo.com/v1/). The client provides object and
210             collection classes to help with the nitty-gritty of dealing with those
211             resources:
212              
213             =over 2
214              
215             =item * L
216              
217             =item * L
218              
219             =item * L
220              
221             =item * L
222              
223             =item * L
224              
225             =item * L
226              
227             =item * L
228              
229             =item * L
230              
231             =item * L
232              
233             =item * L
234              
235             =back
236              
237             Each item class has a related collection class with a similar name I
238             plural form>. The rationale behind this is that the Shippo API can be used
239             to retrieve single objects with the C method, and collections of
240             objects with the C method, and different behaviours may be applied
241             to collections, which is why both forms exist.
242              
243             =head2 Request & Response Data
244              
245             The Perl client ensures that requests are properly encoded and passed to the
246             correct API endpoints using appropriate HTTP methods. There is documentation
247             for each API resource, containing more details on the values accepted by and
248             returned for a given resource (see L).
249              
250             All API requests return responses encoded as JSON strings, which the client
251             converts into Perl blessed object references of the correct type. As a rule,
252             any resource attribute documented in the API specification will have an
253             accessor of the same same in a Perl instance of that object.
254              
255             =head2 REST & Disposable Objects
256              
257             The Shippo API is built with simplicity and RESTful principles in mind:
258             B requests are used to create objects, B requests to fetch and
259             list objects, and B requests to update objects. The Perl client provides
260             C, C, C and C methods for use with resource
261             objects that permit such operations.
262              
263             Addresses, Parcels, Shipments, Rates, Transactions, Refunds, Customs Items and
264             Customs Declarations are disposable objects. This means that once you create
265             an object, you cannot change it. Instead, create a new one with the desired
266             values. Carrier Accounts are the exception and may be updated via B
267             requests.
268              
269             =head1 OBJECTS vs COLLECTIONS
270              
271             Whereas a GET request to L
272             might produce a single address whose structure would resemble this:
273              
274             {
275             "object_state":"VALID",
276             "object_purpose":"PURCHASE",
277             "object_source":"FULLY_ENTERED",
278             "object_created":"2014-07-09T02:19:13.174Z",
279             "object_updated":"2014-07-09T02:19:13.174Z",
280             "object_id":"d799c2679e644279b59fe661ac8fa488",
281             "object_owner":"shippotle@goshippo.com",
282             "name":"Shawn Ippotle",
283             "street1":"215 Clayton St.",
284             "street2":"",
285             "city":"San Francisco",
286             "state":"CA",
287             "zip":"94117",
288             "country":"US",
289             "phone":"15553419393",
290             "email":"shippotle@goshippo.com",
291             "ip":null,
292             "is_residential":true,
293             "messages":[],
294             "metadata":"Customer ID 123456"
295             }
296              
297             A GET request to L might produce a
298             collection of address objects whose structure would resemble this:
299              
300             {
301             "count":3,
302             "next":null,
303             "previous":null,
304             "results":[
305             {
306             "object_state":"VALID",
307             "object_purpose":"PURCHASE",
308             "object_source":"FULLY_ENTERED",
309             "object_created":"2014-07-16T23:20:31.089Z",
310             "object_updated":"2014-07-16T23:20:31.089Z",
311             "object_id":"747207de2ba64443b645d08388d0309c",
312             "object_owner":"shippotle@goshippo.com",
313             "name":"Shawn Ippotle",
314             "street1":"215 Clayton St.",
315             "street2":"",
316             "city":"San Francisco",
317             "state":"CA",
318             "zip":"94117",
319             "country":"US",
320             "phone":"15553419393",
321             "email":"shippotle@goshippo.com",
322             "is_residential":true,
323             "ip":null,
324             "messages":[
325            
326             ],
327             "metadata":"Customer ID 123456"
328             },
329             {...},
330             {...}
331             ]
332             }
333              
334             The Shippo API does not distinguish between a single object and a collection
335             of objects. It deals purely with JSON strings and both object and collection
336             are notionally considered to be the same type of object.
337              
338             Nevertheless, the Perl client B distinguish between a single object and
339             a collection of objects. For each API resource, the client defines an I
340             Class> (e.g. C>) and a corresponding
341             I (e.g. C>). The name of
342             the collection class is always the plural form of the item class. Furthermore,
343             any objects making up the C of a collection will be blessed as
344             instances of the appropriate item class.
345              
346             Both item and collection classes implement different interfaces, but they
347             also exhibit common behaviours. Collection classes will respond to methods
348             that aid item access and cursor navigation, while both types of class respond
349             (where appropriate) to C, C, C, C, C and
350             C methods.
351              
352             =head1 METHODS
353              
354             =head2 api_key
355              
356             Get or set the key used by API requests for Shippo's token-based
357             authentication. This is Shippo's preferred method of authentication.
358              
359             =over 2
360              
361             =item * Return the token currently being used for authentication.
362              
363             my $api_key = Shippo->api_key;
364              
365             =item * Set the token to be used for authentication.
366            
367             Shippo->api_key($auth_token);
368              
369             The C method is chainable when used as a setter.
370              
371             =back
372              
373             =head2 api_credentials
374              
375             Get or set the login credentials used by API requests for Shippo's legacy
376             authentication. Legacy authentication means encoding the HTTP Authorization
377             header for Basic Authentication so, even though requests and repsonses are
378             encrypted, you should still consider using the token-based authentication
379             instead (see C).
380              
381             =over 2
382              
383             =item * Return the login credentials currently being used for authentication.
384              
385             my ($username, $password) = Shippo->api_credentials;
386              
387             =item * Set the credentials to be used for authentication.
388              
389             Shippo->api_credentials($username, $password);
390              
391             The C method is chainable when used as a setter.
392              
393             =back
394              
395             Whenever a configuration specifies both token and login credentials, the
396             client will always favour token-based authentication. If C and
397             C are both set manually then it is the most recently
398             set mechanism that defines the HTTP Authorization header.
399              
400             =head1 EXPORTS
401              
402             The C package exports a number of helpful subroutines by
403             default:
404              
405             =head2 true
406              
407             my $fedex_account = Shippo::CarrierAccount->create(
408             carrier => 'fedex',
409             account_id => '',
410             parameters => { meter => '' },
411             test => true,
412             active => true
413             );
414              
415             Returns a scalar value which will evaluate to true.
416              
417             Since the I connecting Shippo's API and the Perl client is
418             JSON, it can feel more natural to think in those terms. Thus, C may be
419             used in place of C<1>. Now, when creating a new object from a JSON example,
420             any literal and accidental use of C or C is much less likely
421             to result in misery.
422              
423             See Ingy's L package for more guidance.
424              
425             =head2 false
426              
427             my $fedex_account = Shippo::CarrierAccount->create(
428             carrier => 'fedex',
429             account_id => '',
430             parameters => { meter => '' },
431             test => false,
432             active => false
433             );
434              
435             Returns a scalar value which will evaluate to false.
436              
437             Since the I connecting Shippo's API and the Perl client is
438             JSON, it can feel more natural to think in those terms. Thus, C may be
439             used in place of C<0>. Now, when creating a new object from a JSON example,
440             any literal and accidental use of C or C is much less likely
441             to result in misery.
442              
443             See Ingy's L package for more guidance.
444              
445             =head2 boolean
446              
447             my $bool = boolean($value);
448              
449             Casts a scalar value to a boolean value (C or C).
450              
451             See Ingy's L package for more guidance.
452              
453             =head2 callback
454              
455             $put_all_accounts_in_test_mode = Shippo::CarrierAccounts->collect(
456             callback { shift->enable_test_mode } );
457             $put_all_accounts_in_test_mode->();
458            
459             Many of the Perl client's methods allow the use of lambda functions for
460             block operations and filtration.
461              
462             See L for more guidance.
463              
464             =head1 CONFIGURATION
465              
466             While the client does provide C and C methods to
467             help with authentication, hard-coding such calls in anything more mission
468             critical than a simple test script may I be the best way to go.
469              
470             As soon as it is imported, one of the first things the client does is search
471             a number of locations for a L
472             configuration file. The first one it finds is loaded.
473              
474             In order, the locations searched are as follows:
475              
476             =over 2
477              
478             =item * C<./.shipporc>
479              
480             =item * C/I/I/.shipporc>
481              
482             =item * C/shipporc>
483              
484             =item * C/I/I/I/I/I/WebService/Shippo/Config.yml>
485              
486             =back
487              
488             The configuration file is very simple and needs to have the following
489             structure, though not all elements are mandatory:
490              
491             ---
492             username: martymcfly@pinheads.org
493             password: yadayada
494             private_token: f0e1d2c3b4a5968778695a4b3c2d1e0f96877869
495             public_token: 96877869f0e1d2c3b4a5968778695a4b3c2d1e0f
496             default_token: private_token
497              
498             At a minimum, your configuration should define values for C and
499             C. These are your Shippo Private and Publishable Auth tokens,
500             which are found on your L.
501              
502             =head1 FULL API DOCUMENTATION
503              
504             =over 2
505              
506             =item * For API documentation, go to L
507              
508             =item * For API support, contact L with any
509             questions.
510              
511             =back
512              
513             =head1 REPOSITORY
514              
515             =over 2
516              
517             =item * L
518              
519             =back
520              
521             =head1 AUTHOR
522              
523             Iain Campbell
524              
525             =head1 COPYRIGHT AND LICENSE
526              
527             This software is copyright E 2015 by Iain Campbell.
528              
529             You may distribute this software under the terms of either the GNU General
530             Public License or the Artistic License, as specified in the Perl README
531             file.
532              
533              
534             =cut