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