Coder Social home page Coder Social logo

paypal-ipn-listener's Introduction

PayPal IPN Listener

Packagist Version Build Status Scrutinizer Quality Score Total Downloads License

A PayPal IPN (Instant Payment Notification) listener for PHP

Index

Prerequisites

  1. PHP >=7.1.0
  2. A good understanding of how the PayPal Instant Payment Notification system works (see here)

Installation

composer require mike182uk/paypal-ipn-listener

Architecture

This package is made up of several components that work together:

  • Listener - Listens for and processes the IPN messages
  • Verifier - Verifies the IPN message with PayPal
  • Service - Communicates with PayPal
  • Message - Wrapper around the IPN message
  • MessageFactory - Creates a new message instance from a data source
  • EventDispatcher - Dispatches events

The listener creates a Message using a MessageFactory. The Message is passed to the Verifier which uses a Service to communicate with PayPal. The Listener uses the EventDispatcher to dispatch events relating to the outcome of the IPN message verification.

The MessageFactory and Service components are swappable components.

This package provides 2 message factories:

  1. Mdb\PayPal\Ipn\MessageFactory\InputStreamMessageFactory - Creates a message from the php://input stream
  2. Mdb\PayPal\Ipn\MessageFactory\ArrayMessageFactory - Creates a message from an array passed to the setData method

This package provides 1 service:

  1. Mdb\PayPal\Ipn\Service\GuzzleService - Uses Guzzle to communicate with PayPal

Usage

You can either build up the listener object manually or you can use a listener builder. This package provides 2 listener builders:

  1. Mdb\PayPal\Ipn\ListenerBuilder\Guzzle\InputStreamListenerBuilder - Builds a listener using the guzzle service and the input stream message factory
  2. Mdb\PayPal\Ipn\ListenerBuilder\Guzzle\ArrayListenerBuilder - Builds a listener using the guzzle service and the array message factory

Using a listener builder is the preferred way of building up a listener object.

Using a listener builder

use Mdb\PayPal\Ipn\ListenerBuilder\Guzzle\InputStreamListenerBuilder as ListenerBuilder;

$listener = (new ListenerBuilder)->build();

Building up the listener manually

use GuzzleHttp\Client;
use Mdb\PayPal\Ipn\InputStream;
use Mdb\PayPal\Ipn\Listener;
use Mdb\PayPal\Ipn\MessageFactory\InputStreamMessageFactory;
use Mdb\PayPal\Ipn\Service\GuzzleService;
use Mdb\PayPal\Ipn\Verifier;
use Symfony\Component\EventDispatcher\EventDispatcher;

$service = new GuzzleService(
    new Client(),
    'https://www.sandbox.paypal.com/cgi-bin/webscr'
);

$verifier = new Verifier($service);

$messageFactory = new InputStreamMessageFactory(new InputStream());

$listener = new Listener(
    $messageFactory,
    $verifier,
    new EventDispatcher()
);

A lot of plumbing is needed to create the listener manually. The job of the listener builder is to abstract away this logic.

Subscribing to events

Once you have created the listener object you can subscribe to the events that it will dispatch:

use Mdb\PayPal\Ipn\Event\MessageVerifiedEvent;
use Mdb\PayPal\Ipn\Event\MessageInvalidEvent;
use Mdb\PayPal\Ipn\Event\MessageVerificationFailureEvent;

$listener->onVerified(function (MessageVerifiedEvent $event) {
   $ipnMessage = $event->getMessage();
   
   // IPN message was verified, everything is ok! Do your processing logic here...
});

$listener->onInvalid(function (MessageInvalidEvent $event) {
   $ipnMessage = $event->getMessage();
   
   // IPN message was was invalid, something is not right! Do your logging here...
});

$listener->onVerificationFailure(function (MessageVerificationFailureEvent $event) {
    $error = $event->getError();
    
    // Something bad happend when trying to communicate with PayPal! Do your logging here...
});

You can use any callable when subscribing to an event:

class IpnProcessor
{
    public function onVerified(MessageVerifiedEvent $event)
    {
        $message = $event->getMessage();
        
        // ...
    }
}

$listener->onVerified(array(new IpnProcessor, 'onVerified'));
class IpnProcessor
{
    public static function onVerified(MessageVerifiedEvent $event)
    {
        $message = $event->getMessage();
        
        // ...
    }
}

$listener->onVerified(array('IpnProcessor', 'onVerified'));

Listening for IPN messages

The last thing you need to do to kick of the process is listen for an IPN message:

$listener->listen();

Full Example

