Repository navigation
V4: Insights + v4 plugins API client (4.0.0) - #32
Merged
Merged
Conversation
…ntract Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The v4 server addresses a plugin as {api_url}/plugins/{uid}/… and accepts
only the prod_… uid; a numeric id is 404 rest_no_route. So:
- Client and Insights\Client build /plugins/{uid}/{route}.
- The track payload sends plugin_version / plugin_status (the server no
longer reads the product_* names). Config keys and the product_*
properties are unchanged.
- product_uid is required by V4\Integration and V4\Insights: without it a
_doing_it_wrong() notice fires and no update, license or Insights call
is made. get_api_product_key() / get_product_key() no longer fall back
to product_id, which stays for the legacy id-keyed license storage.
- Integration skips the Insights parts without a uid; setProductUid()
builds them later (and keeps the Insights URL in step with the uid).
- The free Insights entry point draws no consent notice without a uid,
and a refused opt-out for a missing uid is not queued for retry.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- README, CLAUDE.md, CHANGELOG: namespace V3 -> V4, release 3.0.0 -> 4.0.0
(the Unreleased section is now 4.0.0; there is no 3.0.0), API URLs
{api_url}/plugins/{uid}/..., track keys plugin_version / plugin_status,
product_uid required, plugin-repo 2.9.0+. References to the frozen
wp-repo/v3 API are unchanged.
- README + CLAUDE.md: the namespace number matches the API version it
calls (V4 <-> wp-repo/v4); V1/V2 <-> wp-repo/v3 is the legacy exception.
- Rename the Insights spec, plan and manual QA sheet to v4 names and fix
the links; the spec and plan get a dated revision note, the QA sheet
points at the current branches and checks.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Nothing can work against wp-repo/v3 (no plugins/{uid} routes) or with a
uid the v4 route pattern rejects, so both entry points now refuse up
front instead of sending requests that can only 404:
- Integration::get_api_error() (empty api_url, v3 api_url, missing or
malformed uid) gates the update Client — check_license / updates /
details return wprepo_no_api_url / wprepo_v3_api_url /
wprepo_no_product_uid and never an invalid_license verdict — and the
Insights parts. The v3 notice now says nothing works on v3.
- Insights\Client::get_config_error() is the same gate for the free
entry point: no consent notice is drawn and nothing is sent.
Insights::opt_in() returns that error without storing consent, token
or cron. Config errors are not retried as failed opt-outs.
- product_uid must match the server's route pattern ^prod_[a-z0-9]+\z;
anything else draws a _doing_it_wrong() (4.0.0) and counts as missing.
- api_url is right-trimmed of '/' once in the Integration constructor,
so ".../v4/" no longer builds "v4//plugins/...".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
last_send was only written on success and the hourly license sync calls
run_daily(), so a track the server refused was re-POSTed (up to 64 KB)
every hour. Now:
- Any 4xx stores {slug}_insights_last_attempt; run_daily() waits
MIN_INTERVAL (20 h) from the later of the last success and the last
refused try. A success clears it.
- 403 wprepo_insights_tracking_disabled also stores the installed plugin
version in {slug}_insights_disabled_version; until the installed
version differs, nothing but `deactivate` is sent (opt-out is a
separate route and is never blocked). The contract: stop sending until
the plugin updates. Other 403s (a proxy/WAF) only get the 20 h wait.
- Network errors and 5xx store nothing and keep retrying as before.
- Insights::uninstall() removes both new options.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The v4 track contract unbinds the install (license_id 0, seat released) when `license` is an empty string, and leaves the binding alone when the key is absent. With licensing on and no code stored (never entered, or deleted on the license page) the payload now carries "license": "" so the seat frees up. Licensing off still sends no license key, and the free entry point (no license callback) never does. Integration::get_insights_license() returns null for "don't send"; Collector sends any string, including ''. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
add_query_arg() does not encode values, so a key holding +, &, # or a space reached the server altered (or truncated at the #). The update Client now rawurlencode()s its query args. V1/V2 unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- README / CLAUDE.md: V4 needs a wp-repo/v4 api_url; on v3 no update, license or Insights call is made. uid shape (prod_ + [a-z0-9]), trailing-slash trim, the two new Insights options and the refusal backoff, license "" on licensed-but-empty installs, query encoding. - README: setProductUid() after construction still leaves the "missing product_uid" notice in debug logs; pass the uid in config. - CHANGELOG 4.0.0 (Unreleased): the new behaviour and fixes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
23c3a97 sent `license: ""` whenever licensing was on and no code could be read, which unbinds the install on the server. A key the site merely cannot read — a V1-shipped plugin configured with a uid but no product_id (the legacy-key migration misses the customer's key), or a failed option read — would have unbound a paying install. Now "" is sent only when a stored code was explicitly removed: Store::delete_license_code() sets {slug}_insights_license_removed when a code was stored and licensing is on; update_license_code() (used by the license page) clears it, and so does the first 2xx track that carried "" (the collector reports it through the new license_sent_callback). No code and no flag: no `license` key, as with licensing off. Insights::uninstall() removes the option. Docs also say the seat is freed on the server's next install/activation recount, not immediately. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… too - The 403 wprepo_insights_tracking_disabled pause is stored as [version, since] and lifts when the installed plugin version changes or after Scheduler::PAUSE_MAX (7 days), so an install resumes if tracking is switched back on server-side without a release. A repeat 403 after expiry starts a new pause. - `deactivate` is no longer exempt: the server's 403 check runs before it would record anything, so sending it only cost a request. Opt-out is a separate call (Client::optout()) and still always goes out. - A paused send returns before any write: one option read per attempt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The README mixed V1, V2 and V4 detail in one 650-line page. It is now an overview: features, install, "Which namespace to use" (with a namespace -> API -> guide table), a minimal V4 example, and what every version shares (server responses, request parameters, metadata merge rules, update hooks, license page, security, errors). Version detail moves to docs/v4.md, docs/v2.md and docs/v1-legacy.md. Corrections made while moving: - V2 file tree said "active"; V2 is stable - "V2 only" hooks and notices also apply to V4 Integration - the product_uid-without-product_id notice fires only with license on, and V4 Integration fires it too (now documented in the V4 guide) The guides now ship in the package (only docs/superpowers and docs/testing stay export-ignored) so the README's links resolve from vendor/. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds the
Shazzad\PluginUpdater\V4namespace: updates, licensing and usage tracking ("Insights") against plugin-repo'swp-repo/v4API (plugin-repo 2.9.0+, contract inshazzad-plugin-repo/docs/v4-api.md). Release: 4.0.0.This is the reverted V3 (#29 → #30), re-landed and renamed V3 → V4 so the updater namespace number matches the API version it calls (V4 ↔
wp-repo/v4; V2 ↔wp-repo/v3stays the one legacy exception). There will be no 3.x.Changes since #29
{api_url}/plugins/{prod_uid}/{updates,details,download,changelog,check_license,track,optout}; track sendsplugin_version/plugin_status.product_uidis required (format^prod_[a-z0-9]+). Without it, or on awp-repo/v3/ emptyapi_url, it logs_doing_it_wrongand makes no HTTP calls: no updates, no licensing, no Insights, no consent notice. Config keys are unchanged (product_uid,product_id);product_idonly keeps the legacy V1 license-storage keys.wprepo_insights_tracking_disabledpauses tracking until the plugin version changes or 7 days pass (opt-out still always goes out).license: ""once, so the server unbinds the install and frees the seat on its next recount. This only happens after an explicit removal, never when a key is merely missing.api_urltrimmed; license key URL-encoded.src/*.php) and V2 (src/V2) are byte-identical tomain.Verification
composer test(PHP 8.3) and PHP 7.4 docker: 497 tests, 1489 assertions (9 risky, pre-existing).shazzad-plugin-updater-testbin/insights-e2e, branchfeature/v4-namespace) against local plugin-repo 2.9.0: 49/49.Before merging
Don't merge or tag 4.0.0 until plugin-repo 2.9.0 is released and deployed to w4dev.com. Live w4dev.com runs 2.6.0, where v4 has no
plugins/routes, so a V4 plugin's update and license checks would 404.🤖 Generated with Claude Code