<h2>License-Protect Your WordPress Plugin</h2>
<p>You've built a WordPress plugin that people want to pay for. Now you need to protect it. This guide shows you how to add license key validation to your plugin so you can sell premium features, gate updates, and track installations.</p>
<h3>The WordPress Licensing Challenge</h3>
<p>WordPress plugins have unique licensing challenges:</p> <ul> <li><strong>No built-in licensing</strong> — WordPress has no native concept of license keys</li> <li><strong>Easy to pirate</strong> — PHP source code is readable; you can't rely on obfuscation</li> <li><strong>Update distribution</strong> — The WordPress updater needs to be hooked to check license status</li> <li><strong>Multi-site support</strong> — One key may need to work across multiple sites</li> </ul>
<h3>Step 1: Create the License Checker Class</h3>
<pre><code>class TO_License_Checker { private $api_url = 'https://api.trafficorchestrator.com/api/v1'; private $option_key = 'my_plugin_license_key';
public function validate() { $key = get_option($this->option_key); if (empty($key)) { return ['valid' => false, 'error' => 'No key entered']; }
$domain = parse_url(home_url(), PHP_URL_HOST); $response = wp_remote_post($this->api_url . '/validate', [ 'body' => json_encode([ 'key' => $key, 'domain' => $domain ]), 'headers' => ['Content-Type' => 'application/json'], 'timeout' => 10 ]);
if (is_wp_error($response)) { // Network error — check cached status return $this->get_cached_status(); }
$body = json_decode(wp_remote_retrieve_body($response), true);
// Cache the result for offline/error scenarios update_option('my_plugin_license_status', $body); update_option('my_plugin_license_checked', time());
return $body; }
private function get_cached_status() { $cached = get_option('my_plugin_license_status'); $checked = get_option('my_plugin_license_checked', 0);
// Accept cached result for up to 72 hours if ($cached && (time() - $checked) < 259200) { $cached['cached'] = true; return $cached; }
return ['valid' => false, 'error' => 'Unable to verify']; } }</code></pre>
<h3>Step 2: Add the Admin Settings Page</h3>
<pre><code>add_action('admin_menu', function() { add_options_page( 'My Plugin License', 'My Plugin License', 'manage_options', 'my-plugin-license', 'render_license_page' ); });
function render_license_page() { if (isset($_POST['license_key'])) { update_option('my_plugin_license_key', sanitize_text_field($_POST['license_key'])); $checker = new TO_License_Checker(); $result = $checker->validate(); }
$key = get_option('my_plugin_license_key', ''); $status = get_option('my_plugin_license_status', []);
echo '<div class="wrap">'; echo '<h1>License Activation</h1>'; echo '<form method="post">'; echo '<table class="form-table"><tr>'; echo '<th>License Key</th>'; echo '<td><input type="text" name="license_key" value="' . esc_attr($key) . '" class="regular-text">'; echo '<p class="description">Enter your license key from Traffic Orchestrator.</p></td>'; echo '</tr></table>'; submit_button('Activate License'); echo '</form></div>'; }</code></pre>
<h3>Step 3: Gate Premium Features</h3>
<pre><code>function is_premium_active() { $status = get_option('my_plugin_license_status', []); return !empty($status['valid']); }
// Use in your plugin code: if (is_premium_active()) { // Show premium feature add_action('admin_init', 'register_premium_settings'); } else { // Show upgrade prompt add_action('admin_notices', function() { echo '<div class="notice notice-info"><p>'; echo 'Upgrade to Pro for advanced features. '; echo '<a href="' . admin_url('options-general.php?page=my-plugin-license') . '">Activate License</a>'; echo '</p></div>'; }); }</code></pre>
<h3>Step 4: Gate Plugin Updates</h3>
<pre><code>add_filter('pre_set_site_transient_update_plugins', function($transient) { if (!is_premium_active()) { return $transient; // No updates without valid license }
// Check your update server for new versions $update_url = 'https://api.trafficorchestrator.com/api/v1/releases/check'; $response = wp_remote_get($update_url . '?product=my-plugin¤t=' . MY_PLUGIN_VERSION);
if (!is_wp_error($response)) { $update = json_decode(wp_remote_retrieve_body($response)); if ($update && version_compare($update->version, MY_PLUGIN_VERSION, '>')) { $transient->response['my-plugin/my-plugin.php'] = (object) [ 'slug' => 'my-plugin', 'new_version' => $update->version, 'package' => $update->download_url ]; } }
return $transient; });</code></pre>
<h3>Best Practices for WordPress Licensing</h3>
<ul> <li><strong>Cache validation results</strong> — Don't call the API on every page load. Check once per day and cache.</li> <li><strong>Graceful degradation</strong> — If the API is unreachable, honor the cached result for 72 hours.</li> <li><strong>Clear admin notices</strong> — Tell users exactly what to do when their license expires.</li> <li><strong>Multi-site support</strong> — Use <code>network_admin_menu</code> for network-wide license management.</li> </ul>
<p><a href="/docs/quickstart/wordpress">View the WordPress Quickstart →</a> | <a href="/docs/quickstart/php">PHP Quickstart →</a></p>
Related Articles
- WordPress Plugin License Management: The Complete Guide
- How to Build a Secure License Key Generator
- Domain-Based Software Licensing Explained
Ship licensing in your next release
5 licenses, 500 validations/month, full API access. Set up in under 5 minutes — no credit card required.