LicenseManager
\Benecaster\License\LicenseManager
The plugin’s cached view of what the licence server has said: plan, entitlements, and add-on access. Reads options only — no API call per request. LicenseValidationCron is what writes them.
⚠⚠ Call addon_is_active() with a show context. The one-argument form is now fatal. Until feature/per-show-addon-entitlements the method took a slug alone; it now takes a required int|string $show_context and a one-argument call raises ArgumentCountError. Add-on code written against the old signature does not degrade — it fatals.
The add-on gates accept either slug form. addon_is_active(), addon_is_granted() and any_show_addon_is_active() normalise the slug they are given, so a bare catalog slug (analytics-dashboard) and a prefixed entitlement slug (benecaster-addon-analytics-dashboard) resolve identically. The conversion belongs in this class because this is where the pricing catalogue’s vocabulary meets the entitlement catalogue’s.
Constructor Dependencies
| Type | Description |
|---|---|
\Benecaster\License\ShowAddonRepository |
Per-show grant cache and local activation set. Defaulted, so existing construction still works. |
Methods
| Method | Visibility | Since | Description |
|---|---|---|---|
addon_is_active( string $addon_slug, int|string $show_context ): bool |
Public | — | Resolves two questions in order: granted (does this show's licence entitle the add-on?) then enabled (has the podcaster left it switched on for this show?). ⚠ $show_context is REQUIRED — a show post ID or _benecaster_show_uuid. Callers that genuinely mean anywhere on this install want any_show_addon_is_active(). |
addon_is_granted( string $addon_slug, int|string $show_context ): bool |
Public | — | The grant half alone, ignoring the podcaster's on/off switch — does this show's licence entitle this add-on? The Add-ons screen needs both halves separately so it can render an owned-but-switched-off add-on rather than hiding it. ⚠ Falls through to the site-wide benecaster_active_addons list when no per-show grant cache exists, so a licence server predating the shows[] field behaves exactly as before. An empty shows array means we know nothing per show, not entitled to nothing — reading it as the latter would blank a healthy licence's add-ons, the very failure this storage exists to prevent. Studio's all-add-ons plan short-circuits to true. |
any_show_addon_is_active( string $addon_slug ): bool |
Public | — | True when the add-on is active on at least one connected show — the site-wide counterpart, mirroring any_show_is_premium(). For consumers with no show in hand: telemetry, boot-time registration, an admin asset decision. ⚠⚠ Never use it to gate content a subscriber sees. Some show on this install has the add-on is not an answer to may this show use it, and substituting one for the other re-opens the cross-licence leak this feature closed. |
catalog_slug_to_entitlement_slug( string $catalog_slug ): string |
— | — | Converts a pricing-catalog slug (email-editor) to the entitlement-system slug (benecaster-addon-email-editor). Idempotent — an already-prefixed slug is returned unchanged. The three add-on gates call it on the way in, so you do not need it to query them; reach for it when you are matching slugs between the two catalogues yourself. |
entitlement_slug_to_catalog_slug( string $entitlement_slug ): string |
— | — | The inverse — benecaster-addon-email-editor to email-editor. Also idempotent. Use it when matching an entitlement back to a pricing-catalog entry for display. |
get_auto_upgrade_enabled(): bool |
Public | — | The cached benecaster_license_auto_upgrade_enabled flag — the customer's auto-upgrade billing consent. ⚠ Defaults to true, not false, because the licence server column is NOT NULL DEFAULT 1: an install with no row yet must not report the customer as opted out. ⚠⚠ READ-ONLY, and the reason is not guessable from the signature. The field is written only by POST /account/preferences on benecaster.com, which is session-authenticated, while the plugin holds a Bearer site token — and a site token lives in a database on the customer's server. A consent to be charged must only ever be changed by someone signed in to the account that gets charged. POST /license/preferences does not accept this field and must not be extended to. ⚠ Two preferences gate an actual upgrade, not one: this flag and the matching enforcement preference must both permit it. |
get_downgrade_eligible(): bool |
Public | — | The cached benecaster_license_downgrade_eligible flag, defaulting to false. ⚠⚠ DISPLAY ONLY. It is named get_ rather than is_ deliberately, so it does not read like permission at a call site — it is one character away from the licence server's real gate, PlanChangeOptions::is_downgrade_eligible(), which is the one that decides. An add-on gating behaviour on this gets a cached opinion that can be a day old. |
get_referral_code(): ?string |
Public | — | The podcaster's referral code from the last daily /validate, or null when the licence server has not supplied one (an unactivated install, or a server that predates referrals). The Refer a Friend page uses null to show its "your link will appear once your license is fully activated" message instead of the link. Reads the cache only, so there is no HTTP call. Also available as benecaster_get_referral_code(). |
get_referral_link(): ?string |
Public | — | The shareable referral URL (https://benecaster.com/ref/{code}) from the last daily /validate, or null when the server has not supplied one. It never changes, so the cached copy is always right. Reads the cache only. Also available as benecaster_get_referral_link(), and printed by the [benecaster_referral_link] shortcode. |
get_referral_credit_cents(): int |
Public | — | The referral credit figure the daily /validate last stored, in whole cents. ⚠⚠ A cache, never to be shown as the podcaster's balance: it can be a day old, stays stale while the licence server is unreachable, and the server sends 0 when it cannot read the figure, so 0 cannot tell "no credit" from "the lookup failed". For display use benecaster_get_live_referral_balance(), which wraps LiveReferralFigures. Also available as benecaster_get_referral_credit_cents(). |
get_referral_conversion_count(): ?int |
Public | — | How many people have signed up, meaning converted and not merely clicked, through the podcaster's referral link, from the last daily /validate. null when the licence server has not sent the field, and null is not zero: the Refer a Friend page hides its "N people have signed up" line rather than claim none. Reads the cache only. |
get_notify_referral_updates(): bool |
Public | — | Whether the podcaster wants the licence server's referral-credit emails (credit earned, credit applied). Returns true when nothing has been answered yet, matching the server's own default. The plugin only caches the value, from /validate and from POST /license/preferences responses, because the licence server owns it and sends the emails. Reads the cache only. Also available as benecaster_get_notify_referral_updates(). |
get_cached_notify_referral_updates(): ?bool |
Public | — | The three-state form of get_notify_referral_updates(): true or false once the licence server has answered, null when it never has. The admin uses null to hide the checkbox rather than show a default nobody confirmed. ⚠ The value is deliberately not cleared when a licence lapses or a show is disconnected: it is the customer's choice, and must survive both rather than reset to "on". |
get_enabled_addons_site_wide(): ?array |
Public | — | A sorted list of the add-on slugs the licence grants that are not switched off on at least one connected show, in entitlement-slug form (benecaster-addon-email-editor), deduplicated across shows. It is what the site uses, as opposed to what it holds, and it feeds the opted-in enabled_addons telemetry field. Returns null (unknown, not "none") when there is no connected show, or on an all-add-ons plan whose server sent no slug list, because such a plan grants every slug and there is nothing local to enumerate. An empty array means every held add-on is switched off everywhere. ⚠ Install-wide by design, like any_show_addon_is_active(). Never use it to gate anything a subscriber sees. |
get_payment_method_valid(): ?bool |
Public | — | Reads the benecaster_license_payment_method_valid wp_option — NOT post meta, despite how it is sometimes described — a three-valued cache of the license server's payment_method_valid field on /validate: '1' maps to true, '0' to false, and an unset or empty stored value (the pre-feature default, and every non-Launch plan today) maps to null for "no answer." First consumer: UpgradePlanDialog's one-click upgrade confirm button, which disables only on an explicit false — null must be treated the same as a good card, never as a bad one. There is no standalone documentation page for the backing option; it is an internal license-state cache, not a public API surface, and is covered here and in the UI spec's Plan changes section instead. |
Constants
| Name | Value | Description |
|---|---|---|
ACCOUNT_URL |
'https://benecaster.com/my-account/' |
The single definition of the customer's account destination, read by InvalidLicenseNotice, Notices\HealthChecks\LicenseServerCheck and the GET /license payload. ⚠ It is NOT the upgrade destination. Several different upgrade URLs still exist elsewhere in the plugin and consolidating them is separate, still-open work — a reader who assumes this constant settled that question will think a live inconsistency is fixed. |
REFERRAL_DASHBOARD_URL |
'https://benecaster.com/my-account/#benecaster-account-referral' |
Where a customer sees who signed up through their referral link: the referral section of their benecaster.com account page, which lists each sign-up by month, plan and credit. The plugin deliberately never shows those details itself. It is ACCOUNT_URL plus the anchor benecaster-account-referral, the licence server's ReferralPanel section id, so renaming that id breaks the link. |
REFERRAL_CONVERSION_COUNT_OPTION |
'benecaster_license_referral_conversion_count' |
The option that caches the referral sign-up count. Read it through get_referral_conversion_count(). |
NOTIFY_REFERRAL_UPDATES_OPTION |
'benecaster_license_notify_referral_updates' |
The option that caches the referral-email preference. Read it through get_notify_referral_updates() or get_cached_notify_referral_updates(). Add-ons must not write it: the emails are sent by the licence server, so only the checkbox, through POST /license/preferences, changes the real preference. |
Notes
Migrating a one-argument addon_is_active() call. Most hooks that could need a show
context already pass one — check the callback signature before concluding you have none.
Where the context is genuinely absent, ask whether the decision is about the install
(registration, assets, telemetry — use any_show_addon_is_active()) or about
entitlement (anything a subscriber sees — thread the show through instead).
⚠ **benecaster_active_addons still exists and is still written, but it is no longer the
source of truth** — only a fall-through for shows with no cached grant. Documentation and
code that treat it as authoritative are now wrong.
Convert at the boundary, not at the call site. Doing it per caller is what produced the
bug this behaviour fixed: AddonActivationController converted and was fine, while
TelemetryCalculator did not and reported the Transcription add-on inactive on every install
that had it. Because the gates normalise, a caller has nothing to convert.
The two halves of addon_is_active() fail in opposite directions, which is why the
normalisation matters on both. The grant check tests membership of an entitlement list, so
an unrecognised slug fails closed. The local switch-off check tests membership of the
podcaster's disabled list — an exclusion — so an unrecognised slug reads as not-disabled
and fails open, ignoring a switch-off the podcaster had made.