LicensePushController
\Benecaster\REST\LicensePushController
Inbound receiver for POST /benecaster/v1/license-push. The licence server calls it when a licence-state change happens out of band — an order paid, a subscription upgraded, a plan cancelled, a referral credit applied — and the site should pick the change up sooner than the daily validation cycle. The handler runs a validation cycle immediately and leaves the daily schedule in place.
HMAC is the sole authentication. There is no cookie auth and no nonce: the server signs the raw request body with the site’s push secret and sends the result in X-Benecaster-Signature, and the plugin rejects on mismatch.
⚠ The push secret’s only source is a /validate response. The server includes it and LicenseValidationCron stores it on the way past. Connecting a show over OAuth does not carry it, and nothing provisions it at activation.
The body is signed but not read. Re-running the validation cycle is the canonical “re-read state from the server” action and always fetches the freshest state, so a payload schema would only duplicate /validate.
Constructor Dependencies
| Type | Description |
|---|---|
\Benecaster\License\LicenseValidationCron |
Run immediately on a verified push. The daily wp-cron event is deliberately left scheduled. |
Methods
| Method | Visibility | Since | Description |
|---|---|---|---|
handle( WP_REST_Request $request ): WP_REST_Response|WP_Error |
Public | — | Verifies the signature, collapses repeat deliveries, and runs a validation cycle. Returns 200 {"ok":true} on success, 401 on signature mismatch with no body detail, and 503 not_activated when no push secret is stored. |
Notes
A 503 does not mean the site is unlicensed. It means this install has never completed a
validation ping — the only event that caches the push secret. A freshly OAuth-connected show
is fully activated and still returns 503 until its first /validate lands. The behaviour is
correct and is not a bug to fix: the server treats the 503 as a known retriable state, drops
the push, and the daily validation self-heals the install.
⚠ **The error code on that response is the literal string not_activated, and renaming it
would break the server's handling.** It is narrower than it reads, which is what this note
exists to explain — treat the mismatch as documented rather than as a typo.
The reason a push carries is an operator breadcrumb, never an instruction. The receiver
is deliberately body-opaque, so a push announcing plan_upgraded answered by a /validate
saying cancelled leaves the site cancelled. Nothing in the push can move state on its own.
Delivery is at-least-once and per site token. An install running N shows receives N pushes carrying the same payload; repeats are collapsed so one validation cycle runs, and that cycle already iterates every connected show.