Another day another challenge. This time I was working on a WordPress Plugin. As you might know, I use the Divi Theme a lot. I think it’s a really cool Theme with a powerful page builder and all sorts of cool modules. There is even a marketplace where you can buy additional modules and child themes.

About two month ago, I was in desperate needs for a module for my german travel blog which didn’t exist in the Divi Page Builder. I also did not find anything close to what I needed on the web or in the marketplace. So as the programmer I am, I decided that it was time to learn another language (PHP) and dive a little bit into WordPress development.

I developed a module called Date Counter for Divi for the Divi Page Builder and I was really happy with it. On Facebook I asked in some Divi groups if people would like to test it – my plan was to sell it via Gumroad on this site. A fellow german developer pointed me to the Elegant Marketplace and told me that I should start selling my plugins there.

I checked the page out, created a account and after one or two days, I already had my first sale. Awesome. But then I created a update because I fixed a small issue with a wrong text. Now how should I give the update to my customers? I mean sure, they can just download the plugin, remove the old one from their WordPress and install the new version but we live in 2017, not 1600 bc – amirite?

Fortunately the Elegant Marketplace works with a tool called Easy Digital Downloads – a suite for selling digital products via your own WordPress installation. It comes with all sorts of extensions and one of the – at least I think so – coolest functions is the possibility to use WordPress native updating mechanism to provide updates for your plugins and themes.

Implementation is quiet simple too. At least if you know what to do. Elegant Marketplace provides a small coding sample in the Vendor Dashboard as well as a sample plugin and a sample theme but I still got confused and in the end, even after I did everything right according to them, it still did not work till I did a small change by myself they were not aware of by themselves.

So let me explain what you actually have to do to get licensing and updating via EDD for your products on the Elegant Marketplace.

Step 1: Preparing your Plugin

The first thing you have to do is to prepare your plugin. Thats really, really really simple. All you have to do is to copy over the EDD_SL_Plugin_Updater.php file to your plugin folder. It really doesn’t matter where you put it. I for example put it in the root directory because my plugins are only a few files and I don’t see a need for folders.

I saw that the popular Woo Layout Injector plugin puts it in a folder called includes but thats just personal preference I guess. You can get the file from the edd-sample-plugin-1 on the vendor dashboard of Elegant Marketplace or directly from the Easy Digital Downloads Github Repo.

Step 2: Copy over the sample code

The second step is to copy over the sample code from the edd-sample-plugin.php to your plugin file. If you are an advances developer, you of course can put this code in a separate php file and include it in your plugin file instead of stuffing your plugin file with all that code. Anyways, lets go through the code. I’ll explain to you step by step what you have to change and what to keep.

// this is the URL our updater / license checker pings. This should be the URL of the site with EDD installed
define( 'EDD_SAMPLE_STORE_URL', 'http://easydigitaldownloads.com' ); // you should use your own CONSTANT name, and be sure to replace it throughout this file

// the name of your product. This should match the download name in EDD exactly
define( 'EDD_SAMPLE_ITEM_NAME', 'Sample Plugin' ); // you should use your own CONSTANT name, and be sure to replace it throughout this file

// the name of the settings page for the license input to be displayed
define( 'EDD_SAMPLE_PLUGIN_LICENSE_PAGE', 'pluginname-license' );

if( !class_exists( 'EDD_SL_Plugin_Updater' ) ) {
	// load our custom updater
	include( dirname( __FILE__ ) . '/EDD_SL_Plugin_Updater.php' );
}

This is the first part. Here we define the EDD store url, the plugin name, the page where the license field is displayed and we also include the EDD_SL_Plugin_Updater.php if it wasn’t installed by another plugin already. Here are the changes you have to do:

  • Rename EDD_SAMPLE_STORE_URL to something meaningful for you
  • Change the value of EDD_SAMPLE_STORE_URL to https://elegantmarketplace.com
  • Rename EDD_SAMPLE_ITEM_NAME to something meaningful for you
  • Change the value of EDD_SAMPLE_ITEM_NAME to the display name of your plugin or theme on Elegant Marketplace. A good example is my “Date Counter for Divi” Module or the “Woo Layout Injector”
  • Rename EDD_SAMPLE_PLUGIN_LICENSE_PAGE to something meaningful for you
  • Change the value of EDD_SAMPLE_PLUGIN_LICENSE_PAGE to the slug of your license page. If you want to include the license field on a existing settings page, this should match it
  • Finally change the path to the EDD_SL_Plugin_Updater.php to match whatever path you put it in. If your EDD_SL_Plugin_Updater.php is in the root directory of your plugin, you don’t need to change the path