use Mdb\PayPal\Ipn\Event\MessageVerifiedEvent;
use Mdb\PayPal\Ipn\Event\MessageInvalidEvent;
use Mdb\PayPal\Ipn\Event\MessageVerificationFailureEvent;
use Mdb\PayPal\Ipn\ListenerBuilder\Guzzle\InputStreamListenerBuilder as ListenerBuilder;

$listener = (new ListenerBuilder)->build();

$listener->onVerified(function (MessageVerifiedEvent $event) {
   $ipnMessage = $event->getMessage();
   
   // IPN message was verified, everything is ok! Do your processing logic here...
});

$listener->onInvalid(function (MessageInvalidEvent $event) {
   $ipnMessage = $event->getMessage();
   
   // IPN message was was invalid, something is not right! Do your logging here...
});

$listener->onVerificationFailure(function (MessageVerificationFailureEvent $event) {
    $error = $event->getError();
    
    // Something bad happend when trying to communicate with PayPal! Do your logging here...
});

$listener->listen();

Sandbox mode

When using one of the provided listener builders you can set your listener to use PayPal's sandbox for testing purposes:

$listenerBuilder = new ListenerBuilder();

$listenerBuilder->useSandbox(); // use PayPal sandbox

$listener = $listenerBuilder->build();

You can find some full usage examples in the examples directory.

Extending

To create your own service you must implement Mdb\PayPal\Ipn\Service.

To create your own message factory you must implement Mdb\PayPal\Ipn\MessageFactory.

To create your own listener builder it is best to extend Mdb\PayPal\Ipn\ListenerBuilder as this provides most of the boilerplate code needed to create a listener builder.

You will notice that when using any of the provided guzzle listener builders that there is a useSandbox method exposed. You can add this functionality to your listener builder by using the Mdb\PayPal\Ipn\ListenerBuilder\ModeDependentServiceEndpoint trait (see Mdb\PayPal\Ipn\ListenerBuilder\GuzzleListenerBuilder for usage example).

Notes

Testing

PayPal provides an IPN simulator here.

paypal-ipn-listener's People

Contributors

dependabot-preview[bot] avatar dependabot[bot] avatar garyross avatar mablae avatar mike182uk avatar omnierror avatar sebdesign avatar shirshir avatar stefanneubig avatar swader avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

paypal-ipn-listener's Issues

Curl error: [35] SSL connect error.

When I receive IPN, CurlVerifier make this error:

Curl error: [35] SSL connect error.

I try solve with $verifier->forceSSLv3(false); and $verifier->forceSSLv3(true);, but don't work. Trying with CURLOPT_SSLVERSION = 4 generate other errors.

Can you help me?

CURLOPT_SSLVERSION

CURLOPT_SSLVERSION is currently set to 3 in CurlVerifier.php, this needs to be updated to 1 (TLSv1) as Paypal no longer allows SSLv3. Verification will fail as a result of this breaking change from Paypal.

PayPal new line handling

UPDATE: I just discovered this is a CodeIgniter bug, where new lines are being standardised on all post data. This is how it was incorrectly fixed (missing double quotes) in another IPN implementation which is the same as my method. https://github.com/orderly/codeigniter-paypal-ipn/blob/master/activerecord/libraries/PayPal_IPN.php#L181

This bug refers to at least v1.1.1, I haven't tested on v2.0.0 but from what i've seen it doesn't look like it is handled any differently. I haven't submitted a pull request because I am still using v1.1.1.

I found that when a PayPal user has 2 lines in their address settings, I was getting INVALID responses. This doesn't occur often (at least for me) because not many payers have 2 lines in their address.

Using $encodedData .= '&' . $k . '=' . urlencode(str_replace("\n", "\r\n", $v)); in Request.php encodeData() fixed the issue for me.

The Raw Post contained "%0A" in the address field, but PayPal would only return VERIFIED with "%0D%0A".

ssl error requesting from non-ssl page on non-standard port

I have needed to add "curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);" to the Curl Class to avoid an SSL error I was getting. It seems the original issue was addressed within the Socket Class by utilising a ternery operator and the ports 443 and 80. In development i'm actually using a non-ssl port other than 80 (due to numerous Virtualhosts etc). I don't mind using a 'quick-fix' for the present but when i'm live (and back to using port 80) will I still have to add this line? Just double-checking..
Cheers.

ipn listener not verifying

I am using the current version, but had used the older version for some time without issues. Its probably just that I have been trying to track this problem (and am tired) and have come up against a brick wall.
The ipns are being sent, returning a http200 and the code is being logged into $listener->onVerified but Paypal isn't getting a response that its gone through. Any idea how to proceed with debugging this further would be greatly appeciated as this is on a live site. Cheers.

Feature request: convert to internal character set

PayPal IPN messages are usually sent as Windows-1252 character set.

However, our application uses UTF-8 internally and I guess most application do.

An automatic character set conversion would be really convenient.

Message:: issue

Hello,

I've copied your example and it appears that as soon as I try to use $ipnMessage['ipnFieldHere'] I get a 500 error, from paypal.

Here's my code:

   public function postPaypalAPI()
    {
        $listener = new Listener;
        $verifier = new CurlVerifier;
        $ipnMessage =  Message::createFromGlobals();

        $verifier->setIpnMessage($ipnMessage);
        $verifier->setEnvironment('sandbox'); // can either be sandbox or production
        $listener->setVerifier($verifier);

        $listener->listen(function() use ($listener) {
            if($ipnMessage['reciever_email'] == 'myemail')//Just to make sure it's being sent to me.
            {
                //Other code here.
            }
            $data = array('name' => 'Donut', 'text' => $listener->getReport(), 'header'=>'PayPal test');
            Mail::send('emails.notification', $data, function($message)
            {
                $message->to('[email protected]', 'DonutSeller')->subject('VALID!');
            });
            $resp = $listener->getVerifier()->getVerificationResponse();
        }, function() use ($listener) {
            $data = array('name' => 'Donut', 'text' => $listener->getReport(), 'header'=>'PayPal test');
            Mail::send('emails.notification', $data, function($message)
            {
                $message->to('[email protected]', 'DonutSeller')->subject('INVALID!');
            });
            $report = $listener->getReport();
            $resp = $listener->getVerifier()->getVerificationResponse();
        });
    }

I can´t get running

Hello i have a problem with this, i dont know if the listener are working im using Laravel 4.1 and my code is this:
I have a route like this:
Route::post('pago/paypal', array('uses' => 'PagosController@pagopp'));
And in my controller i have this:
public function pagopp(){

    $request = new PayPal\Ipn\Request\Curl();

    $listener = new PayPal\Ipn\Listener($request);

    $listener->setMode('sandbox');

    try {
        $status = $listener->verifyIpn();
    }
    catch (Exception $e) {
        $error = $e->getMessage();
        $status = false;
    }

    if ($status) {
        $data = $_POST;
        $id_cotizacion = $data['item_number'];
        $cantidad =  $data['mc_gross'];
        $pago = new Pago;
        $pago->movimiento = $data['txn_id'];
        $pago->id_cliente = $data['custom'];
        $pago->id_cotizacion =  $id_cotizacion;
        $pago->cantidad =  $cantidad
        $pago->save();
        $cotizacion = Cotizacion::find($id_cotizacion);
        $cotizacion->saldo = $cotizacion->saldo - $cantidad);
        if (($cotizacion->saldo - $cantidad) <= 0) {
            $cotizacion->estado = 'C';
        }
        $cotizacion->save();
    } else {
        $report = $listener->getReport();
    }

}

but nothing hapen y checked the ipn request from ipn history in my sandbox account and all the fields are fine.

Guzzle 6

Any plans for a Guzzle 6.x version? Thanks!

Problems integrating with Symfony 3.4

I tried to integrate the listener with Symfony 3.4 and have the following issues:

Class 'Mdb\PayPal\Ipn\Service\GuzzleService' not found

Fatal Error: Interface 'Mdb\PayPal\Ipn\Service' not found

Symfony\Component\Debug\Exception\ClassNotFoundException: Attempted to load interface "Service" from namespace "Mdb\PayPal\Ipn". Did you forget a "use" statement for e.g. "JMS\DiExtraBundle\Tests\Metadata\Driver\Fixture\Service" or "JMS\DiExtraBundle\Annotation\Service"? in /Users/myuser/projects/myproject/vendor/mike182uk/paypal-ipn-listener/src/Service/GuzzleService.php:11 

My current implementation:

<?php
namespace VENDOR\PaymentBundle\Service;

use GuzzleHttp\Client;
use Mdb\PayPal\Ipn\Verifier;
use Mdb\PayPal\Ipn\Listener;
use Mdb\PayPal\Ipn\InputStream;
use Mdb\PayPal\Ipn\Service\GuzzleService;
use Mdb\PayPal\Ipn\Event\MessageInvalidEvent;
use Symfony\Component\HttpFoundation\Request;
use Mdb\PayPal\Ipn\Event\MessageVerifiedEvent;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Mdb\PayPal\Ipn\Event\MessageVerificationFailureEvent;
use Mdb\PayPal\Ipn\MessageFactory\InputStreamMessageFactory;

class PayPalIpnService
{
    /** @var bool */
    private $sandbox;
    /** @var Client */
    private $guzzleClientProd;
    /** @var Client */
    private $guzzleClientTest;

    public function __construct(
        Client $guzzleClientProd,
        Client $guzzleClientTest,
        bool $sandbox
    ) {
        $this->sandbox = $sandbox;
        $this->guzzleClientProd = $guzzleClientProd;
        $this->guzzleClientTest = $guzzleClientTest;
    }

    public function handle(Request $request): Response
    {
        $service = new GuzzleService(
            $this->sandbox ? $this->guzzleClientTest : $this->guzzleClientProd,
            ''
        );

        $verifier = new Verifier($service);

        $messageFactory = new InputStreamMessageFactory(new InputStream());

        $listener = new Listener(
            $messageFactory,
            $verifier,
            new EventDispatcher()
        );

        return $this->listen($listener);
    }

    private function listen(Listener $listener): Response
    {
        $listener->onVerified(function (MessageVerifiedEvent $event) {
            $ipnMessage = $event->getMessage();

            // IPN message was verified, everything is ok! Do your processing logic here...
        });

        $listener->onInvalid(function (MessageInvalidEvent $event) {
            $ipnMessage = $event->getMessage();

            // IPN message was was invalid, something is not right! Do your logging here...
        });

        $listener->onVerificationFailure(function (MessageVerificationFailureEvent $event) {
            $error = $event->getError();

            // Something bad happend when trying to communicate with PayPal! Do your logging here...
        });
    }
}

Version v8.0.2

Any ideas?

Add guzzlehttp/guzzle 7 support

[Editted]
At the moment, there is no guzzlehttp/guzzle 7 support for v9.0.0 but there is for dev-master

- mike182uk/paypal-ipn-listener v9.0.0 requires guzzlehttp/guzzle ^6.2 -> satisfiable by guzzlehttp/guzzle[6.5.x-dev].

Laravel 8 for example requires Guzzle 7.
Thanks!

cURL and SSL Problems

Hi Mike!

I am experiencing weird issues regarding cURL and SSL. I am getting this error:

PHP Fatal error: Uncaught exception 'RuntimeException' with message 'Curl error: [35] error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure.' in /var/www-vhosts//vendor/mike182uk/paypal-ipn-listener/src/PayPal/Ipn/Verifier/CurlVerifier.php:89\nStack trace:\n#0 /var/www-vhosts//vendor/mike182uk/paypal-ipn-listener/src/PayPal/Ipn/Verifier.php(162): PayPal\Ipn\Verifier\CurlVerifier->sendVerificationRequest()\n#1 /var/www-vhosts//vendor/mike182uk/paypal-ipn-listener/src/PayPal/Ipn/Listener.php(87): PayPal\Ipn\Verifier->verify()\n#2 /var/www-vhosts/l/vendor/mike182uk/paypal-ipn-listener/src/PayPal/Ipn/Listener.php(112): PayPal\Ipn\Listener->processIpn()\n#3 /var/www-vhosts//paypalipn.php(169): PayPal\Ipn\Listener->listen(Object(Closure), Object(Closure))\n#4 {main}\n thrown in /var/www-vhosts//vendor/mike182uk/paypal-ipn-listener/src/PayPal/Ipn/Verifier/CurlVerifier.php on line 89

Is it caused by any recent changes in the code maybe?

IPN verification failure

A few people have reported they are having problems with the IPN messages not being verified when they should be, most notably here: #28

We need to do some investigation to see what is actually going on here to try and resolve the problem.

Example not working

Both these lines gave me errors:
$request = new \PayPal\Ipn\Request\Curl();

$request->useSSL(true); //dont need to do this as its done by default, just demonstrating configuring the request component

I had to remove the parens off of Curl, and comment out that second line to use it further

Verification is false (sometimes)?

I have been using your ipn listener for quite a while with no problems (thanks Mike) and have just recently found that I get intermittent false results for $verified = $ipn->verifyIPN() , even though when I resend the IPN post from Paypal they go through ok. Any ideas? The only possible problem I can think of could be using $ipn->usePHPCerts();//leave it up to php-curl as I previously had problems with local certs.

IPN sandbox returns invalid 100% of the time

When using this package, I get INVALID as a response 100% of the time. I have followed the debugging steps, and made sure that I'm calling the correct sandbox URL (that's automatic with this package) and that I'm sending the exact same params in the exact same order back, as per the docs.

