File Coverage

blib/lib/Business/PayPal/IPN.pm
Criterion Covered Total %
statement 12 115 10.4
branch 0 48 0.0
condition 0 8 0.0
subroutine 4 22 18.1
pod 0 13 0.0
total 16 206 7.7


line stmt bran cond sub pod time code
1             package Business::PayPal::IPN;
2              
3             # $Id: IPN.pm,v 1.18 2003/08/19 07:47:08 sherzodr Exp $
4              
5 1     1   6843 use strict;
  1         3  
  1         36  
6 1     1   6 use Carp 'croak';
  1         2  
  1         49  
7 1     1   4 use vars qw($VERSION $GTW $AUTOLOAD $SUPPORTEDV $errstr);
  1         5  
  1         198  
8              
9             # Supported version of PayPal's IPN API
10             $SUPPORTEDV = '1.5';
11              
12             # Gateway to PayPal's validation server as of this writing
13             $GTW = 'https://www.paypal.com/cgi-bin/webscr';
14              
15             # Revision of the library
16             $VERSION = '1.94';
17              
18             # Preloaded methods go here.
19              
20             # Allows access to PayPal IPN's all the variables as method calls
21             sub AUTOLOAD {
22 0     0     my $self = shift;
23              
24 0 0         unless ( ref($self) ) {
25 0           croak "Method $AUTOLOAD is not a class method. You should call it on the object";
26             }
27 0           my ($field) = $AUTOLOAD =~ m/([^:]+)$/;
28 0 0         unless ( exists $self->{_PAYPAL_VARS}->{$field} ) {
29 0           return undef;
30             }
31 1     1   6 no strict 'refs';
  1         2  
  1         1292  
32             # Following line is not quite required to get it working,
33             # but will speed-up subsequent accesses to the same method
34 0     0     *{$AUTOLOAD} = sub { return $_[0]->{_PAYPAL_VARS}->{$field} };
  0            
  0            
35 0           return $self->{_PAYPAL_VARS}->{$field};
36             }
37              
38              
39              
40              
41             # So that AUTOLOAD does not look for destructor. Expensive!
42 0     0     sub DESTROY { }
43              
44              
45              
46              
47              
48             # constructor method. Initializes and returns Business::PayPal::IPN object
49             sub new {
50 0     0 0   my $class = shift;
51 0   0       $class = ref($class) || $class;
52              
53 0           my $self = {
54             _PAYPAL_VARS => {},
55             query => undef,
56             ua => undef,
57             @_,
58             };
59              
60 0           bless $self, $class;
61              
62 0 0         $self->_init() or return undef;
63 0 0         $self->_validate_txn() or return undef;
64              
65 0           return $self;
66             }
67              
68              
69              
70              
71              
72             # initializes class object. Mainly, takes all query parameters presumably
73             # that came from PayPal, and assigns them as object attributes
74             sub _init {
75 0     0     my $self = shift;
76              
77 0 0         my $cgi = $self->cgi() or croak "Couldn't create CGI object";
78 0           map {
79 0           $self->{_PAYPAL_VARS}->{$_} = $cgi->param($_)
80             } ($cgi->param());
81              
82 0 0         unless ( scalar( keys %{$self->{_PAYPAL_VARS}} > 3 ) ) {
  0            
83 0           $errstr = "Insufficient content from the invoker:\n" . $self->dump();
84 0           return undef;
85             }
86 0           return 1;
87             }
88              
89              
90              
91              
92             # validates the transaction by re-submitting it to the PayPal server
93             # and reading the response.
94             sub _validate_txn {
95 0     0     my $self = shift;
96              
97 0           my $cgi = $self->cgi();
98 0           my $ua = $self->user_agent();
99              
100             # Adding a new field according to PayPal IPN manual
101 0           $self->{_PAYPAL_VARS}->{cmd} = "_notify-validate";
102              
103             # making a POST request to the server with all the variables
104 0           my $responce = $ua->post( $GTW, $self->{_PAYPAL_VARS} );
105              
106             # caching the response object in case anyone needs it
107 0           $self->{response} = $responce;
108            
109 0 0         if ( $responce->is_error() ) {
110 0           $errstr = "Couldn't connect to '$GTW': " . $responce->status_line();
111 0           return undef;
112             }
113              
114 0 0         if ( $responce->content() eq 'INVALID' ) {
    0          
115 0           $errstr = "Couldn't validate the transaction. Responce: " . $responce->content();
116 0           return undef;
117             } elsif ( $responce->content() eq 'VERIFIED' ) {
118 0           return 1;
119             }
120              
121             # if we came this far, something is really wrong here:
122 0           $errstr = "Vague response: " . substr($responce->content(), 0, 255);
123 0           return undef;
124             }
125              
126              
127              
128              
129             # returns all the PayPal's variables in the form of a hash
130             sub vars {
131 0     0 0   my $self = shift;
132              
133 0           return %{ $self->{_PAYPAL_VARS} };
  0            
134             }
135              
136              
137              
138              
139              
140              
141              
142             # returns standard CGI object
143             sub cgi {
144 0     0 0   my $self = shift;
145              
146 0 0         if ( defined $self->{query} ) {
147 0           return $self->{query};
148             }
149              
150 0           require CGI;
151              
152 0           my $cgi = CGI->new();
153 0           $self->{query} = $cgi;
154              
155 0           return $self->cgi();
156             }
157              
158              
159             # alias to cgi()
160             sub query {
161 0     0 0   my $self = shift;
162              
163 0           return $self->cgi(@_);
164             }
165              
166              
167              
168             # returns already created response object
169             sub response {
170 0     0 0   my $self = shift;
171              
172 0 0         if ( defined $self->{response} ) {
173 0           return $self->{response};
174             }
175              
176 0           return undef;
177             }
178              
179              
180              
181             # returns user agent object
182             sub user_agent {
183 0     0 0   my $self = shift;
184              
185 0 0         if ( defined $self->{ua} ) {
186 0           return $self->{ua};
187             }
188              
189 0           require LWP::UserAgent;
190            
191 0           my $ua = LWP::UserAgent->new();
192 0           $ua->agent( sprintf("Business::PayPal::IPN/%s (%s)", $VERSION, $ua->agent) );
193 0           $self->{ua} = $ua;
194 0           return $self->user_agent();
195             }
196              
197              
198              
199              
200              
201              
202             # The same as payment_status(), but shorter :-).
203             sub status {
204 0     0 0   my $self = shift;
205 0           return $self->{_PAYPAL_VARS}{payment_status};
206             }
207              
208              
209             # returns true if the payment status is completed
210             sub completed {
211 0     0 0   my $self = shift;
212              
213 0 0         unless ( defined $self->status() ) {
214 0           return undef;
215             }
216 0 0         ($self->status() eq 'Completed') and return 1;
217 0           return 0;
218             }
219              
220              
221             # returns true if the payment status is failed
222             sub failed {
223 0     0 0   my $self = shift;
224              
225 0 0         unless ( defined $self->status() ) {
226 0           return undef;
227             }
228 0 0         ($self->status() eq 'Failed') and return 1;
229 0           return 0;
230             }
231              
232              
233             # returns the reason for pending if the payment status
234             # is pending.
235             sub pending {
236 0     0 0   my $self = shift;
237 0 0         unless ( defined $self->status() ) {
238 0           return undef;
239             }
240 0 0         if ( $self->status() eq 'Pending' ) {
241 0           return $self->{_PAYPAL_VARS}{pending_reason};
242             }
243 0           return 0;
244             }
245              
246              
247             # returns true if payment status is denied
248             sub denied {
249 0     0 0   my $self = shift;
250              
251 0 0         unless ( defined $self->status() ) {
252 0           return undef;
253             }
254 0 0         ($self->status() eq 'Denied') and return 1;
255 0           return 0;
256             }
257              
258              
259              
260             # internally used to assign error messages to $errstr.
261             # Public interface should use it without any arguments
262             # to get the error message
263             sub error {
264 0     0 0   my ($self, $msg) = @_;
265              
266 0 0         if ( defined $msg ) {
267 0           $errstr = $msg;
268             }
269 0           return $errstr;
270             }
271              
272              
273              
274              
275              
276             # for debugging purposes only. Returns the whole object
277             # as a perl data structure using Data::Dumper
278             sub dump {
279 0     0 0   my ($self, $file, $indent) = @_;
280              
281 0   0       $indent ||= 1;
282              
283 0           require Data::Dumper;
284 0           my $d = new Data::Dumper([$self], [ref($self)]);
285 0           $d->Indent( $indent );
286              
287 0 0 0       if ( (defined $file) && (not -e $file) ) {
288 0 0         open(FH, '>' . $file) or croak "Couldn't dump into $file: $!";
289 0           print FH $d->Dump();
290 0 0         close(FH) or croak "Object couldn't be dumped into $file: $!";
291             }
292 0           return $d->Dump();
293             }
294              
295              
296              
297              
298             1;
299             __END__
300             # Below is stub documentation for your module. You'd better edit it!
301              
302             =head1 NAME
303              
304             Business::PayPal::IPN - Perl extension that implements PayPal IPN v1.5
305              
306             =head1 SYNOPSIS
307              
308             use Business::PayPal::IPN;
309              
310             my $ipn = new Business::PayPal::IPN() or die Business::PayPal::IPN->error();
311              
312             if ( $ipn->completed ) {
313             # ...
314             }
315              
316             =head1 ABSTRACT
317              
318             Business::PayPal::IPN implements PayPal IPN version 1.5. It validates transactions
319             and gives you means to get notified of payments to your PayPal account. If you don't already
320             know what PayPal IPN is this library may not be for you. Consult with respective manuals
321             provided by PayPal.com, http://www.paypal.com/.
322              
323             =head2 WARNING
324              
325             I<$Revision: 1.18 $> of Business::PayPal::IPN supports version 1.5 of the API. This was the latest
326             version as of Tuesday, August 19, 2003. Supported version number is available in
327             C<$Business::PayPal::IPN::SUPPORTEDV> global variable. If PayPal introduces new response variables,
328             Business::PayPal::IPN automatically supports those variables thanks to AUTOLOAD. For any further
329             updates, you can contact me or send me a patch.
330              
331             =head1 PAYPAL IPN OVERVIEW
332              
333             As soon as you receive payment to your PayPal account, PayPal posts the transaction details to
334             your specified URL, which you either configure in your PayPal preferences, or in your HTML forms'
335             "notify_url" hidden field.
336              
337             When the payment details are received from, supposedly, PayPal server, your application should
338             check with the PayPal server to make sure it is indeed a valid transaction, and that PayPal is aware
339             of it. This can be achieved by re-submitting the transaction details back to
340             https://www.paypal.com/cgi-bin/webscr and check the integrity of the data.
341              
342             If the transaction is valid, PayPal will respond to you with a single string "VERIFIED",
343             and you can proceed safely. If the transaction is not valid, you will receive "INVALID", and you can
344             log the request for further investigation.
345              
346             So why this verification step is necessary? Because it is very easy for others to simulate a PayPal
347             transaction. If you do not take this step, your program will be tricked into thinking it was a valid
348             transaction, and may act the way you wouldn't want it to act. So you take extra step and check directly
349             with PayPal and see if such a transaction really happened
350              
351             Business::PayPal::IPN is the library which encapsulates all the above complexity into this compact form:
352              
353             my $ipn = new Business::PayPal::IPN() or die Business::PayPal::IPN->error();
354              
355             # if we come this far, we're guaranteed it was a valid transaction.
356             if ( $ipn->completed() ) {
357             # means the funds are already in our paypal account.
358              
359             } elsif ( $ipn->pending() ) {
360             # the payment was made to your account, but its status is still pending
361             # $ipn->pending() also returns the reason why it is so.
362              
363             } elsif ( $ipn->denied() ) {
364             # the payment denied
365              
366             } elsif ( $ipn->failed() ) {
367             # the payment failed
368              
369             }
370              
371             =head1 PREREQUISITES
372              
373             =over 4
374              
375             =item *
376              
377             LWP - to make HTTP requests
378              
379             =item *
380              
381             Crypt::SSLeay - to enable LWP perform https (SSL) requests. If for any reason you
382             are not able to install Crypt::SSLeay, you will need to update $Business::PayPal::IPN::GTW to
383             proper, non-ssl URL.
384              
385             =back
386              
387             =head1 METHODS
388              
389             =over 4
390              
391             =item *
392              
393             C<new()> - constructor. Validates the transaction and returns IPN object. Optionally you may pass
394             it B<query> and B<ua> options. B<query> denotes the CGI object to be used. B<ua> denotes the
395             user agent object. If B<ua> is missing, it will use LWP::UserAgent by default. If the transaction
396             could not be validated, it will return undef and you should check the error() method for a more
397             detailed error string:
398              
399             $ipn = new Business::PayPal::IPN() or die Business::PayPal::IPN->error();
400              
401             =item *
402              
403             C<vars()> - returns all the returned PayPal variables and their respective values in the
404             form of a hash.
405              
406             my %paypal = $ipn->vars();
407             if ( $paypal{payment_status} eq 'Completed' ) {
408             print "Payment was made successfully!";
409             }
410              
411             =item *
412              
413             C<query()> - can also be accessed via C<cgi()> alias, returns respective query object
414              
415             =item *
416              
417             C<response()> - returns HTTP::Response object, which is the response returned while verifying
418             transaction through PayPal. You normally never need this method. In case you do for any reason,
419             here it is.
420              
421             =item *
422              
423             C<user_agent()> - returns user agent object used by the library to verify the transaction.
424             Name of the agent is C<Business::PayPal::IPN/#.# (libwww-perl/#.##)>.
425              
426             =back
427              
428             Business::PayPal::IPN supports all the variables supported by PayPal IPN independent of its
429             version. To access the value of any variable, use the corresponding method name. For example,
430             if you want to get the first name of the user who made the payment ('first_name' variable):
431              
432             my $fname = $ipn->first_name()
433              
434             To get the transaction id ('txn_id' variable)
435              
436             my $txn = $ipn->txn_id()
437              
438             To get payment type ('payment_type' variable)
439              
440             $type = $ipn->payment_type()
441              
442             and so on. For the list of all the available variables, consult IPN Manual provided by PayPal
443             Developer Network. You can find the link at the bottom of http://www.paypal.com.
444              
445             In addition to the above scheme, the library also provides convenience methods
446             such as:
447              
448             =over 4
449              
450             =item *
451              
452             C<status()> - which is a shortcut to C<payment_status()>
453              
454             =item *
455              
456             C<completed()> - returns true if C<payment_status> is "Completed".
457              
458             =item *
459              
460             C<failed()> - returns true if C<payment_status> is "Failed".
461              
462             =item *
463              
464             C<pending()> - returns true if C<payment_status> is "Pending". Return
465             value is also the string that explains why the payment is pending.
466              
467             =item *
468              
469             C<denied()> - returns true if C<payment_status> is "Denied".
470              
471             =back
472              
473             =head1 RETURN VALUES OF METHODS
474              
475             Methods can return 1, 0 or undefined as well as any other true value. The distinction
476             between 0 (which is false) and undefined (which is also false) is important:
477              
478             $ipn->completed eq undef and print "Not relevant for this transaction type";
479             $ipn->completed == 1 and print "Transaction was completed";
480             $ipn->completed == 0 and print "Transaction was NOT completed";
481              
482             In other words, methods return undef indicating this variable is not relevant for
483             this transaction type ("txn_type"). A good example for such transactions is "subscr_signup"
484             transaction, that do not return any "payment_status" nor "txn_id" variables. Methods return
485             0 (zero) indicating failure. They return 1 (one) or any other true value indicating success.
486              
487             =head1 DEBUGGING
488              
489             If for any reason your PayPal IPN solutions don't work as expected, you have no other
490             choice but debugging the process. Although it sounds complex, it really is not.All you need
491             to do is get your IPN script to dump Business::PayPal::IPN object into a file and investigate
492             to see what exactly is happening. For this reason, we provide C<dump()> method which does
493             just that:
494              
495             =over 4
496              
497             =item *
498              
499             C<dump([$filename] [,$indent])> - for dumping Business::PayPal::IPN object.
500             If used without any arguments, simply returns the object as Perl data structure.
501             If filename is passed as the first argument, object is dumped into the file.
502             The second argument, if present, should be a value between 1 and 3 to indicate how well
503             indented the dump file should be. For debugging purposes, I believe 2 is enough, but
504             go ahead and try out for yourself to compare differences.
505              
506             =back
507              
508             Note that the object is dumped only once to the same file. So after investigating the dump,
509             you may need to remove the file or dump to another file instead.
510              
511             Interpreting the dump file may seem tricky, since it is relatively big file. But you don't
512             need to understand everything in it. Simply look for the attribute called "_PAYPAL_VARS".
513             It is a hashref that keeps all the variables returned from PayPal server. These are also
514             the methods that are available through Business::PayPal::IPN object.
515              
516             You can also investigate the content of "response" attribute. It holds the HTTP::Response
517             object. Look for the "_content" attribute of this object. This is what was returned from
518             PayPal.com in response to your request. Ideally, this should hold "VERIFIED". "INVALID"
519             is also explainable though :-).
520              
521             Before you do any "dumping" around, include the following lines on top of your IPN script
522             if you haven't done so already. This will ensure that when PayPal.com calls your IPN script,
523             all the warnings and error messages, if any, will be saved in this file.
524              
525             use CGI::Carp 'carpout';
526             BEGIN {
527             open(LOG, '>>path/to/error.log') && carpout(\*LOG);
528             }
529              
530             =head1 VARIABLES
531              
532             Following global variables are available:
533              
534             =over 4
535              
536             =item *
537              
538             $Business::PayPal::IPN::GTW - gateway url to PayPal's Web Script. Default
539             is "https://www.paypal.com/cgi-bin/webscr", which you may not want to
540             change. But it comes handy while testing your application through a PayPal simulator.
541              
542             =item *
543              
544             $Business::PayPal::IPN::SUPPORTEDV - supported version of PayPal's IPN API.
545             Default value is "1.5". You can modify it before creating ipn object (as long as you
546             know what you are doing. If not don't touch it!)
547              
548             =item *
549              
550             $Business::PayPal::IPN::VERSION - version of the library
551              
552             =back
553              
554             =head1 AUTHOR
555              
556             Sherzod B. Ruzmetov E<lt>sherzodr@cpan.orgE<gt>
557              
558             =head1 CREDITS
559              
560             Following people contributed to this library with their patches and suggestions. It's very
561             possible that list is not complete. Help me with it.
562              
563             =over 4
564              
565             =item B<Brian Grossman>
566              
567             Fixes in the source code. F<pathces/brian-grososman>.
568              
569             =item B<Thomas J Mather>
570              
571             Documentation fixes. F<patches/thomas-mather.patch>
572              
573             =back
574              
575             =head1 COPYRIGHT AND LICENSE
576              
577             Copyright 2003 by Sherzod B. Ruzmetov.
578              
579             This library is free software; you can redistribute it and/or modify
580             it under the same terms as Perl itself.
581              
582             THIS LIBRARY IS PROVIDED WITH THE USEFULNESS IN MIND, BUT WITHOUT EVEN IMPLIED
583             GUARANTEE OF MERCHANTABILITY NOR FITNESS FOR A PARTICULAR PURPOSE. USE IT AT YOUR OWN RISK.
584              
585             =head1 REVISION
586              
587             $Revision: 1.18 $
588              
589             =cut