Ready for the next part? Here we go:

function edd_sl_sample_plugin_updater() {
    // retrieve our license key from the DB
    $license_key = trim( get_option( 'edd_sample_license_key' ) );

    // setup the updater
    $edd_updater = new EDD_SL_Plugin_Updater( EDD_SAMPLE_STORE_URL, __FILE__, array(
        'version' => '1.0', // current version number
        'license' => $license_key, // license key (used get_option above to retrieve from DB)
        'item_name' => EDD_SAMPLE_ITEM_NAME, // name of this plugin
        'author' => 'Pippin Williamson', // author of this plugin
        'beta' => false
    ));
}
add_action( 'admin_init', 'edd_sl_sample_plugin_updater', 0 );

This function sets up the updating mechanism. Here I had the most trouble when setting up my plugin for the first time. It took me a few days to figure out what I was doing wrong. I ended up debugging the EDD_SL_Plugin_Updater class and recreating its API calls to see whats wrong. So let me tell you what you have to change here:

  • Change edd_sample_license_key to something meaningful for you. It’s a good idea to use your vendor prefix (I use jt_ for my plugins) followed by your plugins name followed by license_key. Ideally you use search and replace to replace all occurrences of this string throughout the rest of the code.
  • Match the version field to your plugins current version.
  • Change EDD_SAMPLE_ITEM_NAME to match the field you changed in the last section.
  • Remove the author field completely. This is what in the end prevented my plugin from getting updates but I only found out about this after recreating the API call from the EDD_SL_Plugin_Updater with a REST tool (I use Postman by the way). As far as I’m concerned, the author field is not necessary when checking for updates so you can safely omit it
  • Finally rename edd_sl_sample_plugin_updater to something meaningful for you. Again, using your vendor prefix and plugin name followed by _updater is kind of a good idea I guess

The next part is simplest as well if you take your time and look through the code:

function edd_sample_license_menu() {
	add_plugins_page( 'Plugin License', 'Plugin License', 'manage_options', EDD_SAMPLE_PLUGIN_LICENSE_PAGE, 'edd_sample_license_page' );
}
add_action('admin_menu', 'edd_sample_license_menu');

function edd_sample_license_page() {
	$license = get_option( 'edd_sample_license_key' );
	$status  = get_option( 'edd_sample_license_status' );
	?>
	<div class="wrap">
		<h2><?php _e('Plugin License Options'); ?></h2>
		<form method="post" action="options.php">

			<?php settings_fields('edd_sample_license'); ?>

			<table class="form-table">
				<tbody>
					<tr valign="top">
						<th scope="row" valign="top">
							<?php _e('License Key'); ?>
						</th>
						<td>
							<input id="edd_sample_license_key" name="edd_sample_license_key" type="text" class="regular-text" value="<?php esc_attr_e( $license ); ?>" />
							<label class="description" for="edd_sample_license_key"><?php _e('Enter your license key'); ?></label>
						</td>
					</tr>
					<?php if( false !== $license ) { ?>
						<tr valign="top">
							<th scope="row" valign="top">
								<?php _e('Activate License'); ?>
							</th>
							<td>
								<?php if( $status !== false && $status == 'valid' ) { ?>
									<span style="color:green;"><?php _e('active'); ?></span>
									<?php wp_nonce_field( 'edd_sample_nonce', 'edd_sample_nonce' ); ?>
									<input type="submit" class="button-secondary" name="edd_license_deactivate" value="<?php _e('Deactivate License'); ?>"/>
								<?php } else {
									wp_nonce_field( 'edd_sample_nonce', 'edd_sample_nonce' ); ?>
									<input type="submit" class="button-secondary" name="edd_license_activate" value="<?php _e('Activate License'); ?>"/>
								<?php } ?>
							</td>
						</tr>
					<?php } ?>
				</tbody>
			</table>
			<?php submit_button(); ?>

		</form>
	<?php
}

