shazzad / plugin-updater
A comprehensive WordPress plugin updater library that enables automatic updates, license verification, and remote plugin management for custom WordPress plugins.
Requires
- php: >=7.4
- ext-curl: *
- ext-json: *
Requires (Dev)
- brain/monkey: ^2.6
- dealerdirect/phpcodesniffer-composer-installer: ^0.7.2
- mockery/mockery: ^1.6
- phpcompatibility/php-compatibility: ^9.3
- phpunit/phpunit: ^9.0
- squizlabs/php_codesniffer: ^3.6
- wp-coding-standards/wpcs: ^3.1
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-09-27 05:01:23 UTC
README
A comprehensive WordPress plugin updater library that enables automatic updates, license verification, and remote plugin management for custom WordPress plugins.
Features
- Automatic Plugin Updates: Seamlessly check for and install plugin updates from your remote server
- License Management: Built-in license key verification and validation system
- Admin Interface: Clean WordPress admin interface for license management
- Plugin Tracking: Track plugin activation, deactivation, and usage statistics
- Insights (V4): Appsero-style usage data — opt-in with a consent notice for free wordpress.org plugins, implied for commercial plugins
- WordPress Integration: Hooks into WordPress core update system
- Flexible Configuration: Customizable API endpoints, menu placement, and licensing options
Requirements
- WordPress 5.0 or higher
- PHP 7.4 or higher
- Valid API server endpoint for plugin updates and license verification
Installation
composer require shazzad/plugin-updater
Which namespace to use
The library ships one namespace per major version, side by side:
-
Shazzad\PluginUpdater\V4(src/V4/) — active since 4.0.0. New consumers should use this. Two entry points:V4\Insights— for free wordpress.org plugins: usage tracking only, after an explicit opt-in through an Appsero-style consent notice. No update or license code at all; ship only the Insights files (see Shipping a free wp.org plugin).V4\Integration— for commercial plugins: everything V2 does (updates, license page, notices, update-row message), with install tracking moved from the old/pingto Insights (consent implied, license key in the payload). Same option keys, transients and cron hook as V2/V1, so a plugin moving to V4 keeps every saved license.
Both talk to the repo server's
wp-repo/v4API — oneapi_url(https://w4dev.com/wp-json/wp-repo/v4) serves updates, licensing and Insights, each plugin addressed as{api_url}/plugins/{uid}/…. Both requireproduct_uid(theprod_…uid): v4 accepts no numeric id.wp-repo/v3has noplugins/{uid}routes, so V4 does nothing against it; it stays, frozen, for V1/V2 clients. Requirescomposer require shazzad/plugin-updater:^4.0andshazzad/plugin-repo2.9.0+ on the server (theplugins/{uid}routes). -
Shazzad\PluginUpdater\V2(src/V2/) — stable: additive fixes only. Config-array constructor, license admin notices, and an explanation line in the plugins-list update row. -
Shazzad\PluginUpdater(src/) — V1, frozen: critical fixes only. Existing plugins keep working unchanged and opt in to a newer major deliberately by bumping the constraint and switching the namespace.
The majors never share classes, so plugins on different library versions coexist on one site without the first-loader-wins fatal V1 was exposed to.
The namespace number matches the server API it calls: V4 talks to wp-repo/v4, and a
future V5 would talk to wp-repo/v5. The one exception is legacy: V1 and V2 both call
wp-repo/v3 (V3 was never released under that name — it became V4 before shipping).
| Namespace | Status | Server API | Guide |
|---|---|---|---|
Shazzad\PluginUpdater\V4 |
Active | wp-repo/v4 (plugins/{uid}) |
docs/v4.md |
Shazzad\PluginUpdater\V2 |
Stable, additive fixes only | wp-repo/v3 (products/{id}, legacy exception) |
docs/v2.md |
Shazzad\PluginUpdater (V1) |
Frozen, critical fixes only | wp-repo/v3 (products/{id}) |
docs/v1-legacy.md |
Minimal example (V4, commercial plugin)
<?php if ( class_exists( \Shazzad\PluginUpdater\V4\Integration::class ) ) { new \Shazzad\PluginUpdater\V4\Integration( [ 'api_url' => 'https://w4dev.com/wp-json/wp-repo/v4', 'file' => __FILE__, 'product_uid' => 'prod_xxxxxxxxxxxxxxxxxxxx', 'license' => true, ] ); }
The free-plugin (V4\Insights) quick start, every config key, and what happens without a
product_uid or with a wp-repo/v3 api_url are in the V4 guide.
Documentation
- V4 guide — free (
V4\Insights) and commercial (V4\Integration) quick starts, moving from V2, config keys, Insights storage/backoff/uninstall, thewp-repo/v4plugins/{uid}and Insights endpoints, hooks, and the free wp.org build strip recipe. - V2 guide — quick start, config keys, product uid, the
wp-repo/v3endpoints including ping, hooks, license notices and update-row message. - V1 guide (legacy) — positional constructor, example
configurations,
setProductUid()guard and license-key migration,setMeta()/setMetaCallback()examples. - CHANGELOG.md — release history.
The rest of this page applies to every version.
Server responses
Your API server should provide updates, details and check_license endpoints (plus
ping for V1/V2 or the Insights track / optout routes for V4). They return the same bodies on
wp-repo/v3 (V1, V2: /products/{product_id}/…) and wp-repo/v4 (V4: /plugins/{uid}/…).
Each version's paths are in its guide; the full v4 contract is docs/v4-api.md in
shazzad/plugin-repo.
Update check
{
"updates": {
"new_version": "2.1.0",
"package": "https://download-url.com/plugin.zip",
"url": "https://plugin-info-url.com",
"tested": "6.4",
"requires": "5.0",
"changelog": "Bug fixes and improvements"
}
}
Plugin details
{
"details": {
"name": "My Plugin",
"version": "2.1.0",
"author": "Developer Name",
"homepage": "https://plugin-website.com",
"sections": {
"description": "Plugin description",
"changelog": "Version history",
"installation": "Installation instructions"
},
"download_link": "https://download-url.com/plugin.zip"
}
}
License verification
{
"license": {
"status": "active",
"expires": "2024-12-31",
"customer_name": "John Doe",
"customer_email": "john@example.com",
"renewal_url": "https://example.com/renew?license={license_code}&email={email}"
}
}
The renewal_url field is optional in the license verification response. When present and the license status is expired, a renewal link is displayed on the admin license page. The URL supports two placeholders that are replaced automatically:
{license_code}— replaced with the stored license key{email}— replaced with thebuyer_emailfrom the license data
A static URL without placeholders (e.g., https://example.com/renew) is also supported.
Request parameters
API requests to updates, details, and check_license include these query parameters:
license: License key (if licensing enabled)
Custom metadata
Every version accepts plugin metadata through setMeta() (static values or closures) and
setMetaCallback() (one callback that builds the whole array); V2 and V4 also take them as
the meta / meta_callback config keys. V1 and V2 send it with each ping, V4 with each
Insights track.
- Static values (strings, numbers) are sent as-is
- Closures are called at each send and the return value is sent, so data is always fresh. Which callables resolve differs per version — see the V1, V2 and V4 guides
- When both are used, the
setMetaCallback()array is built first andsetMeta()entries are merged over it — on a key conflict,setMeta()wins
WordPress update hooks
The Updater of V1, V2 and commercial V4 integrates with the WordPress core update system
using these hooks:
pre_set_site_transient_update_plugins: Inject update informationplugins_api: Provide plugin details for update screenupgrader_package_options: Configure upgrade processupgrader_process_complete: Handle post-update cleanupload-update-core.php: Clear the cached API responses so "Check again" fetches fresh data
Activation/deactivation hooks, admin notices and scheduled tasks differ per version; each guide lists its own.
License admin page
When licensing is enabled, the updater adds an admin page with:
- License key input field
- License status display
- Update availability notifications
- Direct upgrade buttons
- Changelog and upgrade notices
By default, the license page appears under the Plugins menu; an empty parent falls back
to plugins.php. The page is always a submenu (add_submenu_page()) — there is no top-level
option. To customise the parent, use the menu key in V2 and
V4, or the $menu_parent constructor argument in
V1.
Security Features
- Input Sanitization: All user inputs are properly sanitized
- Nonce Verification: WordPress nonces protect admin forms
- Capability Checks: Requires
delete_userscapability for license management (the V4 Insights consent notice requiresmanage_options) - XSS Protection: Output is escaped using WordPress functions
Error Handling
The updater includes comprehensive error handling:
- API connection failures
- Invalid license keys
- Update server timeouts
- Malformed responses
Errors are returned as WP_Error from the Client methods and surfaced on the license admin page; the library does not write to the PHP error log.
Changelog
See CHANGELOG.md for the full release history.
Support
For support and bug reports, please contact your plugin developer or visit the plugin's official support channels.
License
This updater package is typically licensed under the same terms as your main plugin. Check your plugin's license file for specific terms.