Paypal IPN (Instant Payment Notification) plugin complete with PaypalHelper
I've created a PayPal IPN (Instant Payment Notification) plugin that includes a handy helper that will build your paypal buttons for you (Checkout, Add to Cart, Subscribe, and Donate). The Paypal IPN Plugin logs, and records any transaction made through your application and is completely customizable via its config and on the fly options. The Plugin is also very simple to switch between sandbox/live paypal.
The biggest benefit to using the PayPal IPN plugin is that it doesn't require a Website Payment Pro account (monthly charge) to use like the other PayPal implementations in the bakery. The IPN service is free from paypal; you simply have to enable it. Whenever paypal receives a transaction (complete or failure) your app will be notified.
This plugin will process Instant Payment Notifications sent from paypal, log it, and save a record in your database. The plugin provides an afterPaypalNotification callback you can use to apply post transaction logic to your application (set an order to paid, give a user premium access, etc...). This plugin also features a Paypal Helper to create various buttons to use with your paypal IPN service. I hope you find it useful.
Paypal IPN plugin. (Paypal Instant Payment Notification)
Version 1.4
Author: Nick Baker (nick@webtechnick.com)
Website: http://www.webtechnick.com
Browse, Download, or Checkout the Plugin.
Browse: http://projects.webtechnick.com/paypal_ipn
Download: http://projects.webtechnick.com/paypal_ipn.tar.gz
SVN: https://svn2.xp-dev.com/svn/nurvzy-paypal-ipn
I suggest reading the README.txt file included within the plugin. Installation is very straight forward.
Install:
1) Copy plugin into your /app/plugins/paypal_ipn directory
2) Run the /plugins/paypal_ipn/paypal_ipn.sql in your database.
3) Add the following into your /app/config/routes.php file (optional):
Paypal Setup:
1) I suggest you start a sandbox account at https://developer.paypal.com
2) Enable IPN in your account.
Administration: (optional) If you want to use the built in admin access to IPNs:
1) Make sure you're logged in as an Administrator via the Auth component.
2) Navigate to http://www.yoursite.com/paypal_ipn
Paypal Button Helper: (optional) if you plan on using the paypal helper for your PayNow or Subscribe Buttons
1) Update /paypal_ipn/config/paypal_ipn_config.php with your paypal information
2) Add 'PaypalIpn.Paypal' to your helpers list in app_controller.php:
$paypal->button(String tittle, Options array);
Examples:
You can seamlessly switch to your testing account by adding 'test' as an option key.
Test Example:
Example:
Paypal Button:
1) Use the PaypalHelper to generate your buttons for you. See Paypal Button Helper (above) for more.
- or -
2) Make sure to use notify_url set to
Example:
After Paypal Notification Callback:
After a notification is sent to your app, it is saved to your database and an afterPaypalNotification is called (if it exists). Here is where you can add logic to your app to do something with that specific payment.
1) Create a function in your /app/app_controller.php like so:
I hope you find it useful.
Please, if you like the plugin, find a bug or have a feature request, post a comment. =)
This plugin will process Instant Payment Notifications sent from paypal, log it, and save a record in your database. The plugin provides an afterPaypalNotification callback you can use to apply post transaction logic to your application (set an order to paid, give a user premium access, etc...). This plugin also features a Paypal Helper to create various buttons to use with your paypal IPN service. I hope you find it useful.
Paypal IPN plugin. (Paypal Instant Payment Notification)
Version 1.4
Author: Nick Baker (nick@webtechnick.com)
Website: http://www.webtechnick.com
Browse, Download, or Checkout the Plugin.
Browse: http://projects.webtechnick.com/paypal_ipn
Download: http://projects.webtechnick.com/paypal_ipn.tar.gz
SVN: https://svn2.xp-dev.com/svn/nurvzy-paypal-ipn
I suggest reading the README.txt file included within the plugin. Installation is very straight forward.
Install:
1) Copy plugin into your /app/plugins/paypal_ipn directory
2) Run the /plugins/paypal_ipn/paypal_ipn.sql in your database.
3) Add the following into your /app/config/routes.php file (optional):
Controller Class:
<?php
/* Paypal IPN plugin */
Router::connect('/paypal_ipn/process', array('plugin' => 'paypal_ipn', 'controller' => 'instant_payment_notifications', 'action' => 'process'));
/* Optional Routes, but nice for administration */
Router::connect('/paypal_ipn/edit/:id', array('admin' => true, 'plugin' => 'paypal_ipn', 'controller' => 'instant_payment_notifications', 'action' => 'edit'), array('id' => '[a-zA-Z0-9\-]+', 'pass' => array('id')));
Router::connect('/paypal_ipn/view/:id', array('admin' => true, 'plugin' => 'paypal_ipn', 'controller' => 'instant_payment_notifications', 'action' => 'view'), array('id' => '[a-zA-Z0-9\-]+', 'pass' => array('id')));
Router::connect('/paypal_ipn/delete/:id', array('admin' => true, 'plugin' => 'paypal_ipn', 'controller' => 'instant_payment_notifications', 'action' => 'delete'), array('id' => '[a-zA-Z0-9\-]+', 'pass' => array('id')));
Router::connect('/paypal_ipn/add', array('admin' => true, 'plugin' => 'paypal_ipn', 'controller' => 'instant_payment_notifications', 'action' => 'edit'));
Router::connect('/paypal_ipn', array('admin' => true, 'plugin' => 'paypal_ipn', 'controller' => 'instant_payment_notifications', 'action' => 'index'));/*
/* End Paypal IPN plugin */
?>
Paypal Setup:
1) I suggest you start a sandbox account at https://developer.paypal.com
2) Enable IPN in your account.
Administration: (optional) If you want to use the built in admin access to IPNs:
1) Make sure you're logged in as an Administrator via the Auth component.
2) Navigate to http://www.yoursite.com/paypal_ipn
Paypal Button Helper: (optional) if you plan on using the paypal helper for your PayNow or Subscribe Buttons
1) Update /paypal_ipn/config/paypal_ipn_config.php with your paypal information
2) Add 'PaypalIpn.Paypal' to your helpers list in app_controller.php:
Controller Class:
<?php
var $helpers = array('Html','Form','PaypalIpn.Paypal');
?>
3) Usage: (view the actual /paypal_ipn/views/helpers/paypal.php for more information)$paypal->button(String tittle, Options array);
Examples:
View Template:
<?php
//Pay Now Button
echo $paypal->button('Pay Now', array('amount' => '12.00', 'item_name' => 'test item'));
//Pay Now Button with Image
echo $paypal->button('pay_now.jpg', array('amount' => '12.00', 'item_name' => 'test item'));
//Subscribe Button
echo $paypal->button('Subscribe', array('type' => 'subscribe', 'amount' => '60.00', 'term' => 'month', 'period' => '2'));
//Donate Button
echo $paypal->button('Donate', array('type' => 'donate', 'amount' => '60.00'));
//Add To Cart
echo $paypal->button('Add To Cart', array('type' => 'addtocart', 'amount' => '15.00'));
?>
You can seamlessly switch to your testing account by adding 'test' as an option key.
Test Example:
View Template:
<?php echo $paypal->button('Pay Now', array('test' => true, 'amount' => '12.00', 'item_name' => 'test item')); ?>
You can also add any other valid PayPal element into the button options.Example:
View Template:
<?php echo $paypal->button('Pay Now', array('amount' => '12.00', 'item_name' => 'Stuff', 'return' => 'http://www.yoursite.com/thankyou')); ?>
Refer to PayPal.com for a complete list of button name-value pair elements you can use in your buttons.Paypal Button:
1) Use the PaypalHelper to generate your buttons for you. See Paypal Button Helper (above) for more.
- or -
2) Make sure to use notify_url set to
http://www.yoursite.com/paypal_ipn/process
in your paypal button.Example:
View Template:
<form action="https://www.paypal.com/cgi-bin/webscr" method="post">
...
...
<input type="hidden" name="notify_url" value="http://www.yoursite.com/paypal_ipn/process" />
...
</form>
After Paypal Notification Callback:
After a notification is sent to your app, it is saved to your database and an afterPaypalNotification is called (if it exists). Here is where you can add logic to your app to do something with that specific payment.
1) Create a function in your /app/app_controller.php like so:
Controller Class:
<?php
function afterPaypalNotification($txnId){
//Here is where you can implement code to apply the transaction to your app.
//for example, you could now mark an order as paid, a subscription, or give the user premium access.
//retrieve the transaction using the txnId passed and apply whatever logic your site needs.
$transaction = ClassRegistry::init('PaypalIpn.InstantPaymentNotification')->findById($txnId);
$this->log($transaction['InstantPaymentNotification']['id'], 'paypal');
//Tip: be sure to check the payment_status is complete because failure transactions
// are also saved to your database for review.
if($transaction['InstantPaymentNotification']['payment_status'] == 'Completed'){
//Yay! We have monies!
}
else {
//Oh no, better look at this transaction to determine what to do; like email a decline letter.
}
}
?>
I hope you find it useful.
Please, if you like the plugin, find a bug or have a feature request, post a comment. =)








