dfc6da1c9d3c631a96eedf8e832ea8b1cae8187b
[Net-Artera.git] / lib / Net / Artera.pm
1 package Net::Artera;
2
3 use 5.005;
4 use strict;
5 use Data::Dumper;
6 use URI::Escape;
7 use LWP::UserAgent;
8 use XML::Simple;
9 use Locale::Country;
10
11 #require Exporter;
12 use vars qw($VERSION @ISA $DEBUG @login_opt); #$WARN );
13          # @EXPORT @EXPORT_OK %EXPORT_TAGS);
14 #@ISA = qw(Exporter);
15
16 # This allows declaration       use Net-Artera ':all';
17 # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
18 # will save memory.
19 #%EXPORT_TAGS = ( 'all' => [ qw(
20 #       
21 #) ] );
22
23 #@EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
24
25 #@EXPORT = qw();
26
27 $VERSION = '0.01';
28
29 #$WARN = 0;
30 $DEBUG = 0;
31
32 =head1 NAME
33
34 Net::Artera - Perl extension for Artera XML API.
35
36 =head1 SYNOPSIS
37
38   use Net::Artera;
39
40   my $connection = new Net::Artera (
41     'username'   => 'reseller_username',
42     'password'   => 'reseller_password',
43     'production' => 0,
44   );
45
46 =head1 DESCRIPTION
47
48 This is a Perl module which speaks the Artera XML API.
49 See <http://www.arteraturbo.com>.  Artera Resellers can use this module
50 to access some features of the API.
51
52 =head1 METHODS
53
54 =over 4
55
56 =item new [ OPTIONS_HASHREF | OPTION => VALUE ... ]
57
58 Constructor.  Options can be passed as a hash reference or a list.  Options are
59 case-insensitive.
60
61 Available options are:
62
63 =over 4
64
65 =item username - Reseller username
66
67 =item password - Reseller password
68
69 =item rid - Reseller ID (RID)
70
71 =item pid - Product ID (PID).
72
73 =item production - if set true, uses the production server instead of the staging server.  
74
75 =back
76
77 =cut
78
79 @login_opt = qw( RID Username Password );
80
81 sub new {
82   my $proto = shift;
83   my $class = ref($proto) || $proto;
84   my $self = {};
85   bless ($self, $class);
86
87   my $opt = $self->_lc_hash_or_hashref(@_);
88   $self->{$_} = $opt->{$_} for map lc($_), @login_opt;
89
90   if ( defined($opt->{'production'}) && $opt->{'production'} ) {
91     $self->{'url'} = 'https://secure.arteragroup.com/';
92   } else {
93     $self->{'url'} = 'http://staging.arteragroup.com/';
94   }
95   $self->{'url'} .= 'Wizards/wsapi/31/APIService.asmx';
96
97   $self->{'ua'} = LWP::UserAgent->new;
98
99   warn "\n$self created: ". Dumper($self) if $DEBUG;
100
101   $self;
102 }
103
104 sub _lc_hash_or_hashref {
105   my $self = shift;
106   my $opt = ref($_[0]) ? shift : {@_};
107   my $gratuitous = { map { lc($_) => $opt->{$_} } keys %$opt };
108   $gratuitous;
109 }
110
111 =item newTrial [ OPTIONS_HASHREF | OPTION => VALUE ... ]
112
113 Options can be passed as a hash reference or a list.  Options are
114 case-insensitive.
115
116 Available options are:
117
118 =over 4
119
120 =item email (required)
121
122 =item cname (required) - Customer's name
123
124 =item ref (required) - Reseller's own order reference
125
126 =item pid (required) - Artera Product ID
127
128 =item priceid (required) - Artera Price ID
129
130 =item aid - Affiliate ID number used when the Reseller wants to track some type of sales channel beneath them.
131
132 =item add1*
133
134 =item add2
135
136 =item add3* - City
137
138 =item add4* - State
139
140 =item zip*
141
142 =item cid* - Country ID.  Defaults to 2 (USA).  Can be specified as a numeric CID or as an ISO 3166 two-letter country code or full name.
143
144 =item phone
145
146 =item fax
147
148 =back 
149
150 *These fields are optional, but must be supplied as a set.
151
152 Returns a hash reference with the following keys (these keys B<are>
153 case-sensitive):
154
155 =over 4
156
157 =item id - This is the Result ID to indicate success or failure: 1 for success, anything else for failure
158
159 =item message - Some descriptive text regarding the success or failure
160
161 =item ASN - The Artera Serial Number
162
163 =item AKC - The Artera Key Code
164
165 =item TrialID - The Artera Trial Number
166
167 =item Ref - The Reseller Reference
168
169 =item CustomerID - Artera's CustomerID
170
171 =item TrialLength - Trial Length
172
173 =cut
174
175 sub newTrial {
176   my $self = shift;
177   my $opt = $self->_lc_hash_or_hashref(@_);
178   $self->_newX('Trial', $opt);
179 }
180
181 =item newOrder [ OPTIONS_HASHREF | OPTION => VALUE ... ]
182
183 Available options are the same as B<newTrial>.  Additionally the I<asn> and
184 I<akc> fields may be specified to convert a trial to an order.
185
186 =cut
187
188 sub newOrder {
189   my $self = shift;
190   my $opt = $self->_lc_hash_or_hashref(@_);
191   push @{$opt->{'optional_params'}}, qw( ASN AKC );
192   $self->_newX('Order', $opt);
193 }
194
195 sub _newX {
196   my( $self, $x, $opt ) = @_;
197
198   if ( defined($opt->{'cid'}) ) {
199     $opt->{'cid'} = $self->_country2cid($opt->{'cid'});
200   } else {
201     $opt->{'cid'} = 2 if grep defined($_), qw(Add1 Add3 Add4 Zip);
202   }
203
204   push @{$opt->{'required_params'}},
205        qw( Email CName Ref PID PriceID );
206   push @{$opt->{'optional_params'}},
207        qw( AID Add1 Add2 Add3 Add4 Zip CID Phone Fax );
208
209   $self->_submit( "new$x", $opt );
210
211 }
212
213 my %country2cid = (
214   'uk' => 1,
215   'gb' => 1,
216   'us' => 2,
217   'in' => 3,
218   'jp' => 4,
219   'ru' => 5,
220   'fr' => 6,
221   'pl' => 7,
222   'gr' => 8,
223   'ug' => 9,
224   'lk' => 10,
225   'sa' => 11,
226   'nl' => 12,
227   'pe' => 13,
228   'ca' => 14,
229   'nz' => 15,
230   'kr' => 16,
231   'it' => 17,
232   'es' => 18,
233   'il' => 19,
234   'se' => 20,
235   'de' => 21,
236   'ie' => 22,
237   'mx' => 23,
238   'au' => 24,
239   'to' => 25,
240   'eg' => 26,
241   'tr' => 27,
242   'am' => 28,
243   'az' => 29,
244   'by' => 30,
245   'ee' => 31,
246   'ge' => 32,
247   'kz' => 33,
248   'kg' => 34,
249   'lt' => 35,
250   'md' => 36,
251   'tj' => 38,
252   'tm' => 39,
253   'ua' => 40,
254   'uz' => 41,
255   '' => 42, #BOSNIA
256   '' => 43, #HERZEGOVINA
257   'hr' => 44,
258   'mk' => 45,
259   '' => 46, #SERBIA
260   '' => 47, #MONTENEGRO
261   'si' => 48,
262   'er' => 49,
263   'mh' => 51,
264   'pw' => 52,
265   'fm' => 53,
266   'na' => 54,
267   'lv' => 56,
268   'za' => 57,
269   'jm' => 58,
270 );
271
272 sub _country2cid {
273   my( $self, $country ) = @_;
274   if ( $country =~ /^\s*(\d+)\s*$/ ) {
275     $1;
276   } elsif ( $country =~ /^\s*(\w\w)\s*$/ ) {
277     $country2cid{$1};
278   } elsif ( $country !~ /^\s*$/ ) {
279     $country2cid{country2code($country)};
280   } else {
281     '';
282   }
283 }
284
285 =item statusChange [ OPTIONS_HASHREF | OPTION => VALUE ... ]
286
287 Options can be passed as a hash reference or a list.  Options are
288 case-insensitive.
289
290 Available options are:
291
292 =over 4
293
294 =item ASN (required) - Artera Serial Number
295
296 =item AKC (required) - Artera Key Code
297
298 =item StatusID (required) - Possible StatusID values are as follows:
299
300 =over 4
301
302 =item 15 - Normal Unrestricted: re-enable a disabled Serial Number (e.g. a payment dispute has been resolved so the Serial Number needs to be re-enabled).
303
304 =item 16 - Disable: temporarily prohibit an end-user's serial number from working (e.g. there is a payment dispute, so you want to turn off the Serial Number until the dispute is resolved).
305
306 =item 17 - Terminate: permanently prohibit an end-user's Serial Number from working (e.g. subscription cancellation) 
307
308 =back
309
310 =item Reason - Reason for terminating
311
312 =back
313
314 Returns a hash reference with the following keys (these keys B<are>
315 case-sensitive):
316
317 =over 4
318
319 =item id - This is the Result ID to indicate success or failure: 1 for success, anything else for failure
320
321 =item message - Some descriptive text regarding the success or failure
322
323 =back
324
325 =cut
326
327 sub statusChange {
328   my $self = shift;
329   my $opt = $self->_lc_hash_or_hashref(@_);
330
331   push @{$opt->{'required_params'}},
332        qw( ASN AKC StatusID );
333   push @{$opt->{'optional_params'}}, 'Reason';
334
335   $self->_submit('statusChange', $opt );
336 }
337
338 =item getProductStatus [ OPTIONS_HASHREF | OPTION => VALUE ... ]
339
340 Options can be passed as a hash reference or a list.  Options are
341 case-insensitive.
342
343 Available options are:
344
345 =over 4
346
347 =item ASN (required) - Artera Serial Number
348
349 =item AKC (required) - Artera Key Code
350
351 =back
352
353 Returns a hash reference with the following keys (these keys B<are>
354 case-sensitive):
355
356 =over 4
357
358 =item id - This is the Result ID to indicate success or failure: 1 for success, anything else for failure
359
360 =item message - On failure, descriptive text regarding the failure
361
362 =item StatusID (required) - Possible StatusID values are as follows:
363
364 =over 4
365
366 =item 15 - Normal Unrestricted: re-enable a disabled Serial Number (e.g. a payment dispute has been resolved so the Serial Number needs to be re-enabled).
367
368 =item 16 - Disable: temporarily prohibit an end-user's serial number from working (e.g. there is a payment dispute, so you want to turn off the Serial Number until the dispute is resolved).
369
370 =item 17 - Terminate: permanently prohibit an end-user's Serial Number from working (e.g. subscription cancellation) 
371
372 =back
373
374 =item Description - Status description
375
376 =back
377
378 =cut
379
380 sub getProductStatus {
381   my $self = shift;
382
383   my $opt = $self->_lc_hash_or_hashref(@_);
384
385   push @{$opt->{'required_params'}}, qw( ASN AKC );
386
387   my $result = $self->_submit('getProductStatus', $opt );
388
389   # munch results, present as flat list
390   $result->{$_} = $result->{'Status'}->{$_} foreach (qw(StatusID Description));
391   delete $result->{'Status'};
392
393   $result;
394
395 }
396
397 =item updateContentControl [ OPTIONS_HASHREF | OPTION => VALUE ... ]
398
399 Options can be passed as a hash reference or a list.  Options are
400 case-insensitive.
401
402 Available options are:
403
404 =over 4
405
406 =item ASN (required) - Artera Serial Number
407
408 =item AKC (required) - Artera Key Code
409
410 =item UseContentControl (required) - 0 for off, 1 for on
411
412 =back
413
414 Returns a hash reference with the following keys (these keys B<are>
415 case-sensitive):
416
417 =over 4
418
419 =item id - This is the Result ID to indicate success or failure: 1 for success, anything else for failure
420
421 =item message - Some descriptive text regarding the success or failure
422
423 =back
424
425 =cut
426
427 sub updateContentControl {
428   my $self = shift;
429
430   my $opt = $self->_lc_hash_or_hashref(@_);
431
432   push @{$opt->{'required_params'}}, qw( ASN AKC UseContentControl );
433
434   $self->_submit('updateContentControl', $opt );
435 }
436
437 =item orderListByDate [ OPTIONS_HASHREF | OPTION => VALUE ... ]
438
439 Unimplemented.
440
441 =cut
442
443 #--
444
445 sub _submit {
446   my( $self, $method, $opt ) = @_;
447   my $ua = $self->{'ua'};
448
449   my $param = {
450     ( map { $_ => $self->{lc($_)} }
451           @login_opt,
452     ),
453     ( map { $_ => $opt->{lc($_)} }
454           @{$opt->{'required_params'}}
455     ),
456     ( map { $_ => ( exists $opt->{lc($_)} ? $opt->{lc($_)} : '' ) }
457           @{$opt->{'optional_params'}}
458     ),
459   };
460   warn "$self url $self->{url}/$method\n" if $DEBUG;
461   warn "$self request parameters: ". Dumper($param). "\n" if $DEBUG;
462
463   #POST
464   my $response = $ua->post( "$self->{'url'}/$method", $param );
465
466   warn "$self raw response: ". $response->content. "\n" if $DEBUG;
467
468   #unless ( $response->is_success ) {
469   #  die $response->content;
470   #}
471
472   my $xml = XMLin( $response->content );
473   warn "$self parsed response: ". Dumper($xml) if $DEBUG;
474
475   #warn "\n".$xml->{'message'}."\n" unless $xml->{'id'} == 1 or not $WARN;
476
477   $xml;
478
479 }
480
481 =back
482
483 =head1 BUGS
484
485 orderListByDate is unimplemented.
486
487 =head1 SEE ALSO
488
489 <http://www.arteraturbo.com>
490
491 =head1 AUTHOR
492
493 Ivan Kohler, E<lt>ivan-net-artera@420.amE<gt>
494
495 Freeside, open-source billing for ISPs: <http://www.sisd.com/freeside>
496
497 Not affiliated with Artera Group, Inc.
498
499 =head1 COPYRIGHT AND LICENSE
500
501 Copyright (C) 2004 Ivan Kohler
502
503 This library is free software; you can redistribute it and/or modify
504 it under the same terms as Perl itself.
505
506 =cut
507
508 1;
509