function edd_sample_register_option() {
	// creates our settings in the options table
	register_setting('edd_sample_license', 'edd_sample_license_key', 'edd_sanitize_license' );
}
add_action('admin_init', 'edd_sample_register_option');

function edd_sanitize_license( $new ) {
	$old = get_option( 'edd_sample_license_key' );
	if( $old && $old != $new ) {
		delete_option( 'edd_sample_license_status' ); // new license has been entered, so must reactivate
	}
	return $new;
}

Here we add the settings page with the field to enter the license for updating. Of course you could also use get_option( ‘edd_sample_license_status’ ) in your plugin itself to check if the license is activated and only if so you would do something. However, I’m not doing that because first of all, I offer lifetime licenses for my customers but even if you offer only let’s say a year of updates, you still want your plugin to function. You just don’t give support.

The things we have to do here are:

  • Rename the functions to follow the previous naming convention <vendor>_<plugin>_<descriptive function name>
  • Rename Plugin License in the add_plugins_page() function to something meaningful. Remember, that page will show up under Plugins in your WordPress installation. Of course you can put this on a existing settings page of your plugin instead of adding a page under Plugins if you like.
  • Well basically you have to rename all the “edd_…” stuff and match it to whatever you renamed in the previous sections (e. g. EDD_SAMPLE_PLUGIN_LICENSE_PAGE which is the settings page slug)

Ready for the last parts? We are almost done.

function edd_sample_activate_license() {

	// listen for our activate button to be clicked
	if( isset( $_POST['edd_license_activate'] ) ) {

		// run a quick security check
	 	if( ! check_admin_referer( 'edd_sample_nonce', 'edd_sample_nonce' ) )
			return; // get out if we didn't click the Activate button

		// retrieve the license from the database
		$license = trim( get_option( 'edd_sample_license_key' ) );


		// data to send in our API request
		$api_params = array(
			'edd_action' => 'activate_license',
			'license'    => $license,
			'item_name'  => urlencode( EDD_SAMPLE_ITEM_NAME ), // the name of our product in EDD
			'url'        => home_url()
		);

		// Call the custom API.
		$response = wp_remote_post( EDD_SAMPLE_STORE_URL, array( 'timeout' => 15, 'sslverify' => false, 'body' => $api_params ) );

		// make sure the response came back okay
		if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {

			if ( is_wp_error( $response ) ) {
				$message = $response->get_error_message();
			} else {
				$message = __( 'An error occurred, please try again.' );
			}

		} else {

			$license_data = json_decode( wp_remote_retrieve_body( $response ) );

			if ( false === $license_data->success ) {

				switch( $license_data->error ) {

					case 'expired' :

						$message = sprintf(
							__( 'Your license key expired on %s.' ),
							date_i18n( get_option( 'date_format' ), strtotime( $license_data->expires, current_time( 'timestamp' ) ) )
						);
						break;

					case 'revoked' :

						$message = __( 'Your license key has been disabled.' );
						break;

					case 'missing' :

						$message = __( 'Invalid license.' );
						break;

					case 'invalid' :
					case 'site_inactive' :

						$message = __( 'Your license is not active for this URL.' );
						break;

					case 'item_name_mismatch' :

						$message = sprintf( __( 'This appears to be an invalid license key for %s.' ), EDD_SAMPLE_ITEM_NAME );
						break;

					case 'no_activations_left':

						$message = __( 'Your license key has reached its activation limit.' );
						break;

					default :

						$message = __( 'An error occurred, please try again.' );
						break;
				}

			}

		}

		// Check if anything passed on a message constituting a failure
		if ( ! empty( $message ) ) {
			$base_url = admin_url( 'plugins.php?page=' . EDD_SAMPLE_PLUGIN_LICENSE_PAGE );
			$redirect = add_query_arg( array( 'sl_activation' => 'false', 'message' => urlencode( $message ) ), $base_url );

			wp_redirect( $redirect );
			exit();
		}

		// $license_data->license will be either "valid" or "invalid"

		update_option( 'edd_sample_license_status', $license_data->license );
		wp_redirect( admin_url( 'plugins.php?page=' . EDD_SAMPLE_PLUGIN_LICENSE_PAGE ) );
		exit();
	}
}
add_action('admin_init', 'edd_sample_activate_license');