$newData['cmd'] = '_notify-validate';
foreach ($data AS $key => $val) {
$newData[$key] = $val;
}
I don't think the _notify-validate was getting added to the beginning of the paramaters. I didn't verify that, but it didn't work until I made this change.
I have 3 separate environments (local, qa, production) where my application lives. My local environment serves the app through IIS (7.5 on Win7) whereas the QA and production versions are run off a pure LAMP stack. Naturally, QA and local environments use a debug level of 2 while production is set to the production default of 0. The plugin routing works great in my local and QA environment but fails with a 404 error in production. The only difference between QA and production is the debug level. Local has the added IIS-specific bonus of using App.baseUrl instead of default routing with mod_rewrite help.
Despite using the recommended routes from the latest README file, my production site continues to issue 404 errors when accessing paypal_ipn urls. Even the default CakePHP plugin urls fail (/plugin/controller/method). If you have any thoughts on debugging this issue (I've tried a few things, dug into Router a bit, tried to do custom error handling, etc), I would very much appreciate it.
Again, thank you for your contribution. I'll keep working on this issue and if I find a solution before I hear back, I'll update this thread.
- Paul
Currently, it will accept the payment, and the payment gets processed ok, but nothing goes in the database, and then it just shows the standard PayPal 'Thank You' screen - there's no button to go back to my site, and it doesn't do it automatically.
What I'd like to happen is for it to accept the payment, and then return to my site to say 'thank you' there; meanwhile, I want to set the order to 'complete', and trigger a few emails.
I've set
'notify_url' => 'http://www.mysite.co.uk/paypal_ipn/process',
in the config file, and enabled IPN in my paypal account. As I say, it's not even saving anything to the database. What else do I need to do?
When I use the test IPN tool on PayPal, it says it has worked.
For changing the order to 'complete' and so on, do I do that in 'afterPaypalNotification' in app_controller? I know how to do it, just not sure where.
Thanks!
Thanks again.
echo $paypal->button('Pay Now', array('test'=>true, 'amount' => '20.00', 'item_name' => 'User Upgrade'));
And also given notify_url in paypal_ipn/config/paypal_ipn_config.php , but when I complete the payment using my paypal sandbox account, it shows the "Test, thank you for your payment" page but don't getting payment data in database. The code is as it is you have given here.
echo $paypal->button('Pay Now', array('test'=>true, 'amount' => '20.00', 'item_name' => 'User Upgrade'));
And also given notify_url in paypal_ipn/config/paypal_ipn_config.php , but when I complete the payment using my paypal sandbox account, it shows the "Test, thank you for your payment" page but don't getting payment data in database. The code is as it is you have given here.
[end quote]
I cannot either. Has anyone solved this ?
Thanks again!
How do you validate a user befor and after the transaction?
More details:
If a user press the "Bay now" button do you add some id, or how do you now if you get the call back from paypal that this user has executed the transaction?
Multiple Items
-------------------
By doing an update to the latest version you'll now need to migrate your databased to the newest release via this command:
cake schema run create -path plugins/paypal_ipn/config/sql -name itemsWith this, you'll now have access to all the items, tax, shipping, etc details for each paypal item a customer purchases via the paypal cart.
This is useful for making paypal your full featured cart/merchant service.
-------
Single items, Subscriptions, and Donations work just as they did before.
You can download the latest version from my website: http://projects.webtechnick.com/paypal_ipn.tar.gz
Or you can checkout/update via my svn
svn co http://svn2.xp-dev.com/svn/nurvzy-paypal-ipn paypal_ipnExample usage:
PHP Snippet:
<?phpecho $paypal->button('Checkout', array(
'type' => 'cart',
'items' => array(
array('item_name' => 'Item 1', 'amount' => '120', 'quantity' => 2, 'item_number' => '1234'),
array('item_name' => 'Item 2', 'amount' => '50'),
array('item_name' => 'Item 3', 'amount' => '80', 'quantity' => 3),
)
));
?>
You can review my API at: http://projects.webtechnick.com/docs/paypal_ipn
Read more about the changes in v3.5 here: http://www.webtechnick.com/blogs/view/225/Paypal_IPN_Plugin_v3_5_Email_and_Multi_Items
Thanks for the inspiration,
Nick
Does it possible ??
I tried like below, but it does not work.
please help me.
PHP Snippet:
<?phpecho $paypal->button('Checkout', array(
'type' => 'cart',
'items' => array(
array('type' => 'subscribe', 'amount' => '500.00', 'term' => 'month', 'period' => '3'),
array('type' => 'subscribe', 'amount' => '600.00', 'term' => 'month', 'period' => '2'),
array('type' => 'subscribe', 'amount' => '700.00', 'term' => 'month', 'period' => '1'),
)
));
?>
I've done the digging for you: https://cms.paypal.com/us/cgi-bin/?&cmd=_render-content&content_ID=developer/e_howto_html_cart_upload
Currently the paypal helper including with my plugin doesn't handle cart uploads but it looks pretty simple to implement in a future release. I'll add that to the 'wish list'.
For now, you'll need to do what you want by hand. The link above is a pretty good tutorial on how to upload an existing cart into a paypal cart for purchasing. As I said before, just make sure to set the 'notify_url' name in the form somewhere and you should be all set.
Hope that helps,
Nick
The paypal helper is completely optional, the plugin will process the IPNs for you if you choose to use the helper, do it all by hand, or build your buttons via the paypal web developer's toolkit.
If you're not going to use the helper, just make sure you have 'notify_url' set in your html form somewhere like so:
<input type="hidden" name="notify_url" value="http://www.yoursite.com/paypal_ipn/process" />You can even tell your paypal account to always send IPN data to a specific URL no matter where the payment came from. By doing that you can skip all the notify_url steps.
As long as you somewhere tell paypal to send back IPN data to the process action in the plugin, the plugin will receive, parse, log, and execute any logic in your afterPaypalNotification callback function for you.
The helper is just a convenience, use whatever you're comfortable with.
Hope that helps,
Nick
how I use the "$paypal->button();" if the item more than one?
there an invoice to confirm so I send all data item at that invoice and the paypal can count the all price. so not one button for one item.
if I use html form, the target site not to paypal transaction site like an always.
Thanks,
johan
I have a problem if I send multi item to paypal.
I use $paypal->button(); to send the data.
if I use Html form there are problem when the data send.
will the plugin knowing if I use html form??
johan
I hope this gonna work to my code.. :P
johan
You can checkout the plugin as well:
svn co http://svn2.xp-dev.com/svn/nurvzy-paypal-ipn
please help me...
thanks
johan
The configuration is completely optional and is only for the paypal helper included with this plugin. This plugin will handle IPN data for you automatically -- as long as you point paypal to use 'notify_url' to your process action. I've included the helper to go along with the plugin so that all your paypal buttons will direct to your paypal process action, but you don't have to use the paypal helper if you don't want to. If you don't use the helper, you'll need to follow the paypal IPN manual from your paypal account and direct any ipn data postback from paypal to the plugin's process action which will take the data, verify it, and then save it to your database.
Configuration Location:
/paypal_ipn/config/paypal_ipn_config.php
In the config file there are two arrays -- settings, and test_settings. You'll need to update these options so you don't have to pass them into the helper each time.
/************
* Each settings key coresponds to the Paypal API. Review www.paypal.com for more.
*/
var $settings = array(
'business' => 'live_email@paypal.com', //Your Paypal email account
'server' => 'https://www.paypal.com', //Main paypal server.
'notify_url' => 'http://www.yoursite.com/paypal_ipn/process', //Set this to the process path of your paypal_ipn::instant_payment_notification::process action
'currency_code' => 'USD', //Currency
'lc' => 'US', //Locality
'item_name' => 'Paypal_IPN', //Default item name.
'amount' => '15.00' //Default item amount.
);
You'll need to update/configure each setting key=>value pair to your own liking. I suggest in the very least updating 'business' with your paypal email and 'notify_url' with the full URL to your paypal_ipn::process action (as specified in the article). That way you wont have to specify those options in the options array whenever you call $paypal->button();
You can even add more key value pairs as specified by the paypal API, review paypal.com for more.
I hope that helps,
Nick
I just see config for admin area, not for IPN data. When paypal return a data, which is file configured for save data into mysql?
svn co http://svn2.xp-dev.com/svn/nurvzy-paypal-ipn/It's public, I was able to do an anon checkout just now.
If you're still having problems let me know.
My bad dude - I read your previous posts wrong. It works. sorry to waste ur time.
NEW SVN:
http://svn2.xp-dev.com/svn/nurvzy-paypal-ipnIf you've currently checked out this plugin you can use svn switch to change the repo to get the latest updates.
svn switch --relocate https://svn2.xp-dev.com/svn/nurvzy-paypal-ipn http://svn2.xp-dev.com/svn/nurvzy-paypal-ipnIf you have any problems, let me know.
$ cd app/plugins/paypal_ipn
$ ls
config/
controllers/
models/
paypal_ipn_app_controller.php
paypal_ipn_app_model.php
paypal_ipn.sql
README.txt
views/
Make sure you have the database setup the way I've described above.
If you installed the app via svn, I recommend doing an
svn updateto get the latest version which I've just released that includes a schema that will create the required table for you via a cake schema run command.cake schema run create -path plugins/paypal_ipn/config/sql -name ipnThat will drop and create the required database.
I hope that helps,
Nick
Error: The requested address '/admin/paypal_ipn' was not found on this server. or if i go directly to site.com/paypal_ipn i get:
Fatal error: Call to undefined function loadmodel() in /cake/libs/model/db_acl.php on line 114
Set up as:
plugins/
/paypal_ipn
/config
/controllers
/models
/views
paypal..controller.php
paypal..model.php
Copied your routes...tried a few variations but keep gettingthe same error. Ideas where I may have gone wrong?
It looked like your code would benefit from being packaged in that way. Think you'd be interested in discussing a good standard that more developers can start to work with? We have a small and slowly growing channel: #cakephp-cupcakes if you want to stop by and help us develop some of the pieces to the plugin.
I'm gonna try merging your code myself, it looks very clean and straightforwards, however if you wish to help out you'd probably know how to refactor it faster.
Actually, as long as you don't have a controller in your app called 'PaypalIpn', you don't have to double up the URL like that. If Cake doesn't find a controller named PaypalIpn, and it sees a plugin called that, it will automatically load the plugin controller with the same name.
Hi, well those routes are optional. I put them in the article because I know most people would find it useful. However I named that way because I have a poor memory and personally like long descriptive names.
However, renaming the controller to PaypalIpnController may not give you what you think without a custom route; you would have navigate to /paypal_ipn/paypal_ipn/:action/:id (once for plugin, once for controller) which just reads odd.
Arguably one might rename the plugin 'paypal' and the controller 'IpnController' so the url would be /paypal/ipn/:action/:id which looks clean but as I said earlier, long descriptive names are my personal flavor. However, I also realize most people don't like long names. Thus, I added an example of custom routes for users to use if they so desire.
Another easier way you might like is:
Router::connect('/paypal_ipn/:action/*', array('admin' => 'true', 'plugin' => 'paypal_ipn', 'controller' => 'instant_payment_notifications', 'action' => 'index'));Which would combine all the "nasty routes" into one aliased route.
Routes are cool. =)
2) Currently support for datasources in plugins isn't good enough so i see your point.
We discussed things on IRC but just wanted to put short comments here for easy reference.
1) The $data['test_ipn'] is sent from the paypal call back, not from the user. Paypal automatically sends a key ['test_ipn'] => 1 if the transaction occured on its sandbox server. It has nothing to do with the user setting an option in the data that is passed to isValid. In my plugin you'll find the data passed to the datasource is purely $_POST, nothing more -- and it will work with both the test and live automatically.
2) Datasources in Plugins are poorly implemented. I tried implementing it the normal way (like the link provided) but that wouldn't work. To get a datasource to work in a plugin I had to take inspiration from matt curry:
http://www.pseudocoder.com/archives/2009/02/10/yahoo-search-boss-as-a-cakephp-plugin/
Matt had to import the file directly. But again, Matt Curry needed a config passed into the datasource, I do not, as all the data needed to validate is given to me by paypal. I don't need credentials, I don't need the user to specify if its test or not, Paypal does all that work for me. All I needed is to import the file manually, because CM doesn't handle plugin datasources, instantiate it, and call the isValid method with the $_POST paypal gave me. Which is what I've done in the cleanest way I know how.
If there's a cleaner way to do it I'm all ears but the link you provided me is great for normal datasources, but doesn't seem to apply to Plugin-Datasources. I'll be hanging around the IRC.
Once again, thank you for all your time and feedback.
Please contact me or another experienced user on irc if you need more help.
Hi, Thanks again for your feedback.
Ahh, yes, thank you for explaining. I agree. I've followed all your suggestions. I've created a datasource for the verification post-back and now utilize the HttpSocket core lib -- which does indeed make the code MUCH cleaner. Thank you again for your feedback.
I hope these updates are to your satisfaction.
Thanks,
Nick
Thank you for your feedback. You may or may not be familiar with the Instant Payment Notification service paypal offers. It's not an api in which I can use to gain information, rather its a url callback that paypal will send POST data to upon a successful transaction. That callback needs to access your cakePHP app from an outside source. A controller action is exactly where it belongs.
The paypal nvp api service is completely different than the paypal IPN service. The paypal NVP api allows a user to directly access and make payments to a paypal account withough actually going to the paypal site. The NVP api is NOT free. A user is required to use SSL and pay a monthly fee to use this service live in paypal.
However, the IPN is a free service offered by paypal for users who don't mind the flow of payment jumping from their site to paypals. After completion paypal notifies your app of the payment. This plugin handles that notification the best way possible.
IRC discussion: http://irc.cakephp.org/logs/link/908711#message908721
Update: I've updated the plugin to be of fat-model-skinny-controller paradigm.
Comments are closed for articles over a year old