Easily Integrate PayPal IPN in Yii Framework

Screen Shot 2014-06-26 at 2.52.33 AM

Wonder how to integrate paypal IPN in Yii framework? Are you searching for Yii extension to integrate paypal IPN into your application? Well, i used to do that until i got fed up with reading all the documentation all excessive code dated by 2012 that hasn't been update for months! So i decided to just integrate paypal ipn alone into my Yii framework and guess what, it was so dead simple that it simply doesn't need any extension to began with.

PayPal IPN Tutorial

Before i began writing this tutorial, you should really understand what is paypal ipn is and why you are integrating it. Paypal IPN refers PayPal's Instant Payment Notification (IPN) which is a service provides by paypal to instantly notify your application whenever there is a payment made to you. And Micah Carrick had explained it perfectly on how to integrate it on a PHP level.

Coding Paypal IPN in Yii Framework

Here we go, firstly, you will need to get the code PHP-PayPal-IPN from github. The library provides you with a simple integration to Paypal IPN. Download the code and placed into your project/protected/vendors/PHP-PayPal-IPN and rename the ipnlistener.php to IpnListener.php.

Next, you will need to create a controller to listen to incoming ipn request and paste the following code into your listener method.

class PaymentController extends Controller{
        public function actionListen(){
                // intantiate the IPN listener
                Yii::import("application.vendors.PHP-PayPal-IPN.*", true);
                $listener = new IpnListener();

                // tell the IPN listener to use the PayPal test sandbox
                $listener->use_sandbox = true;

                // try to process the IPN POST
                try {
                        $verified = $listener->processIpn();
                } catch (Exception $e) {
                        // error_log($e->getMessage());
                        Yii::log("IPN FAILED! returned an error: $e",CLogger::LEVEL_ERROR);

                // TODO: Handle IPN Response here
                if ($verified) {
                        $model = new Transaction('search');
                        // TODO: Implement additional fraud checks and MySQL storage
                        mail('[email protected]', 'Valid IPN', $listener->getTextReport());
                } else {
                        // manually investigate the invalid IPN
                        mail('[email protected]', 'Invalid IPN', $listener->getTextReport());


Do take note that you will need to give your method public access in order for paypal ipn services to reach it. Now, take note that the model i am using here belongs to my own, you can create your own and save every single data passed in by paypal to your database like what i did but do make sure that the naming convention are the same for both paypal $POST request and your own table fields.

Test Paypal IPN

In order to test your Paypal IPN request, you will need to create a sandbox paypal account and use its Instant Payment Notification (IPN) Simulator as show below,

Screen Shot 2014-06-26 at 2.52.33 AM

All you need to do is to scroll it all the way down and click "Send IPN". And you should be able to test your Paypal ipn listener! And guess what? You don't even need to create any sandbox paypal account to create a paypal IPN listener!

Yii renderPartial duplication solution

Its been almost a year since i found something interested to write since im busy working and didn't really get the time to write some useful stuff. Today i came across a well known issue in Yii solution that caused duplicate js request whenever we are doing ajax stuff with renderPartial. Here's a small js script that will save our asses.

                global: true,
                dataFilter: function(data,type){
                        //  only 'text' and 'html' dataType should be filtered
                        if (type && type != "html" && type != "text")
                                return data;
                        var selector = 'script[src]';
                        // get loaded scripts from DOM the first time we execute.
                        if (!$._loadedScripts) 
                                $._loadedScripts = {};
                                $._dataHolder = $(document.createElement('div'));
                                var loadedScripts = $(document).find(selector);
                                //fetching scripts from the DOM
                                for (var i = 0, len = loadedScripts.length; i < len; i++) 
                                        $._loadedScripts[loadedScripts[i].src] = 1;
                        //$._dataHolder.html(data) does not work
                        $._dataHolder[0].innerHTML = data;
                        // iterate over new scripts and remove if source is already in DOM:
                        var incomingScripts = $($._dataHolder).find(selector);
                        for (var i = 0, len = incomingScripts.length; i < len; i++)
                                if ($._loadedScripts[incomingScripts[i].src] && incomingScripts[i].src.indexOf('search') == -1)
                                        $._loadedScripts[incomingScripts[i].src] = 1;
                        return $._dataHolder[0].innerHTML;

Credit goes to Yii forum contributor! but not all scenario will work, still, its good enough at least

Yii CClientScript Disable RegisterScript

I though this might be useful since its not widely spread yet. There will be times when you want to disable some scripts on CClientScript so that your ajax or JSON will print properly. In this case, depending on what you want to disable, these methods might be helpful.

Disable JavaScript or CSS files in CClientScript

Well, you if want to disable JavaScript or CSS files that were included via registerCssFile or registerScriptFile you can fire the following code into your filer or anywhere your action is being fired

        $cs = Yii::app()->clientScript;
        $cs->scriptMap['jquery.js'] = false;
        $cs->scriptMap['bootstrap.min.css'] = false;
        $cs->scriptMap['bootstrap.min.js']  = false;
        $cs->scriptMap['bootstrap-yii.css'] = false;

scriptMap will disable the file mentioned (example jquery,js) and replace with 'false', this is similar to how you would go about optimizing js and css scripts on Yii as well 😉

Removing CSS or JavaScript code in CClientScript

this will be a little bit tougher than you would imagine since the array 'scripts' which suppose to be available isn't really 'public' but you can still remove the code via the following,

Yii::app()->clientScript->registerScript('mykey', "jQuery('{$selector}').{$name}({$options});");

basically, to remove the custom script i have wrote above, i will fire something like this

Yii::app()->clientScript->registerScript('mykey', false);

basically i am overwriting whatever i have registered and this will remove the custom script that i have written. Neat isn't it 😉

Remove every single custom scripts on CClientScript

if the above isn't what you were looking for, may be you are like me who manage to remove custom scripts using the above method but the script tags still appear, in this case, i fire the following code to stop my headache

        $cs = Yii::app()->clientScript;

since we can't make 'hasScripts' to be false (which is the reason why the script tags still appear), we will reset all the calling scripts instead. but this won't always work as neat as you would want to but its a good solution so far.

Disable JavaScripts entirely on CClientScript

Well, its so irritating that i wanted everything to be disable on cclientscript, in this case, use the following,

        $cs = Yii::app()->clientScript;
        $cs->enableJavaScript = false;

This will ensure everything is disabled.

This should be useful for people working on Ajax, restful and JSON implementation on Yii 😉

Disable Yii Log on Action Controller

Well, i found something interesting yesterday that might be useful for Yii developer when they deal with restful api or JSON output with Yii framework. Sometimes it is good to disable Log output in order to return proper JSON or restful api calls. In that case, you can try to do the following,

        foreach (Yii::app()->log->routes as $route)
                if ($route instanceof CWebLogRoute || $route instanceof CFileLogRoute || $route instanceof YiiDebugToolbarRoute)
                        $route->enabled = false;

However, if you still seeing your JSON or Restful api output being 'unclean', this is most likely caused by preload options on your config.php file. Preload will always run earlier than whatever you have defined in your action controller. In order to bypass this, the only way is to dive into these extensions and provide certain validation to prevent it from running unnecessary. Sometimes, you will see facebook api appending javascript and meta tag into your json call, in that case, you can add in a new 'enabled' property on your facebook extension and disabled upon your action controller initialization. You can place the following code below to shut up facebook entirely upon running your action controller method.

Yii::app()->facebook->enabled = false;

the enabled property have to be written by you as a new property on facebook extension class file. And the other condition to prevent certain method to run will also have to be written by you since Yii doesn't provide a more graceful method to disable output before preload or extensions that are not managed by them

Add additional value before ajax call when using Yii CAutoComplete

If you are like me who wish to add an additional value into Yii CAutoComplete and wondering how this can be done, it is pretty simple thanks to Ron Lavie. Assuming, you have the default ajax call on your controller which is "$_GET['q']", now, you would like to get a new value to be included into your controller called "$_GET['f']" and you are wondering what to do in order to get your value into Yii CAutoComplete before it fires ajax call to your controller, this is how you should do it.

'extraParams'=>array('f'=>"js:function(){return $(\"#f\").val();}"),

Using the extraParams, we can add in additional field before an ajax call to the controller. Here's a full illustration.

             'value'=> $text,
             'id' => 'nanny_name',
                         //replace controller/action with real ids
             'max'=>10, //specifies the max number of items to display
                         //specifies the number of chars that must be entered
                         //before autocomplete initiates a lookup
             'delay'=>500, //number of milliseconds before lookup occurs
             'matchCase'=>false, //match case when performing a lookup?
                         //any additional html attributes that go inside of
                         //the input field can be defined here
             'extraParams'=>array('f'=>"js:function(){return $(\"#f\").val();}"),                                                                                   

Now, whenever something is filled up on this CAutoComplete, it will search for the value in id "f" and included into variable "f" before firing up the ajax call to your controller. :)