File Coverage

blib/lib/Business/CPI/Gateway/PayPal/IPN.pm
Criterion Covered Total %
statement 6 6 100.0
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 8 8 100.0


line stmt bran cond sub pod time code
1             package Business::CPI::Gateway::PayPal::IPN;
2             # ABSTRACT: Instant Payment Notifications
3 4     4   48820 use Moo;
  4         8169  
  4         31  
4 4     4   4120 use LWP::UserAgent ();
  4         122524  
  4         1283  
5              
6             our $VERSION = '0.903'; # TRIAL VERSION
7              
8             has is_valid => (
9             is => 'lazy',
10             default => sub {
11             my $self = shift;
12              
13             for ($self->response->decoded_content) {
14             return 0 if /^INVALID$/;
15             return 1 if /^VERIFIED$/;
16              
17             die "Vague response: " . $_;
18             }
19             }
20             );
21              
22             has vars => (
23             is => 'lazy',
24             default => sub {
25             my $self = shift;
26             return { map { $_ => $self->query->param($_) } $self->query->param };
27             },
28             );
29              
30             has gateway_url => (
31             is => 'ro',
32             default => sub { 'https://www.paypal.com/cgi-bin/webscr' },
33             );
34              
35             has query => (
36             is => 'ro',
37             default => sub { require CGI; CGI->new() },
38             );
39              
40             has user_agent_name => (
41             is => 'ro',
42             default => sub {
43             my $base = 'Business::CPI::Gateway::PayPal';
44             my $version = __PACKAGE__->VERSION;
45              
46             return $version ? "$base/$version" : $base;
47             }
48             );
49              
50             has user_agent => (
51             is => 'lazy',
52             default => sub {
53             my $self = shift;
54              
55             my $ua = LWP::UserAgent->new();
56             $ua->agent( $self->user_agent_name );
57              
58             return $ua;
59             },
60             );
61              
62             has response => (
63             is => 'lazy',
64             default => sub {
65             my $self = shift;
66              
67             my $ua = $self->user_agent;
68             my %vars = %{ $self->vars };
69             my $gtw = $self->gateway_url;
70              
71             $vars{cmd} = "_notify-validate";
72              
73             my $r = $ua->post( $gtw, \%vars );
74              
75             die "Couldn't connect to '$gtw': " . $r->status_line
76             if $r->is_error;
77              
78             return $r;
79             },
80             );
81              
82             1;
83              
84             __END__
85              
86             =pod
87              
88             =encoding UTF-8
89              
90             =head1 NAME
91              
92             Business::CPI::Gateway::PayPal::IPN - Instant Payment Notifications
93              
94             =head1 VERSION
95              
96             version 0.903
97              
98             =head1 SYNOPSIS
99              
100             my $ipn = Business::CPI::Gateway::PayPal::IPN->new(
101             # this could be $ctx->req if in Catalyst, for example
102             query => $req,
103              
104             # defaults to the main server, but could be changed to sandbox or
105             # something else
106             gateway_url => 'https://www.sandbox.paypal.com/cgi-bin/webscr',
107             );
108              
109             if ($ipn->is_valid) {
110             my %vars = %{ $ipn->vars };
111              
112             if ($vars{payment_status} eq 'Canceled_Reversal') {
113             # ...
114             }
115             }
116              
117             =head1 DESCRIPTION
118              
119             This is a rewrite of L<Business::PayPal::IPN>. It works somewhat similar to it,
120             and shares almost none of the same code.
121              
122             =head2 But why? Software rewriting is bad!
123              
124             Well, yes, it is usually a bad idea to rewrite software. But the old module had
125             no updates for about 10 years, and, while it worked fine and was well written,
126             Perl has grown a lot in the last 10 years. As I improved my PayPal interface
127             for CPI, I decided I might want to add new features to the IPN module in a near
128             future. As the code was reasonably small, I rewrote it using Moo. This means
129             it's still pretty fast, and much more readable and extensible. Also, the
130             original module had no tests. (Even though it was proven to work due to being
131             used in production.)
132              
133             =head2 How is it different from the original module?
134              
135             It has only attributes, no methods. This gives free caching, and lazy loading.
136             It has less than one third the size (counting blank lines, but not pod). It
137             uses Moo, and has a much more readable code. It has real tests, and the small
138             code makes it easier to find mistakes. I removed some methods like
139             C<completed>, so now you have to check: $ipn->vars->{payment_status} eq
140             'Completed'. There are many more possible payment status in PayPal than what
141             the old module expected (it implemented version 1.5, while at the time of this
142             writing, PayPal's IPN is in version 3.7; so a lot has changed). So I think
143             those auxiliary methods like C<completed>, C<pending>, etc, are not too useful.
144              
145             It's also lazy. Instantiating the object won't try to parse the request.
146             Instead, it waits for you to ask for the variables, or ask if the request is
147             valid. See the L</SYNOPSIS> for more information.
148              
149             =head1 ATTRIBUTES
150              
151             =head2 gateway_url
152              
153             Set this attribute in the constructor in case you want a different server than
154             PayPal's default, such as a test server, or even PayPal's sandbox.
155              
156             =head2 query
157              
158             A CGI-compatible object (e.g. Catalyst::Request).
159              
160             =head2 vars
161              
162             The variables provided by PayPal. Contrary to Business::PayPal::IPN, this
163             returns a HashRef.
164              
165             =head2 is_valid
166              
167             Checks with PayPal that the request was really generated by them. Returns true
168             if PayPal validates, otherwise false.
169              
170             =head2 user_agent_name
171              
172             The name of the user agent to post to PayPal.
173              
174             =head2 user_agent
175              
176             Defaults to a LWP::UserAgent object, but can be a custom object provided for
177             testing purposes, or by the users preference. Could be L<Mojo::UserAgent>, for
178             example.
179              
180             =head2 response
181              
182             The response from PayPal when validating.
183              
184             =head1 SEE ALSO
185              
186             L<Business::PayPal::IPN>
187              
188             =head1 CREDITS
189              
190             Sherzod B. Ruzmetov E<lt>sherzodr@cpan.orgE<gt> for creating Business::PayPal::IPN.
191              
192             =head1 AUTHOR
193              
194             André Walker <andre@andrewalker.net>
195              
196             =head1 COPYRIGHT AND LICENSE
197              
198             This software is copyright (c) 2013 by André Walker.
199              
200             This is free software; you can redistribute it and/or modify it under
201             the same terms as the Perl 5 programming language system itself.
202              
203             =cut