Skip to main content

LicenseClient

\Benecaster\License\LicenseClient

Class Premium

HTTP client for the Benecaster license server API (https://benecaster.com/wp-json/benecaster-license/v1, overridable with BENECASTER_LICENSE_URL for staging). Every public method returns a parsed JSON array on success and null on any transport error, non-2xx HTTP status, or malformed body — a status: "invalid" body at HTTP 200 is a valid application-level response and is returned as-is, not converted to null. upgrade() is the one deliberate exception; see its own entry below.

All endpoints except /pricing, /staging-patterns and /oauth/token are authenticated with a per-show bearer token read from the benecaster_show_token_{show_uuid} wp_option via ShowTokenRepository, so every authenticated call requires a show context (post ID or UUID). The token is sent both as Authorization: Bearer <token> and, mirrored, as X-Benecaster-Token: <token> — some managed WordPress hosts strip the Authorization header, and the mirrored header lets the server accept the request either way. When no token is stored for the given show, the headers are simply omitted and the server answers 401.

Constructor Dependencies

Type Description
\Benecaster\License\ShowTokenRepository Resolves a show context to its stored bearer token. Defaults to a new instance.

Methods

Method Visibility Since Description
validate( array $payload, int|string $show_context ): ?array Public POST /validate — daily validation and optional telemetry payload for a specific connected show.
exchange_oauth_code( string $code, string $verifier ): array Public POST /oauth/token — exchanges an authorization code for a site bearer token. Unlike every other method, returns a shaped array ({ok: true, data} or {ok: false, reason}) rather than null on failure, so the caller can distinguish the documented 400 reason slugs (invalid_grant, pkce_mismatch, site_url_mismatch, site_verification_failed, rate_limited) from a network_error. No auth header — the authorization code itself is the credential.
deactivate( array $payload, int|string $show_context ): ?array Public POST /deactivate — releases a show's activation slot on the license server. Called by LicenseOAuthController. There is deliberately no activate() counterpart — connection is exchange_oauth_code() via OAuthCallbackHandler; the pre-OAuth /activate route and its only caller were removed 2026-08-31.
set_preferences( array $preferences, int|string $show_context ): ?array Public POST /license/preferences — pushes the operator's subscriber-limit and token-cap enforcement choices (auto_upgrade or cap) to the license server, the only place they take effect. Partial updates accepted: omit a key to leave it unchanged. Returns the server's full post-update state, or null on failure — callers must not persist a local value on null.
upgrade( string $target_plan, string $target_interval, int|string $show_context ): array|WP_Error Public POST /license/upgrade — the plugin's one-click, in-place plan upgrade, backing Settings → Account → Plan changes → Upgrade plan. Mints a fresh Idempotency-Key (UUID v4) on every call; a caller must not re-invoke expecting the same key back. Unlike every other method here, a refusal is NOT collapsed to null — the license server's {status, reason, message} body is preserved as a WP_Error whose code is the server's reason slug (or network_error / unknown_error on a transport failure) and whose message is the server's pre-written, customer-facing copy, so the confirmation dialog has something to show. On success (HTTP 200, status: "updated") returns the response array (status, plan, show_limit, full_first_invoice).
get_pricing(): ?array Public GET /pricing — public plan catalog; no authentication required.
get_benchmarks( int|string $show_context ): ?array Public GET /analytics/benchmarks — platform benchmark statistics, authenticated with the per-show bearer token.
get_staging_patterns(): ?array Public GET /staging-patterns — public catalog of canonical staging-hostname glob patterns ({match_rule, patterns, count, updated_at}); no authentication required, deliberately, since the installs that most need this list are the ones with no site token yet.
send_alert( array $payload, int|string $show_context ): bool Public POST /alert — notifies the operator about an extended validation failure. Returns true when the server acknowledged (a non-null parsed response).
send_diagnostic_log( array $payload, int|string $show_context ): ?array Public POST /diagnostic-log — admin-triggered diagnostic snapshot send from Settings → Tools → Support Mode. Payload is gzipped JSON of recent debug log entries and host metadata. Auth is the per-show bearer token; passing the first connected show is sufficient since the customer record resolves server-side from the token.
last_http_status(): ?int Public HTTP status code of the most recent request. Null after a transport-level WP_Error or when no request has been made yet. Used to distinguish an authoritative 401 (token revoked) from a general unreachable-server condition.
last_error_message(): ?string Public The WP_Error message of the most recent request, when it ended in a transport-level failure; null otherwise. LicenseValidationCron's diagnostic failure log uses this to classify a failure as timeout, connection_refused, or unknown, since WordPress's HTTP transports report every transport failure under one generic error code.

Notes

upgrade() is the one method on this client that does not collapse a failure to null. Every other method here answers "did it work?" with null; upgrade() answers "why not?" with a WP_Error carrying the server's own customer-facing message, because the one-click upgrade confirmation dialog has nowhere else to get copy for a refusal (interval mismatch, payment method blocked, etc.). See feature/one-click-upgrade.

There is deliberately no activate() method. Connection is per-show OAuth (exchange_oauth_code()), not a licence-key POST — see deactivate()'s own description above.