Phase 10: REST API
Expose all licensing operations over a versioned REST API with per-key read/write scopes and HTTP Basic authentication.
Files Written
Section titled “Files Written”src/Rest/Auth/BasicAuth.phpsrc/Rest/Controllers/BaseController.phpsrc/Rest/Controllers/LicensesController.phpsrc/Rest/Controllers/ActivationsController.phpsrc/Rest/Controllers/ValidateController.phpsrc/Rest/Controllers/HeartbeatController.phpsrc/Rest/Controllers/RevocationController.phpsrc/Rest/Controllers/GeneratorsController.phpsrc/Rest/Controllers/ApiKeysController.phpsrc/Rest/Controllers/WebhooksController.phpsrc/Rest/Controllers/ReleasesController.phpsrc/Rest/Controllers/SubscriptionsController.phpsrc/Rest/Controllers/AnalyticsController.php
Authentication
Section titled “Authentication”BasicAuth hooks determine_current_user at priority 20.
Request flow:
Authorization: Basic base64(consumer_key:consumer_secret)↓1. Decode header.2. Hash consumer_key with sha256() → look up in wp_wplm_api_keys.3. Verify consumer_secret with hash_equals() against stored bcrypt hash.4. Touch last_access timestamp.5. Store static $current_api_key for permission callbacks.6. Return WP_User for the key owner so WP REST core accepts the request.hash_equals() prevents timing attacks on the secret comparison.
Scoped Permissions
Section titled “Scoped Permissions”API keys carry a permissions field: read, write, or read_write.
BaseController provides three permission callbacks used in register_rest_route():
$this->require_read() // read or read_write key required$this->require_write() // write or read_write key required$this->public_route() // no auth (validate, heartbeat, CRL)Endpoint Summary
Section titled “Endpoint Summary”| Method | Endpoint | Scope |
|---|---|---|
GET | /wplm/v1/licenses | read |
POST | /wplm/v1/licenses | write |
GET | /wplm/v1/licenses/{id} | read |
PUT | /wplm/v1/licenses/{id} | write |
DELETE | /wplm/v1/licenses/{id} | write |
POST | /wplm/v1/validate | public |
POST | /wplm/v1/activate | public |
POST | /wplm/v1/deactivate | public |
POST | /wplm/v1/heartbeat | public |
GET | /wplm/v1/revocation/crl | public |
POST | /wplm/v1/licenses/{id}/revoke | write |
POST | /wplm/v1/licenses/{id}/suspend | write |
POST | /wplm/v1/licenses/{id}/terminate | write |
GET | /wplm/v1/generators | read |
POST | /wplm/v1/generators | write |
POST | /wplm/v1/generators/{id}/generate | write |
GET | /wplm/v1/subscriptions | read |
POST | /wplm/v1/subscriptions/{id}/pause | write |
POST | /wplm/v1/subscriptions/{id}/resume | write |
POST | /wplm/v1/subscriptions/{id}/cancel | write |
GET | /wplm/v1/analytics/overview | read |
GET | /wplm/v1/analytics/anomalies | read |
GET/POST | /wplm/v1/webhooks | read/write |
GET/POST | /wplm/v1/releases | read/write |
Response Format
Section titled “Response Format”All responses use standard WP REST WP_REST_Response. Error responses follow:
{ "code": "wplm_not_found", "message": "License not found.", "data": { "status": 404 }}