LicenseController
\Benecaster\REST\LicenseController
REST controller for licence state, on-demand validation, one-click plan upgrade, and enforcement and telemetry preferences. Registers five routes in the benecaster/v1 namespace.
GET /license returns current licence state read straight from wp_options — no network call. POST /license/validate runs a full validation cycle through LicenseValidationCron::run() and returns the updated state. POST /license/preferences stores the subscriber-limit and token-cap enforcement choices (auto_upgrade or cap), both honoured on every plan including Launch. POST /license/upgrade confirms a one-click plan upgrade from Settings → Account → Plan changes. POST /license/telemetry-preferences stores the two opt-in flags, forcing revenue telemetry off whenever the base opt-in is off.
Connection is not handled here. Connecting and disconnecting a site is per-show OAuth/PKCE in LicenseOAuthController — POST /license/oauth/start and POST /license/oauth/deactivate. Those are the routes the Subscription tab and the setup wizard call.
Constructor Dependencies
| Type | Description |
|---|---|
\Benecaster\License\LicenseManager |
Plan, add-on, referral and enforcement-preference reads. |
\Benecaster\License\LicenseValidationCron |
Runs the validation cycle behind POST /license/validate. |
\Benecaster\License\CapChecker |
Subscriber, show and limit counts for the licence payload. |
\Benecaster\Token\TokenRepository |
Site-wide active and paying token counts. |
Methods
| Method | Visibility | Since | Description |
|---|---|---|---|
register_routes(): void |
Public | — | Registers the five benecaster/v1 licence routes. |
get_license(): WP_REST_Response |
Public | — | Returns current licence state from wp_options. No network call. |
validate(): WP_REST_Response |
Public | — | Triggers an immediate validation cycle and returns the updated licence state. |
subscriber_count(): WP_REST_Response |
Public | — | Returns paying_subscriber_count and total_token_count. Guarded by permission_callback_license_key() — see the notes; unreachable on an OAuth-connected install. |
set_preferences( WP_REST_Request $request ): WP_REST_Response |
Public | — | Stores subscriber_limit_enforcement and token_cap_enforcement. Defaults are auto_upgrade for subscriber enforcement and cap for token enforcement. Returns the resolved values. |
upgrade( WP_REST_Request $request ): WP_REST_Response|WP_Error |
Public | — | Confirms a one-click plan upgrade (Settings → Account → Plan changes → Upgrade plan). Resolves the first connected show's bearer token, calls LicenseClient::upgrade() with target_interval 'monthly' first and retries once with 'annual' only on a 409 interval_mismatch refusal — the controller has no cached billing interval to consult. Persists benecaster_license_plan locally only once the server confirms the new plan, then fires benecaster_license_plan_upgraded when the plan actually changed. Any WP_Error from the client (network failure or a refusal reason/message from the server) is surfaced as a REST error with the server's own customer-facing message where one was supplied. |
set_telemetry_preferences( WP_REST_Request $request ): WP_REST_Response |
Public | — | Stores telemetry_opted_in and revenue_telemetry_opted_in. Revenue opt-in is forced off after the writes whenever the base opt-in is off, so a single request that clears the base flag also clears the revenue flag. |
permission_callback_license_key( WP_REST_Request $request ): bool |
Public | — | Compares the Authorization bearer value against the stored benecaster_license_key with hash_equals(). No nonce and no cookie auth — the intended caller is the license server, not a WordPress user. Returns false when no key is stored. |
Hooks Fired
-
benecaster-license-plan-upgraded
Notes
POST /license/activate and POST /license/deactivate were REMOVED in
feature/retire-legacy-license-key (2026-08-31) and must not be reintroduced. They were the pre-OAuth
licence-key flow: activate() wrote benecaster_license_key and was the only writer of that
option. Nothing reached either route — useActivateLicense() and useDeactivateLicense() existed in
the admin bundle but were imported by no component, and the license server has no pull path that used
them. The constructor's LicenseClient dependency went with them; the class now takes
LicenseManager, LicenseValidationCron, CapChecker, TokenRepository.
Use POST /license/oauth/start and POST /license/oauth/deactivate on LicenseOAuthController
instead.
deactivate() also cleared site-level licence state — plan, add-ons, next renewal date, checkout
URL, referral options and push secret. Nothing does that now, because nothing calls it and per-show
OAuth deactivation is scoped to one show's token by design.
benecaster_license_key is gone entirely as of bugfix/remove-license-key-surface (2026-08-31).
The option is deregistered and the plugin has no reader and no writer for it anywhere. The three
surfaces that read it went with it: permission_callback_license_key(), the license_key_masked
field on GET /license, and DiagnosticLogSender's license_key_hash payload entry.
GET /license/subscriber-count was removed with them. It authenticated by comparing against that
option, which no install has ever held, so the callback returned false unconditionally and the route
could only ever answer 401. Subscriber counts are pushed on POST /validate by
ValidationPayloadBuilder — there has never been a pull path, and the license server was verified
never to call the route. Do not reintroduce it, and in particular do not "fix" the auth by widening it
to the site token: that would turn a route nothing needs into a live inbound surface.
This class registers five routes: /license, /license/validate, /license/preferences,
/license/upgrade, /license/telemetry-preferences. ⚠ **This is the first time that count has been
accurate against the actual route table** — the description above claimed five routes while only four
were ever registered (the removed /license/subscriber-count had already been subtracted, leaving the
claim one high); /license/upgrade (feature/one-click-upgrade, 2026-09-11) is what makes five true.