Introduction
Paypal is perhaps the easiest way to send/receive money online and WordPress is perhaps the most popular blogging platform out there. Wouldn’t it be great if there was a way we could integrate them together?
There are currently a number of plugins available that allow you use WordPress with paypal for invoicing customers and such. However if you want to do anything custom you are going to need to process IPNs (Instant Payment Notifications) yourself.
Problem
One could easily just put a php file on their server to process the IPN independently of WordPress. However if you want to interact with your database at all you have three basic options:
- Hardcode your database settings in your IPN processor.
- include your wp-config.php and pull the database settings from that manually.
- Integrate with WordPress so you can use the $wpdb object in your plugin ( I assume that you are developing your own larger plugin and paypal support is part of that).
Hardcoding your settings is fine for one off scripts, but if you change servers you could easily forget to update your settings breaking your website or worse: stopping people from giving you money!
Including your wp-config.php has a number of drawbacks. It makes certain assumptions that break your software. Namely it assumes that wp-config.php is a directory or two up in ../../wp-config.php or something of that nature. As WordPress allows you to have you config file in other locations e.g. not ../../wp-config.php this is very risky business.
Integrating with WordPress allows the user to use the plugin without any extra configuration to access their database. If WordPress can access the database so can we. We also get access to some wordpress convenience methods – always a plus.
To be able to access the $wpdb object requires WordPress initialize and for the IPN somehow call our plugin. Obviously telling Paypal to send its requests to /wp-content/plugins/my_plugin/ipn_listener.php is a bad idea ( and I don’t think it would even work).
Solution
Let’s ponder for a moment how WordPress processes our requests. Usually we see the exact details of the request and they look pretty thanks to mod_rewrite. A typical request would look something like
http://example.com?/2009/08/01/delicious-cheese
Or as WordPress sees it:
http://example.com?year=2009&month=08&day=01&name=delicious-cheese.
Understanding this, my solution was to build upon this and direct the IPN to index.php with some extra variables set.
http://www.example.com?my_plugin=paypal.
Now that we’ve got that far. Next we need to tell WordPress to be on the look out and accept more variable names from the request. We do this by adding a filter to query_vars. We have to do this because WordPress ignores them if we don’t in an effort to prevent processing unknown and possibly malicious inputs.1
function my_plugin_query_vars($vars) { // add my_plugin to the valid list of variables $new_vars = array('my_plugin'); $vars = $new_vars + vars; return $vars; } add_filter('query_vars', 'my_plugin_query_vars');
Now that WordPress accepts GET variables with the name my_plugin it’s time to do something with that. We add an action on parse_request which gives us first change to parse given request before WordPress does.
Notice that we check to make sure that our specified variable exists and is the value we are expecting. If we do not do this we end up processing all requests. 2 Once we have confirmed that both our GET variable and is the name we expect we call the function in our plugin that processes the IPN.
function muy_plugin_parse_request($wp) { // only process requests with "my_plugin=paypal" if (array_key_exists('my_plugin', $wp->query_vars) && $wp->query_vars['my_plugin'] == 'paypal') { my_plugin_proccess_paypal_ipn($wp); } } add_action('parse_request', 'my_plugin_parse_request');
As per processing the IPN you can start by simply copy/pasting the Paypal sample code in the function. You then access your database using the $wpdb object. Don’t forget that it’s global so you must first
global $wpdb;
before you can access it.
Update
I thought we were finished but we aren’t. The above works fine with Paypal’s Tester IPN as it doesn’t check URLs. Paypal does not allow you to active your IPN in Paypal if a given URL has GET arguments.
We can fix this with just a little more code and the magic of mod_rewrite. First you will need to enable mod_rewrite in WordPress (Options -> Permalinks) by selecting one that is non-default e.g. Day and name.
Then we need to add a hook in WordPress rewrite system and then create a rule that tell WordPress to forward all requests to
http://example.com/my_plugin/paypal
to
http://example.com/?my_plugin=paypal
The code to do this is as follows:
add_action('generate_rewrite_rules', 'my_plugin_rewrite_rules'); function my_plugin_rewrite_rules( $wp_rewrite ) { $new_rules = array('my_plugin/paypal' => 'index.php?my_plugin=paypal'); $wp_rewrite->rules = $new_rules + $wp_rewrite->rules; }
Notes
1 If you wish to access the paypal and other variables (both GET and POST) via $wp->query_vars you must declare them in your query_vars filter as well. Since there are loads of them with paypal this may or may not be worth the effort.
2 It might not hurt to make the value a bit more obscure e.g. ei190A0F (random gibberish). Another side bonus is you could also add support for other services quite easily.
Wow, this looks like it’ll do it. Wish there was a plugin for it…
I actually made an events registration plugin for WordPress that captures the data sent from the PayPal IPN service when someone registers and pays for an event.
Here is the plugin home page on my site: http://shoultes.net/wordpress-events-registration-with-paypal-ipn/
Seth: Your free plugin gives error.