Files
wc-licensed-product-client/tmp/openapi.json
magdev 760e1e752a Add update-check endpoint support (v0.2.1)
Implement /update-check endpoint aligned with remote OpenAPI spec:
- Add checkForUpdates() method to LicenseClientInterface
- Add UpdateInfo DTO for update check responses
- Add ProductNotFoundException for product_not_found error
- Update local openapi.json to v0.4.0

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 20:52:12 +01:00

853 lines
28 KiB
JSON

{
"openapi": "3.1.0",
"info": {
"title": "WooCommerce Licensed Product API",
"description": "REST API for validating and managing software licenses bound to domains. This API allows external applications to validate license keys, check license status, and activate licenses on specific domains.\n\n## Response Signing (Optional)\n\nWhen the server is configured with `WC_LICENSE_SERVER_SECRET`, all API responses include cryptographic signatures for tamper protection:\n\n- `X-License-Signature`: HMAC-SHA256 signature of the response\n- `X-License-Timestamp`: Unix timestamp when the response was generated\n\nSignature verification prevents man-in-the-middle attacks and ensures response integrity. Use the `magdev/wc-licensed-product-client` library's `SecureLicenseClient` class to automatically verify signatures.",
"version": "0.4.0",
"contact": {
"name": "Marco Graetsch",
"url": "https://src.bundespruefstelle.ch/magdev",
"email": "magdev3.0@gmail.com"
},
"license": {
"name": "GPL-2.0-or-later",
"url": "https://www.gnu.org/licenses/gpl-2.0.html"
}
},
"servers": [
{
"url": "{baseUrl}/wp-json/wc-licensed-product/v1",
"description": "WordPress REST API endpoint",
"variables": {
"baseUrl": {
"default": "https://example.com",
"description": "The base URL of your WordPress installation"
}
}
}
],
"paths": {
"/validate": {
"post": {
"operationId": "validateLicense",
"summary": "Validate a license key for a domain",
"description": "Validates whether a license key is valid for a specific domain. Checks license status, expiration, and domain binding. This is the primary endpoint for software to verify license validity.",
"tags": ["License Validation"],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ValidateRequest"
},
"example": {
"license_key": "ABCD-1234-EFGH-5678",
"domain": "example.com"
}
},
"application/x-www-form-urlencoded": {
"schema": {
"$ref": "#/components/schemas/ValidateRequest"
}
}
}
},
"responses": {
"200": {
"description": "License is valid for the specified domain",
"headers": {
"X-License-Signature": {
"$ref": "#/components/headers/X-License-Signature"
},
"X-License-Timestamp": {
"$ref": "#/components/headers/X-License-Timestamp"
}
},
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ValidateSuccessResponse"
},
"example": {
"valid": true,
"license": {
"product_id": 123,
"expires_at": "2027-01-21",
"version_id": 5
}
}
}
}
},
"403": {
"description": "License validation failed",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ValidateErrorResponse"
},
"examples": {
"license_not_found": {
"summary": "License key not found",
"value": {
"valid": false,
"error": "license_not_found",
"message": "License key not found."
}
},
"license_revoked": {
"summary": "License has been revoked",
"value": {
"valid": false,
"error": "license_revoked",
"message": "This license has been revoked."
}
},
"license_expired": {
"summary": "License has expired",
"value": {
"valid": false,
"error": "license_expired",
"message": "This license has expired."
}
},
"license_inactive": {
"summary": "License is inactive",
"value": {
"valid": false,
"error": "license_inactive",
"message": "This license is inactive."
}
},
"domain_mismatch": {
"summary": "License not valid for this domain",
"value": {
"valid": false,
"error": "domain_mismatch",
"message": "This license is not valid for this domain."
}
}
}
}
}
},
"429": {
"$ref": "#/components/responses/RateLimitExceeded"
}
}
}
},
"/status": {
"post": {
"operationId": "checkStatus",
"summary": "Get license status information",
"description": "Retrieves detailed status information for a license key, including validity, domain binding, expiration date, and activation counts.",
"tags": ["License Status"],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/StatusRequest"
},
"example": {
"license_key": "ABCD-1234-EFGH-5678"
}
},
"application/x-www-form-urlencoded": {
"schema": {
"$ref": "#/components/schemas/StatusRequest"
}
}
}
},
"responses": {
"200": {
"description": "License status retrieved successfully",
"headers": {
"X-License-Signature": {
"$ref": "#/components/headers/X-License-Signature"
},
"X-License-Timestamp": {
"$ref": "#/components/headers/X-License-Timestamp"
}
},
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/StatusResponse"
},
"example": {
"valid": true,
"status": "active",
"domain": "example.com",
"expires_at": "2027-01-21",
"activations_count": 1,
"max_activations": 3
}
}
}
},
"404": {
"description": "License key not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
},
"example": {
"valid": false,
"error": "license_not_found",
"message": "License key not found."
}
}
}
},
"429": {
"$ref": "#/components/responses/RateLimitExceeded"
}
}
}
},
"/activate": {
"post": {
"operationId": "activateLicense",
"summary": "Activate a license on a domain",
"description": "Activates a license key on a specific domain. If the license is already activated on the same domain, returns success. If activating on a new domain, the old domain binding is replaced (single-domain licenses).",
"tags": ["License Activation"],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ActivateRequest"
},
"example": {
"license_key": "ABCD-1234-EFGH-5678",
"domain": "newdomain.com"
}
},
"application/x-www-form-urlencoded": {
"schema": {
"$ref": "#/components/schemas/ActivateRequest"
}
}
}
},
"responses": {
"200": {
"description": "License activated successfully or already activated",
"headers": {
"X-License-Signature": {
"$ref": "#/components/headers/X-License-Signature"
},
"X-License-Timestamp": {
"$ref": "#/components/headers/X-License-Timestamp"
}
},
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ActivateSuccessResponse"
},
"examples": {
"activated": {
"summary": "License activated on new domain",
"value": {
"success": true,
"message": "License activated successfully."
}
},
"already_activated": {
"summary": "License already activated on this domain",
"value": {
"success": true,
"message": "License is already activated for this domain."
}
}
}
}
}
},
"403": {
"description": "Activation failed due to license restrictions",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
},
"examples": {
"license_invalid": {
"summary": "License is not valid",
"value": {
"success": false,
"error": "license_invalid",
"message": "This license is not valid."
}
},
"max_activations_reached": {
"summary": "Maximum activations reached",
"value": {
"success": false,
"error": "max_activations_reached",
"message": "Maximum number of activations reached."
}
}
}
}
}
},
"404": {
"description": "License key not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
},
"example": {
"success": false,
"error": "license_not_found",
"message": "License key not found."
}
}
}
},
"429": {
"$ref": "#/components/responses/RateLimitExceeded"
},
"500": {
"description": "Server error during activation",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
},
"example": {
"success": false,
"error": "activation_failed",
"message": "Failed to activate license."
}
}
}
}
}
}
},
"/update-check": {
"post": {
"operationId": "checkForUpdates",
"summary": "Check for plugin updates",
"description": "Checks if a newer version of the licensed product is available. Returns WordPress-compatible update information that can be used to integrate with WordPress's native plugin update system.",
"tags": ["Plugin Updates"],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UpdateCheckRequest"
},
"example": {
"license_key": "ABCD-1234-EFGH-5678",
"domain": "example.com",
"plugin_slug": "my-licensed-plugin",
"current_version": "1.0.0"
}
},
"application/x-www-form-urlencoded": {
"schema": {
"$ref": "#/components/schemas/UpdateCheckRequest"
}
}
}
},
"responses": {
"200": {
"description": "Update check completed successfully",
"headers": {
"X-License-Signature": {
"$ref": "#/components/headers/X-License-Signature"
},
"X-License-Timestamp": {
"$ref": "#/components/headers/X-License-Timestamp"
}
},
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UpdateCheckResponse"
},
"examples": {
"update_available": {
"summary": "Update is available",
"value": {
"success": true,
"update_available": true,
"version": "1.2.0",
"slug": "my-licensed-plugin",
"plugin": "my-licensed-plugin/my-licensed-plugin.php",
"download_url": "https://example.com/license-download/123-456-abc123",
"package": "https://example.com/license-download/123-456-abc123",
"last_updated": "2026-01-27",
"tested": "6.7",
"requires": "6.0",
"requires_php": "8.3",
"changelog": "## 1.2.0\n- New feature added\n- Bug fixes",
"package_hash": "sha256:abc123def456...",
"name": "My Licensed Plugin",
"homepage": "https://example.com/product/my-plugin"
}
},
"no_update": {
"summary": "No update available",
"value": {
"success": true,
"update_available": false,
"version": "1.0.0"
}
}
}
}
}
},
"403": {
"description": "License validation failed",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
},
"examples": {
"license_invalid": {
"summary": "License is not valid",
"value": {
"success": false,
"update_available": false,
"error": "license_invalid",
"message": "License validation failed."
}
},
"domain_mismatch": {
"summary": "Domain mismatch",
"value": {
"success": false,
"update_available": false,
"error": "domain_mismatch",
"message": "This license is not valid for this domain."
}
}
}
}
}
},
"404": {
"description": "License or product not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
},
"examples": {
"license_not_found": {
"summary": "License not found",
"value": {
"success": false,
"update_available": false,
"error": "license_not_found",
"message": "License not found."
}
},
"product_not_found": {
"summary": "Product not found",
"value": {
"success": false,
"update_available": false,
"error": "product_not_found",
"message": "Licensed product not found."
}
}
}
}
}
},
"429": {
"$ref": "#/components/responses/RateLimitExceeded"
}
}
}
}
},
"components": {
"schemas": {
"ValidateRequest": {
"type": "object",
"required": ["license_key", "domain"],
"properties": {
"license_key": {
"type": "string",
"description": "The license key to validate (format: XXXX-XXXX-XXXX-XXXX)",
"maxLength": 64,
"example": "ABCD-1234-EFGH-5678"
},
"domain": {
"type": "string",
"description": "The domain to validate the license against",
"maxLength": 255,
"example": "example.com"
}
}
},
"ValidateSuccessResponse": {
"type": "object",
"properties": {
"valid": {
"type": "boolean",
"const": true,
"description": "Indicates the license is valid"
},
"license": {
"type": "object",
"properties": {
"product_id": {
"type": "integer",
"description": "WooCommerce product ID associated with the license"
},
"expires_at": {
"type": ["string", "null"],
"format": "date",
"description": "Expiration date (null for lifetime licenses)"
},
"version_id": {
"type": ["integer", "null"],
"description": "Product version ID if license is bound to a version"
}
}
}
}
},
"ValidateErrorResponse": {
"type": "object",
"properties": {
"valid": {
"type": "boolean",
"const": false,
"description": "Indicates validation failed"
},
"error": {
"type": "string",
"enum": ["license_not_found", "license_revoked", "license_expired", "license_inactive", "domain_mismatch"],
"description": "Error code for programmatic handling"
},
"message": {
"type": "string",
"description": "Human-readable error message"
}
}
},
"StatusRequest": {
"type": "object",
"required": ["license_key"],
"properties": {
"license_key": {
"type": "string",
"description": "The license key to check",
"example": "ABCD-1234-EFGH-5678"
}
}
},
"StatusResponse": {
"type": "object",
"properties": {
"valid": {
"type": "boolean",
"description": "Whether the license is currently valid"
},
"status": {
"type": "string",
"enum": ["active", "inactive", "expired", "revoked"],
"description": "Current license status"
},
"domain": {
"type": "string",
"description": "Domain the license is bound to"
},
"expires_at": {
"type": ["string", "null"],
"format": "date",
"description": "Expiration date (null for lifetime licenses)"
},
"activations_count": {
"type": "integer",
"description": "Current number of activations"
},
"max_activations": {
"type": "integer",
"description": "Maximum allowed activations"
}
}
},
"ActivateRequest": {
"type": "object",
"required": ["license_key", "domain"],
"properties": {
"license_key": {
"type": "string",
"description": "The license key to activate",
"example": "ABCD-1234-EFGH-5678"
},
"domain": {
"type": "string",
"description": "The domain to activate the license on",
"example": "newdomain.com"
}
}
},
"ActivateSuccessResponse": {
"type": "object",
"properties": {
"success": {
"type": "boolean",
"const": true,
"description": "Indicates activation was successful"
},
"message": {
"type": "string",
"description": "Human-readable success message"
}
}
},
"UpdateCheckRequest": {
"type": "object",
"required": ["license_key", "domain"],
"properties": {
"license_key": {
"type": "string",
"description": "The license key to validate (format: XXXX-XXXX-XXXX-XXXX)",
"maxLength": 64,
"example": "ABCD-1234-EFGH-5678"
},
"domain": {
"type": "string",
"description": "The domain the plugin is installed on",
"maxLength": 255,
"example": "example.com"
},
"plugin_slug": {
"type": "string",
"description": "The plugin slug (optional, for identification)",
"example": "my-licensed-plugin"
},
"current_version": {
"type": "string",
"description": "Currently installed version for comparison",
"example": "1.0.0"
}
}
},
"UpdateCheckResponse": {
"type": "object",
"properties": {
"success": {
"type": "boolean",
"description": "Whether the request was successful"
},
"update_available": {
"type": "boolean",
"description": "Whether an update is available"
},
"version": {
"type": "string",
"description": "Latest available version"
},
"slug": {
"type": "string",
"description": "Plugin slug for WordPress"
},
"plugin": {
"type": "string",
"description": "Plugin basename (slug/slug.php)"
},
"download_url": {
"type": "string",
"format": "uri",
"description": "Secure download URL for the update package"
},
"package": {
"type": "string",
"format": "uri",
"description": "Alias for download_url (WordPress compatibility)"
},
"last_updated": {
"type": "string",
"format": "date",
"description": "Date of the latest release"
},
"tested": {
"type": "string",
"description": "Highest WordPress version tested with"
},
"requires": {
"type": "string",
"description": "Minimum required WordPress version"
},
"requires_php": {
"type": "string",
"description": "Minimum required PHP version"
},
"changelog": {
"type": "string",
"description": "Release notes/changelog for the update"
},
"package_hash": {
"type": "string",
"description": "SHA256 hash of the package for integrity verification",
"example": "sha256:abc123..."
},
"name": {
"type": "string",
"description": "Product name"
},
"homepage": {
"type": "string",
"format": "uri",
"description": "Product homepage URL"
},
"icons": {
"type": "object",
"description": "Plugin icons for WordPress admin",
"properties": {
"1x": {
"type": "string",
"format": "uri"
},
"2x": {
"type": "string",
"format": "uri"
}
}
},
"sections": {
"type": "object",
"description": "Content sections for plugin info modal",
"properties": {
"description": {
"type": "string"
},
"changelog": {
"type": "string"
}
}
}
}
},
"ErrorResponse": {
"type": "object",
"properties": {
"success": {
"type": "boolean",
"const": false,
"description": "Indicates the operation failed"
},
"valid": {
"type": "boolean",
"const": false,
"description": "Indicates validation failed (for validation endpoints)"
},
"error": {
"type": "string",
"description": "Error code for programmatic handling"
},
"message": {
"type": "string",
"description": "Human-readable error message"
}
}
},
"RateLimitResponse": {
"type": "object",
"properties": {
"success": {
"type": "boolean",
"const": false
},
"error": {
"type": "string",
"const": "rate_limit_exceeded"
},
"message": {
"type": "string",
"example": "Too many requests. Please try again later."
},
"retry_after": {
"type": "integer",
"description": "Seconds until rate limit resets"
}
}
}
},
"responses": {
"RateLimitExceeded": {
"description": "Rate limit exceeded (30 requests per minute per IP)",
"headers": {
"Retry-After": {
"schema": {
"type": "integer"
},
"description": "Seconds until the rate limit resets"
}
},
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/RateLimitResponse"
},
"example": {
"success": false,
"error": "rate_limit_exceeded",
"message": "Too many requests. Please try again later.",
"retry_after": 45
}
}
}
}
},
"headers": {
"X-License-Signature": {
"description": "HMAC-SHA256 signature of the response body for tamper protection. Only present when server is configured with WC_LICENSE_SERVER_SECRET. Signature format: hex-encoded HMAC-SHA256 of (timestamp + ':' + canonical_json_body) using a per-license derived key.",
"schema": {
"type": "string",
"pattern": "^[a-f0-9]{64}$",
"example": "a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456"
},
"required": false
},
"X-License-Timestamp": {
"description": "Unix timestamp when the response was generated. Used together with X-License-Signature to prevent replay attacks. Only present when server is configured with WC_LICENSE_SERVER_SECRET.",
"schema": {
"type": "string",
"pattern": "^[0-9]+$",
"example": "1737550000"
},
"required": false
}
}
},
"tags": [
{
"name": "License Validation",
"description": "Validate license keys against domains"
},
{
"name": "License Status",
"description": "Check license status and details"
},
{
"name": "License Activation",
"description": "Activate licenses on domains"
},
{
"name": "Plugin Updates",
"description": "Check for plugin updates"
}
]
}