function edd_sample_deactivate_license() {

	// listen for our activate button to be clicked
	if( isset( $_POST['edd_license_deactivate'] ) ) {

		// run a quick security check
	 	if( ! check_admin_referer( 'edd_sample_nonce', 'edd_sample_nonce' ) )
			return; // get out if we didn't click the Activate button

		// retrieve the license from the database
		$license = trim( get_option( 'edd_sample_license_key' ) );


		// data to send in our API request
		$api_params = array(
			'edd_action' => 'deactivate_license',
			'license'    => $license,
			'item_name'  => urlencode( EDD_SAMPLE_ITEM_NAME ), // the name of our product in EDD
			'url'        => home_url()
		);

		// Call the custom API.
		$response = wp_remote_post( EDD_SAMPLE_STORE_URL, array( 'timeout' => 15, 'sslverify' => false, 'body' => $api_params ) );

		// make sure the response came back okay
		if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {

			if ( is_wp_error( $response ) ) {
				$message = $response->get_error_message();
			} else {
				$message = __( 'An error occurred, please try again.' );
			}

			$base_url = admin_url( 'plugins.php?page=' . EDD_SAMPLE_PLUGIN_LICENSE_PAGE );
			$redirect = add_query_arg( array( 'sl_activation' => 'false', 'message' => urlencode( $message ) ), $base_url );

			wp_redirect( $redirect );
			exit();
		}

		// decode the license data
		$license_data = json_decode( wp_remote_retrieve_body( $response ) );

		// $license_data->license will be either "deactivated" or "failed"
		if( $license_data->license == 'deactivated' ) {
			delete_option( 'edd_sample_license_status' );
		}

		wp_redirect( admin_url( 'plugins.php?page=' . EDD_SAMPLE_PLUGIN_LICENSE_PAGE ) );
		exit();

	}
}
add_action('admin_init', 'edd_sample_deactivate_license');

Here we do almost exactly the same as in the previous section. We change every occurrence of edd to our own vendor prefix, rename samle to our own plugin name and match the constants we have defined. If you used search and replace, you should have almost nothing to do.

Did I say “every occurence of edd”? Well that is not completely true. Notice that the $api_params array has a field called edd_action. This field must keep the name edd_action! This is one of the rare occurrences where you don’t rename edd and it costed me half a day to figure this out. Blindly following the tutorial on Elegant Marketplace I first used a search and replace to replace every edd_ throughout the whole code and then the search for errors started.

So be aware and don’t change the edd_action to your plugin prefix!

Okay, now to the last part:

function edd_sample_admin_notices() {
	if ( isset( $_GET['sl_activation'] ) && ! empty( $_GET['message'] ) ) {

		switch( $_GET['sl_activation'] ) {

			case 'false':
				$message = urldecode( $_GET['message'] );
				?>
				<div class="error">
					<p><?php echo $message; ?></p>
				</div>
				<?php
				break;

			case 'true':
			default:
				// Developers can put a custom success message here for when activation is successful if they way.
				break;

		}
	}
}
add_action( 'admin_notices', 'edd_sample_admin_notices' );

This function takes care of displaying error messages if something goes wrong during the activation process of the license. Here again we just have to rename the functions to match our vendor prefix and plugin name.

Thats it. Your licensing / updating should now work. For me the two issues which caused the most trouble were the fact that I renamed the edd_action parameter and that I had to remove the vendor field from the call to the updater class. If this tutorial was helpful for your, I’d love to hear about it in the comments section. Cheers ✌️