If anyone is using this and getting valid responses in local development mode (I'm using Ngrok to pipe my localhost as an endpoint for IPN to talk to), I'd appreciate some tips and next steps.

This might have something to do with PayPal going SHA256, but I have no idea how to directly debug this. Info that might be relevant: https://www.paypal-knowledge.com/infocenter/index?page=content&id=FAQ1766&expand=true&locale=en_US

How to pull individual IPN fields..??

I see in your examples that $event->getMessage() returns the raw IPN string. Is there any way to pull individual fields or is it left up to me to parse that string at that point?

IPN return Invalid

I am trying from yesterday to implement the IPN but it return INVALID all the time. Today I found this repo but it return invalid too.

This return in the OnInvalid function:
INVALID

payment_type=instant&payment_date=Thu+Jun+14+2018+00%3A50%3A21+GMT%2B0300+%28GTB+Daylight+Time%29&payment_status=Completed&address_status=confirmed&payer_status=verified&first_name=John&last_name=Smith&payer_email=buyer%40paypalsandbox.com&payer_id=TESTBUYERID01&address_name=John+Smith&address_country=United+States&address_country_code=US&address_zip=95131&address_state=CA&address_city=San+Jose&address_street=123+any+street&business=seller%40paypalsandbox.com&receiver_email=seller%40paypalsandbox.com&receiver_id=seller%40paypalsandbox.com&residence_country=US&item_name1=something&item_number1=AK-1234&tax=2.02&mc_currency=USD&mc_fee=0.44&mc_gross=12.34&mc_gross_1=12.34&mc_handling=2.06&mc_handling1=1.67&mc_shipping=3.02&mc_shipping1=1.02&txn_type=cart&txn_id=910839206&notify_version=2.1&custom=xyz123&invoice=abc1234&test_ipn=1&verify_sign=undefined

In IPN Simulator when I try to send a test I got this error: IPN was not sent, and the handshake was not verified. Review your information.

Using the ArrayListenerBuilder always returns Invalid

Hi Mike, thanks for the latest version of your listener.

I've been able to get the listener working with the InputStreamListenerBuilder and it returns a nice string of data from paypal. When I substitute in the ArrayListenerBuilder and log the $ipnMessage however, I always get an Invalid response from Paypal.

Would a verified response return an array as part of the message?

PHP8 deprecation Exception for http_build_query

Exception Message in PHP8.1:
http_build_query(): Passing null to parameter #2 ($numeric_prefix) of type string is deprecated in mike182uk/paypal-ipn-listener/src/Message.php on line 42

currently:

public function __toString() : string
    {
        return http_build_query($this->getAll(), null, '&');
    }

fix:

public function __toString() : string
    {
        return http_build_query($this->getAll(), '', '&');
    }

Updating user records after IPN handling

Hi, great work on this listener. After handling the IPN, how can I update the currently authorized user's db records to update their payment status? Auth::check() indicates that no user is logged in even though they are, I suspect this is because this IPN flow is separate from the user session created. Now I am left with a handled payment but no way to update the user record. Any suggestions?

Thanks.

Error in documentation

Hey,
Sorry for not doing pull request but noticed an error in your readme. In the sandbox section of it, it has this line "$listener = $listenerBuilder()->build();". The line causes an error because of the first set of parentheses.
Thanks.

Can't install with composer (dependency problem)

Composer output:

Problem 1
- Installation request for mike182uk/paypal-ipn-listener ~4.0 -> satisfiable by mike182uk/paypal-ipn-listener[v4.0.0].
- mike182uk/paypal-ipn-listener v4.0.0 requires guzzlehttp/guzzle ~5.2 -> no matching package found.

If you are still maintaining this package you should update guzzle req. and change the potential guzzle usage. Thanks

Feature request: recognize PayPal's fatal failures

We occasionally get these exceptions:

Unexpected verification status encountered: <html>
<body>
Fatal Failure <br>
</body>
</html>

Apparently PayPal is sometimes experiencing a minor downtime. It would be great, if the library could recognize these errors and have map them to a \Mdb\PayPal\Ipn\Event\MessageVerificationFailureEvent.

how to reditect on PayPal site for make payment?

means how to set payment information, payer_id, success, cancel and notify url , etc parameter? Please provide us the full code example. No any plugin available for go paypal site and then make payment.

pass IPN-Message to callback functions

Hi,

I think it would be a reasonable idea to pass either the IPN Message or the Listener instance to the callback functions (when they’re executed off the stack) so that you don’t have to import them explicitly every time (since there is often data in the IPN message that are required for further processing).

Dormilich

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.