line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
1
|
|
|
1
|
|
647
|
use strict; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
31
|
|
2
|
1
|
|
|
1
|
|
4
|
use warnings; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
54
|
|
3
|
|
|
|
|
|
|
package WebService::TaxJar; |
4
|
|
|
|
|
|
|
$WebService::TaxJar::VERSION = '0.0001'; |
5
|
1
|
|
|
1
|
|
511
|
use HTTP::Thin; |
|
1
|
|
|
|
|
85070
|
|
|
1
|
|
|
|
|
42
|
|
6
|
1
|
|
|
1
|
|
538
|
use HTTP::Request::Common qw/GET DELETE PUT POST/; |
|
1
|
|
|
|
|
3776
|
|
|
1
|
|
|
|
|
78
|
|
7
|
1
|
|
|
1
|
|
569
|
use HTTP::CookieJar; |
|
1
|
|
|
|
|
28668
|
|
|
1
|
|
|
|
|
55
|
|
8
|
1
|
|
|
1
|
|
731
|
use JSON; |
|
1
|
|
|
|
|
7896
|
|
|
1
|
|
|
|
|
6
|
|
9
|
1
|
|
|
1
|
|
172
|
use URI; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
26
|
|
10
|
1
|
|
|
1
|
|
488
|
use Ouch; |
|
1
|
|
|
|
|
1468
|
|
|
1
|
|
|
|
|
5
|
|
11
|
1
|
|
|
1
|
|
591
|
use Moo; |
|
1
|
|
|
|
|
11131
|
|
|
1
|
|
|
|
|
7
|
|
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
=head1 NAME |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
WebService::TaxJar - A simple client to L. |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
=head1 VERSION |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
version 0.0001 |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
=head1 SYNOPSIS |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
use WebService::TaxJar; |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
my $tj = WebService::TaxJar->new(api_key => 'XXXXXXXXXXxxxxxxxxxxxx', version => 'v2'); |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
my $categories = $tj->get('categories'); |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
=head1 DESCRIPTION |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
A light-weight wrapper for TaxJar's RESTful API (an example of which can be found at: L). This wrapper basically hides the request cycle from you so that you can get down to the business of using the API. It doesn't attempt to manage the data structures or objects the web service interfaces with. |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
The module takes care of all of these things for you: |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
=over 4 |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
=item Host selection |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
Based on the value of the C flag, the module will either send requests to the production environment C 0> or the sandbox environment C 1>. |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
=item Adding authentication headers |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
C adds an authentication header of the type "Authorization: Bearer C<$tj-Eapi_key>" to each request. |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
=item Adding api version number to URLs |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
C prepends the C< $tj-Eversion > to each URL you submit. |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
=item PUT/POST data translated to JSON |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
When making a request like: |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
$tj->post('customers', { customer_id => '27', exemption_type => 'non_exempt', name => 'Andy Dufresne', }); |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
The data in POST request will be translated to JSON using . |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
=item Response data is deserialized from JSON and returned from each call. |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
=back |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
=head1 EXCEPTIONS |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
All exceptions in C are handled by C. A 500 exception C<"Server returned unparsable content."> is returned if TaxJar's server returns something that isn't JSON. If the request isn't successful, then an exception with the code and response and string will be thrown. |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
=head1 METHODS |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
The following methods are available. |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
=head2 new ( params ) |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
Constructor. |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
=over |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
=item params |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
A hash of parameters. |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
=over |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
=item api_key |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
Your key for accessing TaxJar's API. Required. |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
=item version |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
The version of the API that you are using, like 'v1', 'v2', etc. Required. |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
=item sandbox |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
A boolean that, if true, will send all requests to TaxJar's sandbox environment for testing instead of to production. Defaults to 0. |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
=cut |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
=item debug_flag |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
Just a spare, writable flag so that users of the object should log debug information, since TaxJar will likely ask for request/response pairs when |
98
|
|
|
|
|
|
|
you're having problems. |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
my $sales_tax = $taxjar->get('taxes', $order_information); |
101
|
|
|
|
|
|
|
if ($taxjar->debug_flag) { |
102
|
|
|
|
|
|
|
$log->info($taxjar->last_response->request->as_string); |
103
|
|
|
|
|
|
|
$log->info($taxjar->last_response->content); |
104
|
|
|
|
|
|
|
} |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
=cut |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
has api_key => ( |
109
|
|
|
|
|
|
|
is => 'ro', |
110
|
|
|
|
|
|
|
required => 1, |
111
|
|
|
|
|
|
|
); |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
has version => ( |
114
|
|
|
|
|
|
|
is => 'ro', |
115
|
|
|
|
|
|
|
required => 1, |
116
|
|
|
|
|
|
|
); |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
has sandbox => ( |
119
|
|
|
|
|
|
|
is => 'ro', |
120
|
|
|
|
|
|
|
required => 0, |
121
|
|
|
|
|
|
|
default => sub { 0 }, |
122
|
|
|
|
|
|
|
); |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
has debug_flag => ( |
125
|
|
|
|
|
|
|
is => 'rw', |
126
|
|
|
|
|
|
|
required => 0, |
127
|
|
|
|
|
|
|
default => sub { 0 }, |
128
|
|
|
|
|
|
|
); |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
=item agent |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
A LWP::UserAgent compliant object used to keep a persistent cookie_jar across requests. By default this module uses HTTP::Thin, but you can supply another object when |
133
|
|
|
|
|
|
|
creating a WebService::TaxJar object. |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
=back |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
=back |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
=cut |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
has agent => ( |
142
|
|
|
|
|
|
|
is => 'ro', |
143
|
|
|
|
|
|
|
required => 0, |
144
|
|
|
|
|
|
|
lazy => 1, |
145
|
|
|
|
|
|
|
builder => '_build_agent', |
146
|
|
|
|
|
|
|
); |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
sub _build_agent { |
149
|
0
|
|
|
0
|
|
|
return HTTP::Thin->new( cookie_jar => HTTP::CookieJar->new() ) |
150
|
|
|
|
|
|
|
} |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
=head2 last_response |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
The HTTP::Response object from the last request/reponse pair that was sent, for debugging purposes. |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
=cut |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
has last_response => ( |
159
|
|
|
|
|
|
|
is => 'rw', |
160
|
|
|
|
|
|
|
required => 0, |
161
|
|
|
|
|
|
|
); |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
=head2 get(path, params) |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
Performs a C request, which is used for reading data from the service. |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
=over |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
=item path |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
The path to the REST interface you wish to call. |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
=item params |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
A hash reference of parameters you wish to pass to the web service. These parameters will be added as query parameters to the URL for you. |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
=back |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
=cut |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
sub get { |
182
|
0
|
|
|
0
|
1
|
|
my ($self, $path, $params) = @_; |
183
|
0
|
|
|
|
|
|
my $uri = $self->_create_uri($path); |
184
|
0
|
|
|
|
|
|
$uri->query_form($params); |
185
|
0
|
|
|
|
|
|
return $self->_process_request( GET $uri->as_string ); |
186
|
|
|
|
|
|
|
} |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
=head2 delete(path) |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
Performs a C request, deleting data from the service. |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
=over |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
=item path |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
The path to the REST interface you wish to call. |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
=item params |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
A hash reference of parameters you wish to pass to the web service. These parameters will be added as query parameters to the URL for you. |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
=back |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
=cut |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
sub delete { |
207
|
0
|
|
|
0
|
1
|
|
my ($self, $path, $params) = @_; |
208
|
0
|
|
|
|
|
|
my $uri = $self->_create_uri($path); |
209
|
0
|
|
|
|
|
|
$uri->query_form($params); |
210
|
0
|
|
|
|
|
|
return $self->_process_request( DELETE $uri->as_string ); |
211
|
|
|
|
|
|
|
} |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
=head2 put(path, json) |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
Performs a C request, which is used for updating data in the service. |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
=over |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
=item path |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
The path to the REST interface you wish to call. |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
=item params |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
A hash reference of parameters you wish to pass to Tax Jar. This will be translated to JSON. |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
=back |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
=cut |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
sub put { |
232
|
0
|
|
|
0
|
1
|
|
my ($self, $path, $params) = @_; |
233
|
0
|
|
|
|
|
|
my $uri = $self->_create_uri($path); |
234
|
0
|
|
|
|
|
|
my %headers = ( Content => to_json($params), "Content-Type" => 'application/json', ); |
235
|
0
|
|
|
|
|
|
return $self->_process_request( POST $uri->as_string, %headers ); |
236
|
|
|
|
|
|
|
} |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
=head2 post(path, params, options) |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
Performs a C request, which is used for creating data in the service. |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
=over |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
=item path |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
The path to the REST interface you wish to call. |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
=item params |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
A hash reference of parameters you wish to pass to Tax Jar. They will be encoded as JSON. |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
=back |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
=head2 Notes |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
The path you provide as arguments to the request methods C should not have a leading slash. |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
As of early 2019: |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
The current version of their API is 'v2'. There is no default value for the C parameter, so please provide this when creating a WebService::TaxJar object. |
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
TaxJar does not provide a free sandbox for prototyping your code, it is part of their premium service level. |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
TaxJar's sandbox mode does not implement all API endpoints. |
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
=cut |
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
sub post { |
269
|
0
|
|
|
0
|
1
|
|
my ($self, $path, $params) = @_; |
270
|
0
|
|
|
|
|
|
my $uri = $self->_create_uri($path); |
271
|
0
|
|
|
|
|
|
my %headers = ( Content => to_json($params), "Content-Type" => 'application/json', ); |
272
|
0
|
|
|
|
|
|
return $self->_process_request( POST $uri->as_string, %headers ); |
273
|
|
|
|
|
|
|
} |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
sub _create_uri { |
276
|
0
|
|
|
0
|
|
|
my $self = shift; |
277
|
0
|
|
|
|
|
|
my $path = shift; |
278
|
0
|
0
|
|
|
|
|
my $host = $self->sandbox |
279
|
|
|
|
|
|
|
? 'https://api.sandbox.taxjar.com' |
280
|
|
|
|
|
|
|
: 'https://api.taxjar.com' |
281
|
|
|
|
|
|
|
; |
282
|
0
|
|
|
|
|
|
return URI->new(join '/', $host, $self->version, $path); |
283
|
|
|
|
|
|
|
} |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
sub _add_auth_header { |
286
|
0
|
|
|
0
|
|
|
my $self = shift; |
287
|
0
|
|
|
|
|
|
my $request = shift; |
288
|
0
|
|
|
|
|
|
$request->header( Authorization => 'Bearer '.$self->api_key() ); |
289
|
0
|
|
|
|
|
|
return; |
290
|
|
|
|
|
|
|
} |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
sub _process_request { |
293
|
0
|
|
|
0
|
|
|
my $self = shift; |
294
|
0
|
|
|
|
|
|
my $request = shift; |
295
|
0
|
|
|
|
|
|
$self->_add_auth_header($request); |
296
|
0
|
|
|
|
|
|
my $response = $self->agent->request($request); |
297
|
0
|
|
|
|
|
|
$response->request($request); |
298
|
0
|
|
|
|
|
|
$self->last_response($response); |
299
|
0
|
|
|
|
|
|
$self->_process_response($response); |
300
|
|
|
|
|
|
|
} |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
sub _process_response { |
303
|
0
|
|
|
0
|
|
|
my $self = shift; |
304
|
0
|
|
|
|
|
|
my $response = shift; |
305
|
0
|
|
|
|
|
|
my $result = eval { from_json($response->decoded_content) }; |
|
0
|
|
|
|
|
|
|
306
|
0
|
0
|
|
|
|
|
if ($@) { |
|
|
0
|
|
|
|
|
|
307
|
0
|
|
|
|
|
|
ouch 500, 'Server returned unparsable content.', { error => $@, content => $response->decoded_content }; |
308
|
|
|
|
|
|
|
} |
309
|
|
|
|
|
|
|
elsif ($response->is_success) { |
310
|
0
|
|
|
|
|
|
return from_json($response->content); |
311
|
|
|
|
|
|
|
} |
312
|
|
|
|
|
|
|
else { |
313
|
0
|
|
|
|
|
|
ouch $response->code, $response->as_string; |
314
|
|
|
|
|
|
|
} |
315
|
|
|
|
|
|
|
} |
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
=head1 PREREQS |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
L |
320
|
|
|
|
|
|
|
L |
321
|
|
|
|
|
|
|
L |
322
|
|
|
|
|
|
|
L |
323
|
|
|
|
|
|
|
L |
324
|
|
|
|
|
|
|
L |
325
|
|
|
|
|
|
|
L |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
=head1 SUPPORT |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
=over |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
=item Repository |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
L |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
=item Bug Reports |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
L |
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
=back |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
=head1 AUTHOR |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
Colin Kuskie |
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
=head1 LEGAL |
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
This module is Copyright 2019 Plain Black Corporation. It is distributed under the same terms as Perl itself. |
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
=cut |
350
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
1; |