Beefy Boxes and Bandwidth Generously Provided by pair Networks
The stupid question is the question not asked

Re: How do you use Paypal IPN with Dancer2?

by perlfan (Curate)
on Jul 13, 2016 at 21:14 UTC ( #1167733=note: print w/replies, xml ) Need Help??

in reply to How do you use Paypal IPN with Dancer2?

I've been there. DON'T use that script. Use Business::PayPal::IPN instead.

Inside of your handler, do something like:
my $PP_WEBSCR = q{}; +# for testing #my $PP_WEBSCR = q{} # for produc +tion $Business::PayPal::IPN::GTW = $PP_WEBSCR; # set PP verification url yo +u use my $ipn = eval { Business::PayPal::IPN->new( query => query_string ) } +; # where "query_string" is a Dancer2 method #... do something with $ipn or handle error
Also note, that unless you want PP to keep attempting the IPN, always return 200.

Replies are listed 'Best First'.
Re^2: How do you use Paypal IPN with Dancer2?
by $h4X4_|=73}{ (Monk) on Jul 14, 2016 at 09:08 UTC

    I was going to mention Business::PayPal::IPN but i seen in Business::CPI::Gateway::PayPal::IPN the person that wrote it stated this about Business::PayPal::IPN = This is a rewrite of Business::PayPal::IPN. It works somewhat similar to it, and shares almost none of the same code.

    Looking into what Business::PayPal::IPN does. It uses CGI to get the param's, so if the module works. that means it decodes the content before sending it back to PayPal.
    You can try to use this code on $return_query to decode it and see if it works now.

    sub decode_it { my $value = shift || ''; $value =~ tr/+/ /; $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; return $value; }

    Update: What perlfan means by "always return 200" is at the end of the code you need to print out a blank header like in the example. They use text/plain for the 200 response at the end. I'm not sure what that would equal in Dancer2 to get a header like that.
    print "content-type: text/plain\n\n";

      Also, it has to be an HTTP status of 200. For example, if you have an uncaught die (but it's your fault not PPs) or your processing script crashes, then PP has no way of knowing not to repeat the IPN. Also, I'll have to take a look at Business::CPI::Gateway::PayPal::IPN. Thanks!

        You can catch the errors. This needs to be tested, but i bet it will work.

        #!/usr/bin/perl BEGIN { $| = 1; # if you need it use strict; use warnings; # Catch fatal errors. $SIG{__DIE__} = \&print_header; } #my $Just_Exit = 0; # It is highly recommended that you use version 6 upwards of # the UserAgent module since it provides for tighter server # certificate validation use LWP::UserAgent 6; my $query = ''; # read post from PayPal system and add 'cmd' # maybe add security to limit CONTENT_LENGTH read (STDIN, $query, $ENV{'CONTENT_LENGTH'}); $query = decode_it($query); $query .= '&cmd=_notify-validate'; # post back to PayPal system to validate my $ua = LWP::UserAgent->new(ssl_opts => { verify_hostname => 1 }); # # my $req = HTTP::Request->new('POST', ' +gi-bin/webscr'); $req->content_type('application/x-www-form-urlencoded'); $req->header(Host => ''); # ? $req->content($query); my $res = $ua->request($req); # make the variable hash my %variable = map { split(m'='x, $_, 2) } grep { m'='x } split(m'&'x, $query); # assign posted variables to local variables my $item_name = $variable{'item_name'}; my $item_number = $variable{'item_number'}; my $payment_status = $variable{'payment_status'}; my $payment_amount = $variable{'mc_gross'}; my $payment_currency = $variable{'mc_currency'}; my $txn_id = $variable{'txn_id'}; my $receiver_email = $variable{'receiver_email'}; my $payer_email = $variable{'payer_email'}; if ($res->is_error) { # HTTP error } elsif ($res->content eq 'VERIFIED') { # check the $payment_status=Completed # check that $txn_id has not been previously processed # check that $receiver_email is your Primary PayPal email # check that $payment_amount/$payment_currency are correct # process payment } elsif ($res->content eq 'INVALID') { # log for manual investigation } else { # error } # end with header or will die with header print_header('Good'); sub print_header { my $error = shift || ''; # what you do here can die like logging. That can be detected with $Ju +st_Exit # so we know we have been here before and not to run the thing that di +ed # if ( $error ne 'Good' && ! $Just_Exit ) { # $Just_Exit = 1; # log($error); # } # error will be the die info with \n print "Content-type: text/plain\n\n"; exit(0); } sub decode_it { my $value = shift || ''; $value =~ tr/+/ /; $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; return $value; }

      Thanks, the decoding doesn't seem to be working if I've handled it correctly.

      As far as I can tell, I'm ending the script gracefully as the web page for sending the test reports everything ok (so I assume that means that it's got all the responses it expected)

      The 'invalid' it sends is in content not a header, incidentally.

      I do wonder if this issue may to be do with my apache set up and the forwarding to Dancer and if there might not be some kind of header changing going on there.

      I've also seen someone claim their script doesn't work in the simulator but does when they use the sandbox environment

      I think, for now, I'm going to use a different validation method - or just assume that the IPN is good enough as it is.

      If I solve this, I'll come back and report on it.

      Thanks again


        Try using HTTP with the sandbox server.
        change to
        Update: and this my $ua = LWP::UserAgent->new(ssl_opts => { verify_hostname => 1 }); to this my $ua = LWP::UserAgent->new;
        That would mean your SSL encryption was not right or PP could have sent it to you none HTTPS or the sandbox server does not run SSL or...

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://1167733]
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others studying the Monastery: (3)
As of 2018-07-20 01:33 GMT
Find Nodes?
    Voting Booth?
    It has been suggested to rename Perl 6 in order to boost its marketing potential. Which name would you prefer?

    Results (422 votes). Check out past polls.