File Coverage

blib/lib/Business/PayPal/API.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package Business::PayPal::API;
2              
3 2     2   66983 use 5.008001;
  2         8  
  2         85  
4 2     2   11 use strict;
  2         4  
  2         72  
5 2     2   12 use warnings;
  2         7  
  2         102  
6              
7 2     2   2415 use SOAP::Lite 0.67; # +trace => 'all';
  0            
  0            
8             use Carp qw(carp);
9              
10             our $VERSION = '0.70';
11             our $CVS_VERSION = '$Id: API.pm,v 1.24 2009/07/28 18:00:58 scott Exp $';
12             our $Debug = 0;
13              
14             ## NOTE: This package exists only until I can figure out how to use
15             ## NOTE: SOAP::Lite's WSDL support for complex types and importing
16             ## NOTE: type definitions, at which point this module will become much
17             ## NOTE: smaller (or non-existent).
18              
19             sub C_api_sandbox () { 'https://api.sandbox.paypal.com/2.0/' }
20             sub C_api_sandbox_3t () { 'https://api-3t.sandbox.paypal.com/2.0/' }
21             sub C_api_live () { 'https://api.paypal.com/2.0/' }
22             sub C_api_live_3t () { 'https://api-3t.paypal.com/2.0/' }
23             sub C_xmlns_pp () { 'urn:ebay:api:PayPalAPI' }
24             sub C_xmlns_ebay () { 'urn:ebay:apis:eBLBaseComponents' }
25             sub C_version () { '3.0' } ## 3.0 adds RecurringPayments
26              
27             ## this is an inside-out object. Make sure you 'delete' additional
28             ## members in DESTROY() as you add them.
29             my %Soap;
30             my %Header;
31              
32             my %H_PKCS12File; ## path to certificate file (pkc12)
33             my %H_PKCS12Password; ## password for certificate file (pkc12)
34             my %H_CertFile; ## PEM certificate
35             my %H_KeyFile; ## PEM private key
36              
37             sub import {
38             my $self = shift;
39             my @modules = @_;
40              
41             for my $module ( @modules ) {
42             eval( "use Business::PayPal::API::$module;" );
43             if( $@ ) {
44             warn $@;
45             next;
46             }
47              
48             ## import 'exported' subroutines into our namespace
49             no strict 'refs';
50             for my $sub ( @{"Business::PayPal::API::" . $module . "::EXPORT_OK"} ) {
51             *{"Business::PayPal::API::" . $sub} = *{"Business::PayPal::API::" . $module . "::" . $sub};
52             }
53             }
54             }
55              
56             sub new {
57             my $class = shift;
58             my %args = @_;
59             my $self = bless \(my $fake), $class;
60              
61             ## if you add new args, be sure to update the test file's @variables array
62             $args{Username} ||= '';
63             $args{Password} ||= '';
64             $args{Signature} ||= '';
65             $args{Subject} ||= '';
66             $args{sandbox} = 1 unless exists $args{sandbox};
67             $args{timeout} ||= 0;
68              
69             $H_PKCS12File{$self} = $args{PKCS12File} || '';
70             $H_PKCS12Password{$self} = $args{PKCS12Password} || '';
71             $H_CertFile{$self} = $args{CertFile} || '';
72             $H_KeyFile{$self} = $args{KeyFile} || '';
73              
74             my $proxy = ($args{sandbox}
75             ? ($args{Signature}
76             ? C_api_sandbox_3t
77             : C_api_sandbox)
78             : ($args{Signature}
79             ? C_api_live_3t
80             : C_api_live)
81             );
82              
83             $Soap{$self} = SOAP::Lite->proxy( $proxy, timeout => $args{timeout} )->uri( C_xmlns_pp );
84              
85             $Header{$self} = SOAP::Header
86             ->name( RequesterCredentials => \SOAP::Header->value
87             ( SOAP::Data->name( Credentials => \SOAP::Data->value
88             ( SOAP::Data->name( Username => $args{Username} )->type(''),
89             SOAP::Data->name( Password => $args{Password} )->type(''),
90             SOAP::Data->name( Signature => $args{Signature} )->type(''),
91             SOAP::Data->name( Subject => $args{Subject} )->type(''),
92             ),
93             )->attr( {xmlns => C_xmlns_ebay} )
94             )
95             )->attr( {xmlns => C_xmlns_pp} )->mustUnderstand(1);
96              
97             return $self;
98             }
99              
100             sub DESTROY {
101             my $self = $_[0];
102              
103             delete $Soap{$self};
104             delete $Header{$self};
105              
106             delete $H_PKCS12File{$self};
107             delete $H_PKCS12Password{$self};
108             delete $H_CertFile{$self};
109             delete $H_KeyFile{$self};
110              
111             my $super = $self->can("SUPER::DESTROY");
112             goto &$super if $super;
113             }
114              
115             sub version_req {
116             return SOAP::Data->name( Version => C_version )
117             ->type('xs:string')->attr( {xmlns => C_xmlns_ebay} );
118             }
119              
120             sub doCall {
121             my $self = shift;
122             my $method_name = shift;
123             my $request = shift;
124             my $method = SOAP::Data->name( $method_name )->attr( {xmlns => C_xmlns_pp} );
125              
126             my $som;
127             {
128             $H_PKCS12File{$self} and local $ENV{HTTPS_PKCS12_FILE} = $H_PKCS12File{$self};
129             $H_PKCS12Password{$self} and local $ENV{HTTPS_PKCS12_PASSWORD} = $H_PKCS12Password{$self};
130             $H_CertFile{$self} and local $ENV{HTTPS_CERT_FILE} = $H_CertFile{$self};
131             $H_KeyFile{$self} and local $ENV{HTTPS_KEY_FILE} = $H_KeyFile{$self};
132              
133             if( $Debug ) {
134             print STDERR SOAP::Serializer->envelope(method => $method,
135             $Header{$self}, $request), "\n";
136             }
137              
138             # $Soap{$self}->readable( $Debug );
139             # $Soap{$self}->outputxml( $Debug );
140              
141             no warnings 'redefine';
142             local *SOAP::Deserializer::typecast = sub {shift; return shift};
143             eval {
144             $som = $Soap{$self}->call( $Header{$self}, $method => $request );
145             };
146              
147             if( $@ ) {
148             carp $@;
149             return;
150             }
151             }
152              
153             if( $Debug ) {
154             ## FIXME: would be nicer to dump a SOM to XML, but how to do that?
155             require Data::Dumper;
156             print STDERR Data::Dumper::Dumper($som->envelope);
157             }
158              
159             if( ref($som) && $som->fault ) {
160             carp "Fault: " . $som->faultstring
161             . ( $som->faultdetail ? " (" . $som->faultdetail . ")" : '' )
162             . "\n";
163             return;
164             }
165              
166             return $som;
167             }
168              
169             sub getFieldsList {
170             my $self = shift;
171             my $som = shift;
172             my $path = shift;
173             my $fields = shift;
174              
175             return unless $som;
176              
177             my %trans_id = ();
178             my @records = ();
179             for my $rec ( $som->valueof($path) ) {
180             my %response = ();
181             @response{keys %$fields} = @{$rec}{keys %$fields};
182              
183             ## avoid duplicates
184             if( defined $response{TransactionID}) {
185             if( $trans_id{$response{TransactionID}}) {
186             next;
187             } else {
188             $trans_id{$response{TransactionID}} = 1;
189             }
190             }
191             push @records, \%response;
192             }
193              
194             return \@records;
195             }
196              
197             sub getFields {
198             my $self = shift;
199             my $som = shift;
200             my $path = shift;
201             my $response = shift;
202             my $fields = shift;
203              
204             return unless $som;
205              
206             ## kudos to Erik Aronesty via email, Drew Simpson via rt.cpan.org (#28596)
207              
208             ## Erik wrote:
209             ##
210             ## If you want me to write the code for the "flagged" version, i
211             ## can .. i think the '/@' flag is a pretty safe, and obvious flag.
212             ##
213             ## the advantage of the flagged version would be that the caller
214             ## doesn't have to check the returned value ... in the case of a
215             ## field where multiple values are expected.
216             ##
217             ##
218             ## I agree with this on principle and would prefer it, but I voted
219             ## against a special flag, now forcing the caller to check the
220             ## return value, but only for the sake of keeping everything
221             ## consistent with the rest of the API. If Danny Hembree wants to
222             ## go through and implement Erik's suggestion, I'd be in favor of
223             ## it.
224              
225             for my $field ( keys %$fields ) {
226             my @vals = grep { defined } $som->valueof("$path/$fields->{$field}");
227             next unless @vals;
228              
229             if( scalar(@vals) == 1 ) {
230             $response->{$field} = $vals[0];
231             }
232             else {
233             $response->{$field} = \@vals;
234             }
235             }
236             }
237              
238             sub getBasic {
239             my $self = shift;
240             my $som = shift;
241             my $path = shift;
242             my $details = shift;
243              
244             return unless $som;
245              
246             for my $field ( qw( Ack Timestamp CorrelationID Version Build ) ) {
247             $details->{$field} = $som->valueof("$path/$field") || '';
248             }
249              
250             return $details->{Ack} =~ /Success/;
251             }
252              
253             sub getErrors {
254             my $self = shift;
255             my $som = shift;
256             my $path = shift;
257             my $details = shift;
258              
259             return unless $som;
260              
261             my @errors = ();
262              
263             for my $enode ( $som->valueof("$path/Errors") ) {
264             push @errors, { LongMessage => $enode->{LongMessage},
265             ErrorCode => $enode->{ErrorCode}, };
266             }
267             $details->{Errors} = \@errors;
268              
269             return;
270             }
271              
272             1;
273             __END__