# License Server Documentation
Canonical: https://docs.wpanchorbay.com/license-server-for-woocommerce/
This file contains the main public documentation content in a machine-readable text format.
## License Server for WooCommerce
URL: https://docs.wpanchorbay.com/license-server-for-woocommerce/
Description: Operate License Server, integrate client plugins, and understand the REST API and WooCommerce workflow.
import { CardGrid } from '@astrojs/starlight/components';
import HeroTitleEffect from '../../components/HeroTitleEffect.astro';
import Card from '../../components/starlight/Card.astro';
import WPAnchorBayFooter from '../../components/WPAnchorBayFooter.astro';
## What it does
License Server converts a WooCommerce store into a licensing and update backend for software products.
## Core capabilities
- Automatic license generation when eligible WooCommerce orders are completed.
- License activation and validation through a public REST API.
- Activation limits per license key.
- Protected update metadata and zip downloads for licensed clients.
- Admin management for software releases, licenses, activations, and logs.
- Customer-facing license visibility in WooCommerce account pages and order details.
## Main workflows
1. An admin creates a software record with a slug, version, readme, and release zip.
2. The admin links a WooCommerce product or variation to that software slug.
3. A customer buys the product and receives generated license keys.
4. A client plugin activates and checks the license against the REST API.
5. The client plugin requests update metadata and downloads the protected zip package.
## Start here
Start with the installation and setup flow, then connect your first software release to WooCommerce.
Learn how software releases, licenses, customer delivery, and troubleshooting work once the server is live.
Use the activation, check, update, and download patterns to connect another plugin to License Server.
Jump straight to the public and admin endpoint reference when you need exact route behavior.
## Quick Links
- [Getting Started](./getting-started/requirements/)
- [Operate License Server](./operate/software-releases/)
- [Integrate Your Plugin](./integrations/overview/)
- [REST API](./api/public-endpoints/)
- [Agent Guide](./agents/)
## How to navigate this documentation?
You do not need to read everything at once. Start with the path that matches what you are trying to do.
- If you want to install and operate License Server, read [Getting Started](./getting-started/requirements/) and [Operate License Server](./operate/software-releases/).
- If you want to integrate another plugin or app with License Server, read [Integrate Your Plugin](./integrations/overview/) and the [REST API](./api/public-endpoints/).
- If you want to connect License Server knowledge to an AI system, read [Agents and MCP](./agents/) and [MCP Server](./agents/mcp-server/).
- If you want the complete picture, follow the sidebar from top to bottom to understand setup, operations, integration, APIs, developer internals, and agent-facing guidance.
## Who This Is For
- Store owners selling premium plugins, themes, or apps through WooCommerce.
- Plugin developers who need a license activation and update backend.
- Engineers who need the data model, hooks, and API behavior to extend the plugin safely.
## Agent Guide
URL: https://docs.wpanchorbay.com/license-server-for-woocommerce/agents/
Description: Start here when an AI agent needs to understand how to use or extend License Server.
This section exposes the agent-facing guide in a public, crawlable form. It is useful for AI systems, MCP clients, and automation that need precise instructions for operating or integrating License Server.
## Which module to load
- To install, configure, or operate License Server, read [Install And Operate](./install-operate/).
- To integrate another plugin, read [Integrate Plugins](./integrate-plugins/).
- To work with routes and payloads, read [Agent REST Reference](./rest-api/).
- To diagnose setup or integration problems, read [Agent Troubleshooting](./troubleshooting/).
- To connect through MCP, read [MCP Server](./mcp-server/).
## Core facts
- Plugin slug, text domain, and REST namespace root: `license-server-for-woocommerce`.
- REST base path: `/wp-json/license-server-for-woocommerce/v1`.
- Public API authentication is license-key based.
- Admin API authentication is WordPress capability and nonce based.
- WooCommerce completed orders drive automatic license generation.
## Meta files and discovery endpoints
These public machine-readable files help crawlers, agents, and automation discover the docs and related integration metadata:
- [llms.txt](../llms.txt) for the concise LLM-friendly overview of the documentation set.
- [llms-full.txt](../llms-full.txt) for the expanded plain-text export of the docs corpus.
- [robots.txt](../robots.txt) for crawler policy and the sitemap reference.
- [sitemap-index.xml](../sitemap-index.xml) for the top-level sitemap index.
- [sitemap-0.xml](../sitemap-0.xml) for the generated URL list currently referenced by the sitemap index.
- [`.well-known/mcp.json`](../.well-known/mcp.json) for MCP discovery metadata related to the public documentation site.
## Keep docs and agent knowledge in sync
When License Server behavior changes, update the public docs and the agent guide together. The most important areas to keep current are setup flow, product meta, REST API contracts, database schema, release packaging, activation behavior, update/download behavior, and recommended integration patterns.
## Install And Operate
URL: https://docs.wpanchorbay.com/license-server-for-woocommerce/agents/install-operate/
Description: Agent-focused checklist for installing, configuring, releasing, and verifying License Server.
Use this page when an AI agent or automation needs to set up or verify License Server on a WooCommerce store.
## Setup flow
1. Install and activate WooCommerce.
2. Install and activate License Server.
3. Confirm activation created the custom database tables.
4. Create a software record in the License Server admin area.
5. Upload or place the release zip in `releases/`.
6. Enable licensing on a WooCommerce product or variation.
7. Complete a test order for that product.
8. Confirm the generated license appears in admin and customer My Account.
9. Call the public check and update endpoints with the generated key.
## Software record requirements
Each software record should include:
- `slug`
- `name`
- `version`
- `file_name`
- `readme`
The `file_name` must match a zip file in the plugin `releases/` directory.
## Product meta contract
For a simple product or variation, License Server stores:
- `_licenseserver_enabled`
- `_licenseserver_slug`
- `_licenseserver_limit`
- `_licenseserver_duration`
License generation runs when WooCommerce fires `woocommerce_order_status_completed`.
## Verification checklist
- The order status is `completed`.
- The product or variation has licensing enabled.
- The configured software slug exists.
- A row exists in `licenseserver_licenses`.
- The customer can see the key under My Account.
- `/check` returns a valid response for the key and slug.
- `/update-check/{slug}/{license_key}` returns metadata when the software record exists.
- `/download/{slug}/{license_key}` streams the zip when the file exists.
## Integrate Plugins
URL: https://docs.wpanchorbay.com/license-server-for-woocommerce/agents/integrate-plugins/
Description: Agent-focused integration model for client plugins that consume License Server.
Use this page when an AI agent needs to connect another plugin, theme, or app to License Server.
## Client integration model
A client plugin should contain:
- a config source for server URL, software slug, option keys, transient keys, and timeout
- a license client or service class
- local admin-only REST endpoints for the client plugin UI
- local storage for license key, status, and expiration
- a transient cache for validation status
- an update integration that calls `/update-check/{slug}/{license_key}`
Avoid calling License Server directly from browser UI. Browser/admin UI should call the client plugin's local REST endpoint; the local PHP endpoint should call License Server.
## Parameter rules
- `/activate` requires `license_key`, `slug`, and `domain`.
- `/check` requires `license_key` and `slug`.
- `/check` can also verify a stored activation row when `domain` is sent.
- `/check` does not use a parameter named `url`.
- `/update-check/{slug}/{license_key}` accepts optional `host` for logging.
- `/download/{slug}/{license_key}` accepts optional `host` for logging.
## Recommended domain value
```php
wp_parse_url(home_url(), PHP_URL_HOST)
```
Use the same exact value for activation and check requests when activation-row validation matters.
## Local removal
License Server does not currently expose a public deactivation endpoint. A client plugin can remove the key locally, but this does not free the activation slot on the server. The store admin can delete the activation in License Server admin.
## MCP Server
URL: https://docs.wpanchorbay.com/license-server-for-woocommerce/agents/mcp-server/
Description: Connect MCP-capable AI systems to License Server documentation and agent guidance.
This docs project includes a read-only MCP server that exposes License Server documentation and agent guidance to MCP-capable AI clients.
## What it provides
Resources:
- public docs pages
- agent guide pages
- API and troubleshooting references
Tools:
- `search_license_server_docs`
- `get_license_server_doc`
Prompts:
- `setup_license_server`
- `integrate_client_plugin`
- `troubleshoot_license_activation`
## Run with stdio
Use stdio for local clients that launch a command directly.
```bash
bun run mcp
```
Example client command:
```json
{
"command": "bun",
"args": ["run", "mcp"],
"cwd": "/home/usr/projects/awesome-plugin"
}
```
## Run over HTTP
Use HTTP when deploying the MCP server separately from GitHub Pages.
```bash
bun run mcp:http
```
By default, the HTTP server listens on port `8787` and responds at:
```text
http://localhost:8787/mcp
```
## Important deployment note
GitHub Pages can host the public docs, `llms.txt`, `llms-full.txt`, `robots.txt`, and sitemap files, but it cannot host the live MCP endpoint. Deploy the MCP server to a dynamic Node-capable host, then keep the static docs on GitHub Pages.
## Public discovery file
The docs build also publishes:
```text
/.well-known/mcp.json
```
That file points AI systems to the public docs, LLM text files, and MCP server instructions.
## Agent REST Reference
URL: https://docs.wpanchorbay.com/license-server-for-woocommerce/agents/rest-api/
Description: Compact route reference for AI agents working with License Server integrations.
Base URL:
```text
https://example.com/wp-json/license-server-for-woocommerce/v1
```
## Public routes
| Method | Route | Purpose |
|---|---|---|
| `POST` | `/activate` | Validate a key and create a domain activation. |
| `GET` | `/check` | Validate license status and optionally domain activation. |
| `GET` | `/update-check/{slug}/{license_key}` | Return update metadata and a protected package URL. |
| `GET` | `/download/{slug}/{license_key}` | Validate the key and stream the release zip. |
## Admin routes
| Method | Route | Purpose |
|---|---|---|
| `GET` | `/dashboard/stats` | Return dashboard counts and recent logs. |
| `GET` | `/admin/software` | List software records. |
| `POST` | `/admin/software` | Create software record and optionally upload zip. |
| `POST` | `/admin/software/{id}` | Update software metadata and optionally replace zip. |
| `DELETE` | `/admin/software/{id}` | Delete software record. |
| `GET` | `/licenses` | List and filter licenses. |
| `POST` | `/licenses` | Manually create a license. |
| `POST` | `/licenses/{id}` | Update license status, expiration, or activation limit. |
| `DELETE` | `/licenses/{id}` | Delete license and associated activations. |
| `GET` | `/activations` | List activations. |
| `DELETE` | `/activations/{id}` | Delete one activation. |
| `GET` | `/logs` | List filtered logs. |
| `GET` | `/logs/{id}` | Fetch one log. |
| `DELETE` | `/logs/{id}` | Delete one log. |
| `DELETE` | `/logs/bulk-delete` | Delete selected logs. |
## Public rate limits
- Activation: 10 requests per 60 seconds.
- License check: 60 requests per 60 seconds.
- Update check: 60 requests per 60 seconds.
- Download: 60 requests per 60 seconds.
## Agent Troubleshooting
URL: https://docs.wpanchorbay.com/license-server-for-woocommerce/agents/troubleshooting/
Description: Compact troubleshooting guide for AI agents diagnosing License Server setup and integration failures.
## License was not generated
Check:
- the WooCommerce order reached `completed`
- the product or variation has `_licenseserver_enabled = yes`
- the product or variation has `_licenseserver_slug`
- the matching software slug exists
- the custom database tables exist
## Activation fails
Check:
- request includes `license_key`, `slug`, and `domain`
- the key exists
- the license slug matches the request slug
- license status is `active`
- expiration is empty or in the future
- activation count is below the limit
## Check returns `license_not_activated`
The `/check` request included `domain`, but that exact domain does not exist in the activations table. Activate first, or send the same normalized domain value used during activation.
## Update works but download fails
Check:
- software row exists for the slug
- software row has `file_name`
- zip file exists in `releases/`
- license is active, unexpired, and bound to the same slug
## Admin nonce or permission errors
Check:
- request includes `X-WP-Nonce`
- nonce is generated with `wp_create_nonce('wp_rest')`
- current user has the capability required by the endpoint
## Admin Endpoints
URL: https://docs.wpanchorbay.com/license-server-for-woocommerce/api/admin-endpoints/
Description: Reference the admin routes for software, licenses, activations, dashboard stats, and logs.
Admin endpoints require a logged-in WordPress user with the appropriate capability. Some also require a REST nonce.
## Dashboard
- `GET /dashboard/stats`
Returns summary counts and recent log activity.
## Software
- `GET /admin/software`
- `POST /admin/software`
- `POST /admin/software/{id}`
- `DELETE /admin/software/{id}`
Software records can include:
- slug
- name
- version
- file name
- readme
- uploaded zip file
## Licenses
- `GET /licenses`
- `POST /licenses`
- `POST /licenses/{id}`
- `DELETE /licenses/{id}`
Use these routes to list, create, update, and delete license records.
## Activations
- `GET /activations`
- `DELETE /activations/{id}`
Use these routes to inspect and revoke activations.
## Logs
- `GET /logs`
- `GET /logs/{id}`
- `DELETE /logs/{id}`
- `DELETE /logs/bulk-delete`
Use these routes to inspect filtered request logs and clear individual or bulk log records.
## Public Endpoints
URL: https://docs.wpanchorbay.com/license-server-for-woocommerce/api/public-endpoints/
Description: Technical reference for public license activation, validation, and update APIs.
The License Server exposes a public REST API for client plugins and themes to manage activations and check for updates.
**Base Namespace:**
```text
/wp-json/license-server/v1
```
---
## 1. License Activation (`POST /activate`)
Registers a specific domain or environment against a license key.
### Request Body (JSON)
| Field | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| `license_key` | string | Yes | The license key provided to the customer. |
| `slug` | string | Yes | The software slug (e.g., `my-cool-plugin`). |
| `domain` | string | Yes | The normalized host name of the client site. |
### Example Request
```json
{
"license_key": "WPAB-6638C488-ABCD",
"slug": "my-cool-plugin",
"domain": "example.com"
}
```
### Success Response (`200 OK`)
```json
{
"success": true,
"message": "License activated successfully.",
"license": "valid",
"expires_at": "2025-12-31 23:59:59"
}
```
### Error Responses
- **400 Bad Request**: Missing required parameters or slug mismatch.
- **403 Forbidden**: Activation limit reached or license inactive/expired.
- **404 Not Found**: Invalid license key.
---
## 2. License Status Check (`GET /check`)
Verifies the current status of a license.
### Query Parameters
| Parameter | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| `license_key` | string | Yes | The license key to check. |
| `slug` | string | Yes | The software slug. |
| `domain` | string | No | If provided, also verifies if the license is active for this domain. |
### Success Response (`200 OK`)
```json
{
"success": true,
"license": "valid",
"expires_at": "2025-12-31 23:59:59",
"activation_limit": 3
}
```
---
## 3. Update Check (`GET /update-check/{slug}/{license_key}`)
Returns metadata in a format compatible with the [WordPress.org Plugin API](https://developer.wordpress.org/plugins/metadata/) and the [Plugin Update Checker (PUC)](https://github.com/YahnisElsts/plugin-update-checker) library.
### Parameters
- `{slug}`: The software slug.
- `{license_key}`: The customer's license key.
### Optional Query Params
- `host`: The host URL requesting the update (for logging).
### Success Response (`200 OK`)
```json
{
"name": "My Cool Plugin Pro",
"slug": "my-cool-plugin",
"version": "2.1.0",
"download_url": "https://server.com/wp-json/license-server/v1/download/my-cool-plugin/WPAB-...",
"package": "https://server.com/wp-json/license-server/v1/download/my-cool-plugin/WPAB-...",
"homepage": "https://server.com",
"requires": "5.0",
"tested": "6.5",
"sections": {
"description": "Full plugin description...",
"changelog": "2.1.0
"
}
}
```
---
## 4. Protected Download (`GET /download/{slug}/{license_key}`)
Streams the latest release zip file. This endpoint performs full license validation before initiating the stream.
### Parameters
- `{slug}`: The software slug.
- `{license_key}`: The customer's license key.
### Headers
The response is returned with standard file-stream headers:
- `Content-Type: application/zip`
- `Content-Disposition: attachment; filename="slug-version.zip"`
---
## Rate Limits
To prevent abuse, the following limits are enforced per IP address:
| Endpoint | Limit | Window |
| :--- | :--- | :--- |
| `/activate` | 10 requests | 60 seconds |
| `/check` | 60 requests | 60 seconds |
| `/update-check` | 60 requests | 60 seconds |
| `/download` | 60 requests | 60 seconds |
:::tip
> Clients should implement local caching (e.g., using WordPress transients) to avoid hitting these limits during normal operation.
:::
## Architecture
URL: https://docs.wpanchorbay.com/license-server-for-woocommerce/developer/architecture/
Description: Understand the core design principles, data flow, and security model of License Server.
License Server is designed as a high-performance licensing backend that bridges WooCommerce commerce logic with a specialized REST API for software delivery.
## System Overview
The system consists of three primary domains:
1. **Commerce (WooCommerce)**: Handles payments, order fulfillment, and license generation.
2. **Operations (Admin API & SPA)**: Provides the interface for managing software, licenses, and releases.
3. **Delivery (Public API)**: Serves activation, validation, and update requests to external clients.
---
## Data Flow Diagram
```mermaid
graph TD
subgraph "External Client"
Client[Plugin/Theme]
end
subgraph "License Server (WordPress)"
WC[WooCommerce Order] --> OM[Order Manager]
OM --> DB_L[License DB]
API_P[Public API] --> LC[License Controller]
LC --> DB_L
LC --> DB_A[Activation DB]
API_P --> UC[Update Checker]
UC --> DB_L
UC --> DB_S[Software DB]
UC --> FS[File System /releases/]
end
subgraph "Admin Dashboard"
SPA[React App] --> API_A[Admin API]
API_A --> SC[Software Controller]
SC --> DB_S
SC --> FS
end
Client -- "Activate/Check" --> API_P
Client -- "Update/Download" --> API_P
```
---
## Core Components
### 1. Orchestration (`Plugin.php`)
The `Plugin` class acts as the central hub. It initializes all controllers, managers, and services. It uses a singleton-like pattern to ensure that core services (like logging and database management) are shared across the application.
### 2. REST API Layer (`app/Api/`)
Endpoints are split into two namespaces:
- **Admin Endpoints**: Require `manage_licenseserver` capability. Used by the React SPA to manage the system.
- **Public Endpoints**: Do not require WordPress authentication. Security is instead enforced via:
- License key validation.
- Domain locking.
- IP-based rate limiting.
### 3. Database Layer (`app/Data/`)
License Server uses custom SQL tables for performance and isolation from standard WordPress post meta:
- `wp_licenseserver_software`: Metadata about releases.
- `wp_licenseserver_licenses`: The central license registry.
- `wp_licenseserver_activations`: Mapping of licenses to specific domains.
- `wp_licenseserver_logs`: Audit trail of all API requests.
---
## Security Model
License Server prioritizes **Support Reliability** and **Availability**.
### License Keys
Keys are stored in **plain text**. This is a deliberate design choice to allow support staff to quickly identify keys and enable features like "one-click activation" for customers. Security is maintained through:
1. **Domain Locking**: Licenses are bound to a specific host upon activation.
2. **Activation Limits**: Each key has a strict limit on the number of concurrent domains.
3. **Rate Limiting**: Brute-force protection on all public endpoints.
### Release Protection
Release zip files are stored in a non-web-accessible directory within `wp-content/plugins/license-server/releases/`. They are never served directly by the web server (Nginx/Apache). Instead:
1. The client requests a protected download URL.
2. The server validates the license and expiration.
3. PHP reads the file and streams it directly to the client with appropriate headers.
## Data Model
URL: https://docs.wpanchorbay.com/license-server-for-woocommerce/developer/data-model/
Description: Review the custom tables that store software releases, licenses, activations, and logs.
License Server stores its core data in custom tables.
## Tables
### `licenseserver_software`
Stores:
- software slug
- name
- version
- zip filename
- readme content
### `licenseserver_licenses`
Stores:
- public license key
- software slug
- order and product ownership
- activation limit
- expiration
- status
### `licenseserver_activations`
Stores:
- license relationship
- activated domain
- client IP
- activation timestamps
### `licenseserver_logs`
Stores:
- action type
- license key
- slug
- request metadata
- response code
- extra context
## Hooks And Lifecycle
URL: https://docs.wpanchorbay.com/license-server-for-woocommerce/developer/hooks-and-lifecycle/
Description: Understand how WooCommerce, WordPress hooks, and the order lifecycle drive License Server behavior.
## Key WordPress and WooCommerce hooks
- `plugins_loaded`
- `rest_api_init`
- `admin_menu`
- `admin_enqueue_scripts`
- `woocommerce_product_data_tabs`
- `woocommerce_product_data_panels`
- `woocommerce_process_product_meta`
- `woocommerce_save_product_variation`
- `woocommerce_order_status_completed`
- `woocommerce_account_menu_items`
- `woocommerce_account_licenses_endpoint`
## High-level lifecycle
1. the plugin boots and registers its controllers and managers
2. admins configure software and WooCommerce products
3. a completed order triggers license generation
4. customers receive and view their keys
5. client plugins activate and validate through REST APIs
6. update-check and download requests are logged and protected
## Initial Setup
URL: https://docs.wpanchorbay.com/license-server-for-woocommerce/getting-started/initial-setup/
Description: Create your first software release, connect it to WooCommerce, and verify the license flow.
## Create a software record
In the License Server admin area, add a software record with:
- a unique `slug`
- a product `name`
- a `version`
- a `file_name` matching the release zip in `releases/`
- a plugin-style `readme`
You can upload the zip or place it in the plugin `releases/` directory.
## Link WooCommerce products
Edit a WooCommerce product or variation and enable License Server for that item. Save:
- the software slug
- the activation/site limit
- the validity duration
## Verify the end-to-end flow
1. Complete a test order for the licensed product.
2. Confirm a license row was created.
3. Confirm the customer can see the license in My Account.
4. Test the public API with the generated key and the correct slug.
For endpoint details, continue to [Public Endpoints](../api/public-endpoints/).
## Installation
URL: https://docs.wpanchorbay.com/license-server-for-woocommerce/getting-started/installation/
Description: Install License Server in WordPress and prepare it for your first software release.
## Install the plugin
1. Install and activate WooCommerce.
2. Install and activate License Server.
3. Confirm the plugin activates without fatal errors.
On activation, License Server creates the custom tables it needs for:
- software releases
- licenses
- activations
- logs
## Development build commands
If you are working on the plugin locally, the main commands are:
```bash
bun install
composer install
bun run build
bun run package-plugin
```
The production release, an installable **zip** file, is created from the plugin repository's packaging workflow (not from this docs site).
## Requirements
URL: https://docs.wpanchorbay.com/license-server-for-woocommerce/getting-started/requirements/
Description: Review the runtime and development requirements for License Server.
## Runtime requirements
- [WordPress](https://wordpress.org/) 6.8 or newer
- [PHP](https://www.php.net/) 7.4 or newer
- [WooCommerce](https://wordpress.org/plugins/woocommerce/) 10.x or newer
## Development requirements
- [Node.js](https://nodejs.org/en) (recommend latest LTS version, currently 24.x) for the admin frontend build
- [Bun](https://bun.com/) for JavaScript dependencies
- [Composer](https://getcomposer.org/) for PHP dependencies
## Plugin prerequisites
License Server assumes **[WooCommerce](https://wordpress.org/plugins/woocommerce/)** is active and the site is **intended to sell** digital software products such as:
- WordPress plugins
- WordPress themes
- desktop software
- other license-key-based digital goods
## Integration Overview
URL: https://docs.wpanchorbay.com/license-server-for-woocommerce/integrations/overview/
Description: Learn the expected integration model for client plugins that activate licenses and receive updates.
Client plugins should treat License Server as a remote licensing backend.
## Recommended client-side pieces
- a config source for base URL, slug, option keys, and timeouts
- a license client or service class
- local admin-only REST endpoints for the plugin UI
- local storage for license key, status, and expiration
- a transient cache for validation state
## Endpoint rules
- `/activate` requires `license_key`, `slug`, and `domain`
- `/check` requires `license_key` and `slug`
- `/check` can also validate a specific activation row when `domain` is sent
- `/update-check/{slug}/{license_key}` returns update metadata
- `/download/{slug}/{license_key}` streams the protected zip
Avoid calling the License Server API directly from browser code. A safer pattern is:
1. browser UI calls your plugin's local admin REST endpoint
2. your local PHP endpoint calls License Server
3. your plugin stores the resulting status locally
## WordPress Plugin Integration
URL: https://docs.wpanchorbay.com/license-server-for-woocommerce/integrations/wordpress-plugin/
Description: Production-ready patterns for integrating your WordPress plugin with License Server.
Integrating your premium plugin with License Server involves three main components: a settings UI for the license key, a background validation process, and an update checker.
## Normalizing the Domain
To ensure consistent activation checks, always normalize the domain using the host portion of `home_url()`. This prevents issues with `http` vs `https` or trailing slashes.
```php
/**
* Get normalized site domain for activation.
*/
function my_plugin_get_domain() {
return wp_parse_url(home_url(), PHP_URL_HOST);
}
```
---
## Production Implementation Example
Below is a robust pattern for a `LicenseManager` class that handles activation and background validation via transients.
```php
class MyPluginLicenseManager {
private $api_url = 'https://your-license-server.com/wp-json/license-server/v1';
private $slug = 'my-cool-plugin';
private $option_name = 'my_plugin_license_data';
/**
* Activate the license key.
*/
public function activate($license_key) {
$response = wp_remote_post($this->api_url . '/activate', [
'body' => json_encode([
'license_key' => $license_key,
'slug' => $this->slug,
'domain' => my_plugin_get_domain(),
]),
'headers' => ['Content-Type' => 'application/json'],
]);
if (is_wp_error($response)) {
return $response;
}
$data = json_decode(wp_remote_retrieve_body($response), true);
if (!empty($data['success'])) {
update_option($this->option_name, [
'key' => $license_key,
'status' => 'active',
'expires_at' => $data['expires_at'] ?? '',
]);
// Clear update cache to force immediate update check
delete_site_transient('update_plugins');
return true;
}
return new WP_Error('activation_failed', $data['message'] ?? 'Unknown error');
}
/**
* Validate license status (with 12-hour transient cache).
*/
public function is_valid() {
$license_data = get_option($this->option_name);
if (empty($license_data['key'])) return false;
$cache_key = 'my_plugin_license_check';
if (get_transient($cache_key)) return true;
$response = wp_remote_get(add_query_arg([
'license_key' => $license_data['key'],
'slug' => $this->slug,
'domain' => my_plugin_get_domain(),
], $this->api_url . '/check'));
if (is_wp_error($response)) return true; // Fail gracefully on server error
$data = json_decode(wp_remote_retrieve_body($response), true);
if (!empty($data['success']) && $data['license'] === 'valid') {
set_transient($cache_key, true, 12 * HOUR_IN_SECONDS);
return true;
}
// License invalid: mark as inactive locally
$license_data['status'] = 'inactive';
update_option($this->option_name, $license_data);
return false;
}
}
```
---
## Enabling Automatic Updates
We highly recommend using the [Plugin Update Checker (PUC)](https://github.com/YahnisElsts/plugin-update-checker) library by Yahnis Elsts. It is the industry standard for non-repo plugin updates.
Install PUC throught [composer](https://getcomposer.org/):
```bash
composer require yahnis-elsts/plugin-update-checker
```
Learn more on [packagist](https://packagist.org/packages/yahnis-elsts/plugin-update-checker).
### Initialization with PUC
```php
require 'plugin-update-checker/plugin-update-checker.php';
use YahnisElsts\PluginUpdateChecker\v5\PucFactory;
$license_data = get_option('my_plugin_license_data');
$license_key = $license_data['key'] ?? '';
$myUpdateChecker = PucFactory::buildUpdateChecker(
'https://your-license-server.com/wp-json/license-server/v1/update-check/my-cool-plugin/' . $license_key,
__FILE__,
'my-cool-plugin'
);
// Optional: Add host info to the request for better server logging
$myUpdateChecker->addQueryArgFilter(function($args) {
$args['host'] = my_plugin_get_domain();
return $args;
});
```
---
## Best Practices
1. **Graceful Failure**: If the license server is down, `is_valid()` should return `true` to avoid locking customers out of their own sites due to temporary network issues.
2. **Normalized Domains**: Always use `wp_parse_url` as shown above to ensure your client and server agree on what the "domain" is.
3. **Background Sync**: Use `wp_next_scheduled` (WP-Cron) to perform validation checks in the background rather than on every admin page load.
4. **Clear Transients**: When a user updates their license key, remember to delete the `update_plugins` site transient so WordPress sees the new update availability immediately.
## Customer Experience
URL: https://docs.wpanchorbay.com/license-server-for-woocommerce/operate/customer-experience/
Description: See how license keys are delivered and where customers can find them after purchase.
Customers interact with License Server through WooCommerce.
## Delivery points
- email sent after order completion
- My Account `licenses` endpoint
- WooCommerce order details page
## What customers see
License-related screens can show:
- the software name or slug
- the license key
- status
- expiration date
The email flow can also include a direct download link for the licensed release.
## License Management
URL: https://docs.wpanchorbay.com/license-server-for-woocommerce/operate/license-management/
Description: Understand how licenses are generated, created manually, updated, and deleted.
License Server supports both automatic generation and manual creation.
## Automatic generation
When a WooCommerce order reaches `completed`, the plugin checks the purchased items and generates licenses for items that have licensing enabled.
## Manual creation
Admins can create licenses directly from the License Server admin interface by selecting:
- a software slug
- an activation limit
- an optional expiration date
## Admin actions
Admins can:
- search licenses
- filter by order, slug, or status
- update activation limits
- change status
- edit expiration
- delete licenses
Deleting a license also removes its associated activation rows.
## Software Releases
URL: https://docs.wpanchorbay.com/license-server-for-woocommerce/operate/software-releases/
Description: Manage software records, release zips, version metadata, and update-readme content.
Software records power the update and download system.
## Each release record includes
- `slug`
- `name`
- `version`
- `file_name`
- `readme`
The `file_name` must match a real zip file in the plugin `releases/` directory.
## Why the slug matters
The software slug connects three parts of the system:
- the software release record
- the WooCommerce product configuration
- the client plugin integration
If the slug does not match across those pieces, activation and update checks will fail.
## Troubleshooting
URL: https://docs.wpanchorbay.com/license-server-for-woocommerce/operate/troubleshooting/
Description: Diagnose common issues with setup, generation, activation, update checks, and downloads.
## License key was not generated
Check that:
- the WooCommerce order reached `completed`
- the product or variation has licensing enabled
- the configured software slug exists
- the custom database tables were created
## Activation fails
Check that:
- the key exists
- the slug matches the software
- the license is active
- the license is not expired
- the activation limit has not been reached
- the `domain` parameter is present
## Update check works but download fails
Check that:
- the software record exists
- the `file_name` is set
- the zip exists in `releases/`
- the license key belongs to the requested slug
## Admin requests fail with nonce or permission errors
Check that:
- the request includes `X-WP-Nonce`
- the nonce was generated for `wp_rest`
- the current user has the required capability for that endpoint