PayPal IPN Listener
A PayPal IPN (Instant Payment Notification) listener for PHP
Index
Prerequisites
- PHP ^7.2
- A good understanding of how the PayPal Instant Payment Notification system works. See https://developer.paypal.com/webapps/developer/docs/classic/ipn/integration-guide/IPNIntro/
Installation
composer require dansmaculotte/paypal-ipn-listener
Architecture
This package is made up of several components that work together:
Listener
- Listens for and processes the IPN messagesVerifier
- Verifies the IPN message with PayPalService
- Communicates with PayPalMessage
- Wrapper around the IPN messageMessageFactory
- Creates a new message instance from a data sourceEventDispatcher
- 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:
DansMaCulotte\PayPal\Ipn\MessageFactory\InputStreamMessageFactory
- Creates a message from thephp://input
streamDansMaCulotte\PayPal\Ipn\MessageFactory\ArrayMessageFactory
- Creates a message from an array passed to thesetData
method
This package provides 1 service:
DansMaCulotte\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:
DansMaCulotte\PayPal\Ipn\ListenerBuilder\Guzzle\InputStreamListenerBuilder
- Builds a listener using the guzzle service and the input stream message factoryDansMaCulotte\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 DansMaCulotte\PayPal\Ipn\ListenerBuilder\Guzzle\InputStreamListenerBuilder as ListenerBuilder;
$listener = (new ListenerBuilder)->build();
Building up the listener manually
use GuzzleHttp\Client;
use DansMaCulotte\PayPal\Ipn\InputStream;
use DansMaCulotte\PayPal\Ipn\Listener;
use DansMaCulotte\PayPal\Ipn\MessageFactory\InputStreamMessageFactory;
use DansMaCulotte\PayPal\Ipn\Service\GuzzleService;
use DansMaCulotte\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 DansMaCulotte\PayPal\Ipn\Event\MessageVerifiedEvent;
use DansMaCulotte\PayPal\Ipn\Event\MessageInvalidEvent;
use DansMaCulotte\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:
use DansMaCulotte\PayPal\Ipn\Event\MessageVerifiedEvent;
class IpnProcessor
{
public function onVerified(MessageVerifiedEvent $event)
{
$message = $event->getMessage();
// ...
}
}
$listener->onVerified([new Processor, 'onVerified']);
use DansMaCulotte\PayPal\Ipn\Event\MessageVerifiedEvent;
class IpnProcessor
{
public static function onVerified(MessageVerifiedEvent $event)
{
$message = $event->getMessage();
// ...
}
}
$listener->onVerified(['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 DansMaCulotte\PayPal\Ipn\Event\MessageVerifiedEvent;
use DansMaCulotte\PayPal\Ipn\Event\MessageInvalidEvent;
use DansMaCulotte\PayPal\Ipn\Event\MessageVerificationFailureEvent;
use DansMaCulotte\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:
use DansMaCulotte\PayPal\Ipn\ListenerBuilder\Guzzle\InputStreamListenerBuilder as ListenerBuilder;
$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 DansMaCulotte\PayPal\Ipn\Service
.
To create your own message factory you must implement DansMaCulotte\PayPal\Ipn\MessageFactory
.
To create your own listener builder it is best to extend DansMaCulotte\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 DansMaCulotte\PayPal\Ipn\ListenerBuilder\ModeDependentServiceEndpoint
trait (see DansMaCulotte\PayPal\Ipn\ListenerBuilder\GuzzleListenerBuilder
for usage example).
Notes
Testing
PayPal provides an IPN simulator here: https://developer.paypal.com/webapps/developer/applications/ipn_simulator