Plugin Licenses, Upsells, and Add-ons

If you can't obfuscate your code, or have an API just to check a license, how are you supposed to earn any money with a plugin in the WordPress repository? Stop thinking about hiding your code and start extending it.

This post is dedicated to WPEngine, who donated to help me get to WCSF. While I don’t use them, I think that if you’ve outgrown and aren’t quite ready to host everything yourself, but you still want the plugins and themes, then you should check these guys out. They aren’t cheap, but then again, I firmly believe in paying for what’s important.

Read the comments before you comment. Otto haz the smartz.

Phone HomeOne of the many rules of hosted plugins is you can’t phone home. Actually you can, and the rule really is ‘Don’t phone home without a damn good reason.’ To use Akismet as an example, it phones home with information to help verify who posted on your site, and are they a damn dirty spammer. That’s a damn good reason. But phoning home to check “Did Bob pay for a license?” is not. That’s considered abuse of the “serviceware” guideline, and essentially making an API just to make sure a license is okay isn’t okay. Now making money on your plugins is an awesome thing. But when your code is open source and anyone can see it, how do you keep people honest?

To get down to brass tacks, I’m going to take a little jounrey the wrong way, before I get to some suggestions on how you can provide ‘free’ and ‘pay’ versions of a plugin on the WordPress repository, and not cause any guideline issues.

Let’s start out with the most common way people restrict you: A license key. If you put in a plain license key check like this, it’s easy to crack:

