File Coverage

blib/lib/Business/WebMoney.pm
Criterion Covered Total %
statement 19 21 90.4
branch n/a
condition n/a
subroutine 7 7 100.0
pod n/a
total 26 28 92.8


line stmt bran cond sub pod time code
1             package Business::WebMoney;
2              
3 2     2   42249 use 5.008000;
  2         8  
  2         87  
4 2     2   13 use strict;
  2         4  
  2         72  
5 2     2   11 use warnings;
  2         3  
  2         198  
6 2     2   3316 use utf8;
  2         21  
  2         10  
7              
8             our $VERSION = '0.11';
9              
10 2     2   226 use Carp;
  2         4  
  2         234  
11 2     2   3792 use LWP::UserAgent;
  2         195229  
  2         74  
12 2     2   1650 use XML::LibXML;
  0            
  0            
13             use HTTP::Request;
14             use File::Spec;
15             use POSIX();
16              
17             sub new
18             {
19             my ($class, @args) = @_;
20              
21             my $opt = parse_args(\@args, {
22             p12_file => 'mandatory',
23             p12_pass => undef,
24             timeout => 20,
25             ca_file => undef,
26             });
27              
28             my $ca_file = $opt->{ca_file};
29             $ca_file or ($ca_file) = grep(-r $_, map(File::Spec->catdir($_, qw(Business WebMoney WebMoneyCA.crt)), @INC));
30             $ca_file or warn "Business/WebMoney/WebMoneyCA.crt missing";
31              
32             my $self = {
33             p12_file => $opt->{p12_file},
34             p12_pass => $opt->{p12_pass},
35             timeout => $opt->{timeout},
36             ca_file => $ca_file,
37             };
38              
39             return bless $self, $class;
40             }
41              
42             sub parse_args
43             {
44             my ($args_list, $fields) = @_;
45              
46             if (@$args_list % 2) {
47              
48             croak 'Unpaired arguments';
49             }
50              
51             my %args;
52              
53             while (@$args_list) {
54              
55             my $key = shift @$args_list;
56             my $value = shift @$args_list;
57              
58             exists($fields->{$key}) or croak "Unknown argument $key";
59             exists($args{$key}) and croak "Argument $key specified multiple times";
60              
61             $args{$key} = $value;
62             }
63              
64             while (my ($key, $value) = each(%$fields)) {
65              
66             unless (exists($args{$key})) {
67              
68             if ($value && $value eq 'mandatory') {
69              
70             croak "Mandatory argument $key not specified";
71              
72             } else {
73              
74             $args{$key} = $value;
75             }
76             }
77             }
78              
79             return \%args;
80             }
81              
82             sub request
83             {
84             my ($self, %args) = @_;
85              
86             my $old_locale = POSIX::setlocale(&POSIX::LC_ALL, 'C');
87              
88             my $res = $self->do_request(%args);
89              
90             POSIX::setlocale(&POSIX::LC_ALL, $old_locale);
91              
92             return $res;
93             }
94              
95             sub do_request
96             {
97             my ($self, %args) = @_;
98              
99             $self->{errstr} = undef;
100             $self->{errcode} = undef;
101              
102             my $req_fields = parse_args($args{args}, { %{$args{arg_rules}}, debug_response => undef });
103              
104             my $doc = XML::LibXML::Document->new('1.0', 'UTF-8');
105              
106             my $request = $doc->createElement('w3s.request');
107             $doc->setDocumentElement($request);
108              
109             my $node = $doc->createElement('reqn');
110             $request->appendChild($node);
111             $node->appendChild($doc->createTextNode($req_fields->{reqn}));
112             delete $req_fields->{reqn};
113              
114             my $data_node = $doc->createElement($args{req_tagname});
115             $request->appendChild($data_node);
116              
117             while (my ($key, $value) = each %$req_fields) {
118              
119             next unless defined $value;
120             next if $key eq 'debug_response';
121              
122             my $node = $doc->createElement($key);
123             $data_node->appendChild($node);
124             $node->appendChild($doc->createTextNode($value));
125             }
126              
127             my $res = eval {
128              
129             local $SIG{__DIE__};
130            
131             # Warning! Thread unsafe!
132              
133             local %ENV = %ENV;
134              
135             $ENV{HTTPS_PKCS12_FILE} = $self->{p12_file};
136             $ENV{HTTPS_PKCS12_PASSWORD} = $self->{p12_pass};
137             $ENV{HTTPS_CA_FILE} = $self->{ca_file};
138              
139             my $req_data = $doc->serialize;
140              
141             utf8::encode($req_data) if utf8::is_utf8($req_data);
142              
143             my $res_content;
144              
145             unless ($res_content = $req_fields->{debug_response}) {
146              
147             my $ua = LWP::UserAgent->new;
148             $ua->timeout($self->{timeout} + 1);
149              
150             my $req = HTTP::Request->new;
151             $req->method('POST');
152             $req->uri("https://w3s.wmtransfer.com/asp/XML$args{func}Cert.asp");
153             $req->content($req_data);
154              
155             my ($res, $timeout);
156              
157             eval {
158             local $SIG{__DIE__};
159             local $SIG{ALRM} = sub {
160             $timeout = 1;
161             };
162              
163             alarm($self->{timeout});
164             $res = $ua->request($req);
165             alarm(0);
166             };
167              
168             if ($timeout) {
169              
170             $self->{errcode} = -1001;
171             $self->{errstr} = 'Connection timeout';
172             return undef;
173              
174             } elsif (!$res->is_success) {
175              
176             $self->{errcode} = $res->code;
177             $self->{errstr} = $res->message;
178             return undef;
179             }
180              
181             $res_content = $res->content;
182             }
183              
184             my $parser = XML::LibXML->new;
185              
186             my $doc = $parser->parse_string($res_content);
187              
188             if (my $retval = $doc->findvalue('/w3s.response/retval')) {
189              
190             $self->{errcode} = $retval;
191             $self->{errstr} = $doc->findvalue('/w3s.response/retdesc');
192             return undef;
193             }
194              
195             my ($result_node) = $doc->getElementsByTagName($args{result_tag});
196              
197             if ($args{result_format} eq 'list') {
198              
199             [ map { result_node($_) } grep { $_->isa('XML::LibXML::Element') } $result_node->childNodes ];
200              
201             } elsif ($args{result_format} eq 'hash') {
202              
203             result_node($result_node);
204              
205             } else {
206              
207             1;
208             }
209             };
210              
211             if ($@) {
212              
213             $self->{errcode} = -1000;
214             $self->{errstr} = $@;
215             return undef;
216             }
217              
218             return $res;
219             }
220              
221             sub errcode
222             {
223             my ($self) = @_;
224              
225             return $self->{errcode};
226             }
227              
228             sub errstr
229             {
230             my ($self) = @_;
231              
232             return $self->{errstr};
233             }
234              
235             sub result_node
236             {
237             my ($node) = @_;
238              
239             my %result;
240              
241             for my $attr ($node->attributes) {
242              
243             my $key = $attr->name;
244              
245             $result{$key} = $attr->value;
246             }
247              
248             for my $child (grep { $_->isa('XML::LibXML::Element') } $node->childNodes) {
249              
250             my $key = $child->nodeName;
251              
252             $result{$key} = $child->textContent;
253             }
254              
255             return \%result;
256             }
257              
258             sub get_operations
259             {
260             my ($self, @args) = @_;
261              
262             return $self->request(
263             func => 'Operations',
264             args => \@args,
265             arg_rules => {
266             reqn => 'mandatory',
267             purse => 'mandatory',
268             wmtranid => undef,
269             tranid => undef,
270             wminvid => undef,
271             orderid => undef,
272             datestart => 'mandatory',
273             datefinish => 'mandatory',
274             },
275             req_tagname => 'getoperations',
276             result_format => 'list',
277             result_tag => 'operations',
278             );
279             }
280              
281             sub invoice
282             {
283             my ($self, @args) = @_;
284              
285             return $self->request(
286             func => 'Invoice',
287             args => \@args,
288             arg_rules => {
289             reqn => 'mandatory',
290             orderid => 'mandatory',
291             customerwmid => 'mandatory',
292             storepurse => 'mandatory',
293             amount => 'mandatory',
294             desc => 'mandatory',
295             address => '',
296             period => '0',
297             expiration => '0',
298             },
299             req_tagname => 'invoice',
300             result_format => 'hash',
301             result_tag => 'invoice',
302             );
303             }
304              
305             sub get_out_invoices
306             {
307             my ($self, @args) = @_;
308              
309             return $self->request(
310             func => 'OutInvoices',
311             args => \@args,
312             arg_rules => {
313             reqn => 'mandatory',
314             purse => 'mandatory',
315             wminvid => undef,
316             orderid => undef,
317             datestart => 'mandatory',
318             datefinish => 'mandatory',
319             },
320             req_tagname => 'getoutinvoices',
321             result_format => 'list',
322             result_tag => 'outinvoices',
323             );
324             }
325              
326             sub get_in_invoices
327             {
328             my ($self, @args) = @_;
329              
330             return $self->request(
331             func => 'InInvoices',
332             args => \@args,
333             arg_rules => {
334             reqn => 'mandatory',
335             wmid => 'mandatory',
336             wminvid => undef,
337             datestart => 'mandatory',
338             datefinish => 'mandatory',
339             },
340             req_tagname => 'getininvoices',
341             result_format => 'list',
342             result_tag => 'ininvoices',
343             );
344             }
345              
346             sub reject_protect
347             {
348             my ($self, @args) = @_;
349              
350             return $self->request(
351             func => 'RejectProtect',
352             args => \@args,
353             arg_rules => {
354             reqn => 'mandatory',
355             wmtranid => 'mandatory',
356             },
357             req_tagname => 'rejectprotect',
358             result_format => 'hash',
359             result_tag => 'operation',
360             );
361             }
362              
363             sub finish_protect
364             {
365             my ($self, @args) = @_;
366              
367             return $self->request(
368             func => 'FinishProtect',
369             args => \@args,
370             arg_rules => {
371             reqn => 'mandatory',
372             wmtranid => 'mandatory',
373             pcode => 'mandatory',
374             },
375             req_tagname => 'finishprotect',
376             result_format => 'hash',
377             result_tag => 'operation',
378             );
379             }
380              
381             sub message
382             {
383             my ($self, @args) = @_;
384              
385             return $self->request(
386             func => 'SendMsg',
387             args => \@args,
388             arg_rules => {
389             reqn => 'mandatory',
390             receiverwmid => 'mandatory',
391             msgsubj => 'mandatory',
392             msgtext => 'mandatory',
393             },
394             req_tagname => 'message',
395             result_format => 'hash',
396             result_tag => 'message',
397             );
398             }
399              
400             sub get_balance
401             {
402             my ($self, @args) = @_;
403              
404             return $self->request(
405             func => 'Purses',
406             args => \@args,
407             arg_rules => {
408             reqn => 'mandatory',
409             wmid => 'mandatory',
410             },
411             req_tagname => 'getpurses',
412             result_format => 'list',
413             result_tag => 'purses',
414             );
415             }
416              
417             sub money_back
418             {
419             my ($self, @args) = @_;
420              
421             return $self->request(
422             func => 'TransMoneyback',
423             args => \@args,
424             arg_rules => {
425             reqn => 'mandatory',
426             inwmtranid => 'mandatory',
427             amount => 'mandatory',
428             },
429             req_tagname => 'trans',
430             result_format => 'hash',
431             result_tag => 'operation',
432             );
433             }
434              
435             sub transfer
436             {
437             my ($self, @args) = @_;
438              
439             return $self->request(
440             func => 'Trans',
441             args => \@args,
442             arg_rules => {
443             reqn => 'mandatory',
444             tranid => 'mandatory',
445             pursesrc => 'mandatory',
446             pursedest => 'mandatory',
447             amount => 'mandatory',
448             period => 0,
449             pcode => undef,
450             desc => 'mandatory',
451             wminvid => 0,
452             },
453             req_tagname => 'trans',
454             result_format => 'hash',
455             result_tag => 'operation',
456             );
457             }
458              
459             1;
460              
461             __END__