Implement versions 0.0.4-0.0.7 features

v0.0.4:
- Add WooCommerce settings tab for default license settings
- Per-product settings override global defaults

v0.0.5:
- Add bulk license operations (activate, deactivate, revoke, extend, delete)
- Add license renewal/extension and lifetime functionality
- Add quick action buttons per license row

v0.0.6:
- Add license dashboard with statistics and analytics
- Add license transfer functionality (admin)
- Add CSV export for licenses
- Add OpenAPI 3.1 specification
- Remove /deactivate API endpoint

v0.0.7:
- Move license dashboard to WooCommerce Reports section
- Add license search and filtering in admin
- Add customer-facing license transfer with AJAX modal
- Add email notifications for license expiration warnings
- Add bulk import licenses from CSV
- Update README with comprehensive documentation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-21 20:32:35 +01:00
parent 78e43b9aea
commit 49a0699963
21 changed files with 4132 additions and 289 deletions

538
openapi.json Normal file
View File

@@ -0,0 +1,538 @@
{
"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.",
"version": "0.0.7",
"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",
"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",
"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",
"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."
}
}
}
}
}
}
}
},
"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"
}
}
},
"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
}
}
}
}
}
},
"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"
}
]
}