if ( $license == "yes" ) { // Licensed! }

Okay, you say, I want to encrypt things so that I tell someone ‘your license is ipstenuisreallyneatsheismadeofturtlemeat’ but when I look at my code, it shows ‘774ffc4efce8da294dff77f35f75df98‘ instead (that’s md5(ipstenuisreallyneatsheismadeofturtlemeat) as it happens). Wait. We can’t encrpyt code. Or rather, we can’t include encrypted code in a plugin, it’s against the no-obfuscation rule. We’d want to decrypt that instead. So I give you the code string instead and run it this way instead (Just pretend that $options['license'] is a site option.):

if ( md5(ipstenuisreallyneatsheismadeofturtlemeat) == $options['license'] ) { // Licensed! }

But then I have the problem of anyone can just look and see what’s going on again. You could go the extra step like putting ipstenuisreallyneatsheismadeofturtlemeat in a file and then pull off something like this:

$md5file = file_get_contents("md5file.txt");
if (md5_file("test.txt") == $options['license'] )

No LicenseIs this easily decrypted? Yes. Is this easily circumvented by editing the code and removing the if? Again, yes. In fact the only way to really do this would be to use an API on your server to check the validity of the license (which you can’t do if you want to be hosted on anyway – no APIs just to check licenses), and even then I can strip mine your plugin and remove all checks like that. So why bother? Because you want to make a living on your code, and that’s certainly a fair-go! But as Otto rightly says, we can’t stop piracy, so why are we trying? DRM doesn’t work, and reverse engineering hasn’t proven sustainable. Maybe we’re building the wrong mousetrap.

If we throw the code solutions out the window, because we know they won’t work, where are we left? The next most common thing I see is people offering two plugins. A free, totally open GPL one on the WordPress repository and then a version behind a pay-wall that you would ‘replace’ that free one with. For example, I have a Rickroll plugin, and let’s say I wanted to make a Rickroll Pro version that let you change the video to anything you want, just put in the YouTube URL. I would have a settings page on my free version that pretty much says “Hi, if you want to change the video, visit to download.” And now I have to code Rickroll Pro to check if Rickroll (free) is installed and active, and refuse to activate if so. Furthermore, my users have to be told to delete the free Rickroll.

You know what? That’s a pain in the butt. What if instead I coded a Rickroll Pro add-on. No, I don’t mean ‘add this file to your plugin’ but ‘Install this second plugin, which will add functionality to Rickroll.’

It’s a second plugin, yes, but now I can have Rickroll free look for Rickroll pro. Not active? The settings page (which I would keep in Rickroll free) would tell you ‘Hey, you don’t have Rickroll pro! Install it and get more things!’ or ‘Hi, you have Rickroll pro installed by not active. Don’t you know it’ll never give you up? Activate it and have fun!’

Now the code muscle becomes a question of ‘How do I ensure my dependency checks work?’ First, Scribu wrote an awesome plugin dependency plugin, and the only flaw with it, is you’d have to install a third plugin. We don’t want that here, since yet another plugin is problematic. But looking back, that code grew out of a trac ticket about handling plugin dependencies. Now there’s a nice way to check: is_plugin_active()

if (!is_plugin_active('rickroll/rickroll.php')) {
    // do not activate. Provide message why.

ProtectedYou could go to town with the checks in there. Like if the plugin isn’t active, deactivate the child and so on and so forth. I’m not going to write it all for you (though Otto wrote a lot about it for themes)

Now going back in your parent plugin, you can run the same check:

if (!is_plugin_active('rickroll-pro/rickroll.php')) {
    // Rickroll pro isn't active, prompt the user to buy it.
} else {
   // Include rickroll-pro/adminsettings.php so they can use it

The one last thought I had on this was how to handle pro upgrades. Since I don’t like to upgrade a plugin a lot unless I have to, I’d make it an ‘upgrade both’. In Rickroll Pro I’d set a version constant, and then in that check to see if it’s active call, reference that version. So Rickroll, after verifying Rickroll Pro is active, would come back and say ‘My current supported version of Rickroll Pro is 1.5 and the constant is set to 1.0. You should upgrade!’ Then every time I write a new version of Rickroll Pro, I’d update Rickroll to point to the new version, and when they upgrade from WP, they would get notified about Rickroll Pro needing an update too.

Probably not the most efficient or effective way about it, but the other option is a self hosted plugin update API.

Bear in mind, because of GPL, all these hoops and ladders can be circumvented. Your plugin can and will be taken away for free. Don’t fight the pirates with registration circuses, and limit the weight of your code by selling the right thing. It’s a strange idea to think that giving your code away for free will help you earn money, but at the very least, not fighting against the pirates will give you time to write better, more secure, code. And that certainly will earn you more money. Then sell your support, because that time is money.


  1. Another thought I had which is similar to selling an add on but without adding another plugin is to load your settings from a json string option value. So you have basic settings and then when the user upgrades you provide a json string which then can then loaded into the plugin which unlocks new settings. This way your base plugin can be maintained in the WP Repo. Again this string can be easily passed around bur so can an addon. Just a thought.

    • Both? 😉

      A json string may edge on obfuscation, depending on how you put it in. Though Otto and I chatted elsewhere, and his idea below of essentially not ‘unlocking’ the free version, but using the add-on to … add on, is probably the friendliest we can get with the repository as is.

      Now admittedly you get the added responsibility of hosting the ‘pro’ version, and making sure you supply it in a nice locked down way so not everyone can just steal it, but that’s a different color of headache.

    • Though Otto and I chatted elsewhere, and his idea below of essentially not ‘unlocking’ the free version, but using the add-on to … add on, is probably the friendliest we can get with the repository as is.

      Yeah the more I think about it, add ons would be the way to go. I actually think Otto invented the Add-On model with his Twitter and Facebook plugins? Though he did not sell them.

  2. A somewhat smarter way to go about it is to put *only* the pro features in the pro plugin, and then have it add those features into the free plugin using hooks you’ve added into the free plugin itself.

    The free plugin could have do_action(‘plugin_settings_page’) and apply_filters(‘plugin_rickroll_video’) and such in it, and the pro plugin could then just add the settings fields to the page, and use the filter to change the video, and whatever. You don’t necessarily need the dependency checks that way.

    If a plugin was outputting a message like this, then it wouldn’t need to check if other plugins were activated:
    echo apply_filters(‘plugin_upsell’,’Hey, why not try my RickRoll Pro plugin!’);

    Then the RickRoll Pro plugin could do this:

    And the upsell message would be gone, automagically, when the Pro plugin was activated. You generally don’t need complex dependency checking if you plan ahead a little bit and include useful filters for other code to use to modify behaviors.

    echo apply_filters

  3. Thank you so much for laying this out! We are getting ready to release a plugin in which we would like to offer both a free and paid version and have been discussing how to handle this and still have the free version included on .org.

    Thank you!

  4. I’m with Otto on the best way to do this. Forget about checking for dependencies, or loading one set of settings if pro is detected, but rather provide the basic hooks and filters needed in the free version, and hook into them with the pro version. Everything is seamless and a piece of cake.

  5. I had just started writing a long comment on using hooks and filters to add pro features via add-ons like we do for The Events Calendar, when I scanned the comments. What Otto says!

    There is a secondary advantage. It creates a pattern for the community to contribute their own add-ons. We just got another great little add-on posted by a user on .org this week due to having the right code in place:

  6. Shane beat me to it, but I was just going to say how The Events Calendar (which I worked on for a few months) uses the add-on + hooks system to provide pro functionality. Definitely the best current way to go about this.

    • It doesn’t need to for all of it’s sub-plugins, but it does for WP Stats, Comments, Subscriptions, Gravatars, Shortlinks, Enhanced Distribution, and Sharing, all of which are running code off, which requires an API key to phone home.

      Personally I’d love to see Jetpack not phone home until you activate those sub-plugs, but seeing as over half do need it, it’s a justified call. It’s a legit API usage.

    • I dunno.

      I like JetPack, and am ok with the idea of needing an API key (like for Akismet), it just kind of seems to be a little disingenuous.

      To quantify, I’m not sure anyone else would get away with that. The idea that you have to input an API key, and allow it to call home, before it needs to – or without testing if it EVER needs to – that seems to fly in the face of the concept: ‘Don’t phone home without a damn good reason’.

      Of course, it’s WP’s repository, and it’s their rules, so in essence it’s up to them as to how, when and how strongly those guidelines/rules are enforced. It just seems to me that if you want people to play by your rules, you kinda have to play by them too.

      Or put it another way:

      If I create a plug-in, and about half of its features required a “phone home”/external-API feature; and I forced all users of my plug-in to register with an external service, provide me with an API key, and then allow me to “phone home” every time a page was loaded regardless if it was needed or not… do you think it would get on the WP repo?

      I don’t think it would pass muster, but thats just my 2cents 🙂

    • I’d argue that it should be included (and would fight for it since Jetpack’s allowed). If the plugin is providing a legit service, then it has a darn good reason.

      It’s the people that use an API only to check for licenses that are at issue here.

Comments are closed.

%d bloggers like this: