You want to charge for a WordPress plugin. The catch: if you distribute the free version on WordPress.org, you cannot ship paid code in it or lock features behind a payment: the directory forbids "trialware." So how do you sell a Pro tier without getting your plugin pulled? The answer is a clean two-part architecture, and it is simpler than it sounds.

The rule you must not break

The plugin hosted on WordPress.org may contain only free, fully-functional code. No disabled paid features, no "upgrade to unlock" gates on code that ships in the free download. Violate this and your plugin gets rejected or removed. Everyone selling a Pro tier works around it the same way.

The compliant pattern: free plugin + separate Pro add-on

  • The free plugin (on wp.org) contains the free features plus a gate and "Get Pro" links, but no paid code. The gate is just a filter: if ( apply_filters( 'myplugin_is_pro', false ) ) { /* register paid hooks */ }.
  • The Pro add-on is a separate plugin you sell on your own store (Gumroad, Freemius, Lemon Squeezy). It is never uploaded to wp.org. When installed and licensed, it answers that filter with true and registers the paid features on hooks the free plugin already exposes.

Because the paid code lives only in the add-on the buyer downloads from you, the wp.org copy stays 100% free and compliant. See the full freemium architecture for how the two pieces fit together.

Verifying the license key

The Pro add-on needs to confirm the buyer actually paid. The lightest approach: your store issues a license key per sale, and the add-on verifies it against the store’s API. Gumroad, for example, exposes a license-verification endpoint: the add-on POSTs the product ID and the key, gets back whether it’s valid (and not refunded), and stores the result. Cache the check so you are not calling the API on every page load, and re-check periodically.

Whatever store you pick, keep one store as the license authority so you are not reconciling two key formats. The buyer pastes their key once; the add-on flips myplugin_is_pro to true; every gated feature lights up.

Failing safe when the license server is down

The question that separates a licensing system people tolerate from one they resent: what happens when your check can’t complete? A remote verification will fail eventually: your server has an outage, their host blocks outbound requests, a DNS hiccup times out the call. If a failed check instantly disables paid features, you have built a product that punishes paying customers for your downtime, on their production store, without warning.

Fail open, within limits. Cache the last successful verification with a generous grace period (a week or two is common) and keep Pro working throughout it. Re-check quietly in the background rather than during a page load, and never block admin_init on a network request, which is how a slow license endpoint turns into a hung dashboard for every user. Only after the grace window expires with repeated failures should you degrade, and then with a clear, non-destructive message.

Degrade without destroying data

When a license genuinely lapses, disable functionality, never data. If a customer’s Pro tier built 300 records, an expired license must not delete them or make them unreachable; it should stop new Pro actions while leaving everything already created intact and exportable. Anything else is holding a customer’s own data hostage, and it generates the refund requests and one-star reviews that cost more than the renewal was worth.

Two more details that decide whether renewals are painless: make deactivation easy so a customer can move a license between a staging site and production without opening a ticket, and store the key where it belongs: an option, not hardcoded, and never logged in plain text in a debug file that ends up in a support thread.

Support burden is a design decision

Licensing decisions look like revenue decisions and behave like support decisions. Every rule you add is a message someone will eventually open a ticket about, and the ticket volume is roughly proportional to how surprising the rule is.

Site limits are the clearest example. A licence valid on one site sounds simple until you meet the way people actually work: a staging copy, a local development install, and a production site are three activations of what the customer correctly regards as one website. If your activation logic counts hostnames naively, you will spend real time manually resetting licences for honest customers, and the customers who are genuinely sharing keys will not be inconvenienced in the slightest, because they were prepared to be.

The pragmatic pattern is to detect obvious non-production hosts and exempt them, allow a modest number of activations rather than exactly one, and make deactivation something the customer can do themselves from their account. All three reduce tickets more than they reduce piracy, which is the correct trade for a small plugin business.

Don’t build the gate twice

The license gate, the settings framework, the readme format, and the build-to-zip script are the same for every freemium plugin you ship. The feature is the only part that changes. Build the plumbing once (or start from a kit that already has it), and each new plugin becomes "add the feature, ship it" instead of a week of boilerplate.

Skip building the gate

The Freemium WordPress Plugin Boilerplate ships the free-plugin half already wired: the Pro filter, a settings framework, a wp.org-ready readme, and a one-command scaffolder. It does not verify keys: the check, the cache and the grace window described above are yours to write in the Pro add-on.

See the boilerplate →

Related reading: how to build a freemium WordPress plugin (and actually sell the Pro version).