You've already forked wc-licensed-product
Implement version 0.0.9 features
- Add API client examples for PHP, Python, JavaScript, curl, and C# - Create comprehensive REST API documentation in docs/client-examples/ - All examples include rate limit handling (HTTP 429) - Examples cover validate, status, and activate endpoints Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
69
docs/client-examples/README.md
Normal file
69
docs/client-examples/README.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# WC Licensed Product API Client Examples
|
||||
|
||||
This directory contains example API clients for integrating with the WC Licensed Product REST API.
|
||||
|
||||
## API Base URL
|
||||
|
||||
```shell
|
||||
https://your-site.com/wp-json/wc-licensed-product/v1
|
||||
```
|
||||
|
||||
## Available Endpoints
|
||||
|
||||
| Endpoint | Method | Description |
|
||||
|----------|--------|-------------|
|
||||
| `/validate` | POST | Validate a license key for a specific domain |
|
||||
| `/status` | POST | Get the current status of a license |
|
||||
| `/activate` | POST | Activate a license on a domain |
|
||||
|
||||
## Rate Limiting
|
||||
|
||||
The API is rate-limited to 30 requests per minute per IP address. If you exceed this limit, you'll receive a `429 Too Many Requests` response with a `Retry-After` header indicating when you can retry.
|
||||
|
||||
## Example Files
|
||||
|
||||
- [curl.sh](curl.sh) - cURL command examples
|
||||
- [php-client.php](php-client.php) - PHP client class
|
||||
- [python-client.py](python-client.py) - Python client class
|
||||
- [javascript-client.js](javascript-client.js) - JavaScript/Node.js client class
|
||||
- [csharp-client.cs](csharp-client.cs) - C# client class
|
||||
|
||||
## Response Format
|
||||
|
||||
All endpoints return JSON responses with the following structure:
|
||||
|
||||
### Success Response (Validate)
|
||||
|
||||
```json
|
||||
{
|
||||
"valid": true,
|
||||
"license": {
|
||||
"status": "active",
|
||||
"domain": "example.com",
|
||||
"expires_at": "2025-12-31",
|
||||
"product_id": 123
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Error Response
|
||||
|
||||
```json
|
||||
{
|
||||
"valid": false,
|
||||
"error": "license_not_found",
|
||||
"message": "License key not found."
|
||||
}
|
||||
```
|
||||
|
||||
## Error Codes
|
||||
|
||||
| Code | Description |
|
||||
| ------ | ------------- |
|
||||
| `license_not_found` | The license key does not exist |
|
||||
| `license_expired` | The license has expired |
|
||||
| `license_revoked` | The license has been revoked |
|
||||
| `license_inactive` | The license is inactive |
|
||||
| `domain_mismatch` | The license is not valid for the provided domain |
|
||||
| `max_activations_reached` | Maximum number of domain activations reached |
|
||||
| `rate_limit_exceeded` | Too many requests, please wait and retry |
|
||||
352
docs/client-examples/csharp-client.cs
Normal file
352
docs/client-examples/csharp-client.cs
Normal file
@@ -0,0 +1,352 @@
|
||||
/**
|
||||
* WC Licensed Product API Client for C#
|
||||
*
|
||||
* A C# client for interacting with the WC Licensed Product REST API.
|
||||
*
|
||||
* Requirements:
|
||||
* - .NET 6.0+ or .NET Framework 4.7.2+
|
||||
* - System.Net.Http
|
||||
* - System.Text.Json (or Newtonsoft.Json)
|
||||
*
|
||||
* Usage:
|
||||
* var client = new WcLicensedProductClient("https://your-site.com");
|
||||
* var result = await client.ValidateAsync("XXXX-XXXX-XXXX-XXXX", "example.com");
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace WcLicensedProduct
|
||||
{
|
||||
/// <summary>
|
||||
/// License status response data
|
||||
/// </summary>
|
||||
public class LicenseStatus
|
||||
{
|
||||
[JsonPropertyName("valid")]
|
||||
public bool Valid { get; set; }
|
||||
|
||||
[JsonPropertyName("status")]
|
||||
public string Status { get; set; }
|
||||
|
||||
[JsonPropertyName("domain")]
|
||||
public string Domain { get; set; }
|
||||
|
||||
[JsonPropertyName("expires_at")]
|
||||
public string ExpiresAt { get; set; }
|
||||
|
||||
[JsonPropertyName("activations_count")]
|
||||
public int ActivationsCount { get; set; }
|
||||
|
||||
[JsonPropertyName("max_activations")]
|
||||
public int MaxActivations { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validation response data
|
||||
/// </summary>
|
||||
public class ValidationResult
|
||||
{
|
||||
[JsonPropertyName("valid")]
|
||||
public bool Valid { get; set; }
|
||||
|
||||
[JsonPropertyName("error")]
|
||||
public string Error { get; set; }
|
||||
|
||||
[JsonPropertyName("message")]
|
||||
public string Message { get; set; }
|
||||
|
||||
[JsonPropertyName("license")]
|
||||
public LicenseInfo License { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// License information in validation response
|
||||
/// </summary>
|
||||
public class LicenseInfo
|
||||
{
|
||||
[JsonPropertyName("status")]
|
||||
public string Status { get; set; }
|
||||
|
||||
[JsonPropertyName("domain")]
|
||||
public string Domain { get; set; }
|
||||
|
||||
[JsonPropertyName("expires_at")]
|
||||
public string ExpiresAt { get; set; }
|
||||
|
||||
[JsonPropertyName("product_id")]
|
||||
public int ProductId { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Activation response data
|
||||
/// </summary>
|
||||
public class ActivationResult
|
||||
{
|
||||
[JsonPropertyName("success")]
|
||||
public bool Success { get; set; }
|
||||
|
||||
[JsonPropertyName("error")]
|
||||
public string Error { get; set; }
|
||||
|
||||
[JsonPropertyName("message")]
|
||||
public string Message { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exception for API errors
|
||||
/// </summary>
|
||||
public class WcLicensedProductException : Exception
|
||||
{
|
||||
public string ErrorCode { get; }
|
||||
public int HttpCode { get; }
|
||||
|
||||
public WcLicensedProductException(string message, string errorCode = null, int httpCode = 0)
|
||||
: base(message)
|
||||
{
|
||||
ErrorCode = errorCode;
|
||||
HttpCode = httpCode;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exception for rate limit errors
|
||||
/// </summary>
|
||||
public class RateLimitException : WcLicensedProductException
|
||||
{
|
||||
public int RetryAfter { get; }
|
||||
|
||||
public RateLimitException(int retryAfter)
|
||||
: base($"Rate limit exceeded. Retry after {retryAfter} seconds.")
|
||||
{
|
||||
RetryAfter = retryAfter;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Client for the WC Licensed Product REST API
|
||||
/// </summary>
|
||||
public class WcLicensedProductClient : IDisposable
|
||||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly string _baseUrl;
|
||||
private readonly JsonSerializerOptions _jsonOptions;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the client
|
||||
/// </summary>
|
||||
/// <param name="siteUrl">Your WordPress site URL (e.g., "https://example.com")</param>
|
||||
/// <param name="timeout">Request timeout (default: 30 seconds)</param>
|
||||
public WcLicensedProductClient(string siteUrl, TimeSpan? timeout = null)
|
||||
{
|
||||
_baseUrl = siteUrl.TrimEnd('/') + "/wp-json/wc-licensed-product/v1";
|
||||
|
||||
_httpClient = new HttpClient
|
||||
{
|
||||
Timeout = timeout ?? TimeSpan.FromSeconds(30)
|
||||
};
|
||||
_httpClient.DefaultRequestHeaders.Accept.Add(
|
||||
new MediaTypeWithQualityHeaderValue("application/json"));
|
||||
|
||||
_jsonOptions = new JsonSerializerOptions
|
||||
{
|
||||
PropertyNameCaseInsensitive = true
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validate a license key for a specific domain
|
||||
/// </summary>
|
||||
/// <param name="licenseKey">The license key to validate</param>
|
||||
/// <param name="domain">The domain to validate against</param>
|
||||
/// <returns>Validation result</returns>
|
||||
public async Task<ValidationResult> ValidateAsync(string licenseKey, string domain)
|
||||
{
|
||||
var data = new { license_key = licenseKey, domain = domain };
|
||||
return await RequestAsync<ValidationResult>("/validate", data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the status of a license
|
||||
/// </summary>
|
||||
/// <param name="licenseKey">The license key to check</param>
|
||||
/// <returns>License status</returns>
|
||||
public async Task<LicenseStatus> StatusAsync(string licenseKey)
|
||||
{
|
||||
var data = new { license_key = licenseKey };
|
||||
return await RequestAsync<LicenseStatus>("/status", data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Activate a license on a domain
|
||||
/// </summary>
|
||||
/// <param name="licenseKey">The license key to activate</param>
|
||||
/// <param name="domain">The domain to activate on</param>
|
||||
/// <returns>Activation result</returns>
|
||||
public async Task<ActivationResult> ActivateAsync(string licenseKey, string domain)
|
||||
{
|
||||
var data = new { license_key = licenseKey, domain = domain };
|
||||
return await RequestAsync<ActivationResult>("/activate", data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make an API request
|
||||
/// </summary>
|
||||
private async Task<T> RequestAsync<T>(string endpoint, object data)
|
||||
{
|
||||
var url = _baseUrl + endpoint;
|
||||
var json = JsonSerializer.Serialize(data);
|
||||
var content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
|
||||
HttpResponseMessage response;
|
||||
try
|
||||
{
|
||||
response = await _httpClient.PostAsync(url, content);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
throw new WcLicensedProductException("Request timeout");
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
throw new WcLicensedProductException($"Request failed: {ex.Message}");
|
||||
}
|
||||
|
||||
// Handle rate limiting
|
||||
if ((int)response.StatusCode == 429)
|
||||
{
|
||||
var retryAfter = 60;
|
||||
if (response.Headers.TryGetValues("Retry-After", out var values))
|
||||
{
|
||||
int.TryParse(string.Join("", values), out retryAfter);
|
||||
}
|
||||
throw new RateLimitException(retryAfter);
|
||||
}
|
||||
|
||||
var responseBody = await response.Content.ReadAsStringAsync();
|
||||
T result;
|
||||
|
||||
try
|
||||
{
|
||||
result = JsonSerializer.Deserialize<T>(responseBody, _jsonOptions);
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
throw new WcLicensedProductException("Invalid JSON response");
|
||||
}
|
||||
|
||||
// Check for API errors (for error responses)
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
var errorResult = JsonSerializer.Deserialize<ValidationResult>(responseBody, _jsonOptions);
|
||||
if (errorResult?.Error != null)
|
||||
{
|
||||
throw new WcLicensedProductException(
|
||||
errorResult.Message ?? "Unknown error",
|
||||
errorResult.Error,
|
||||
(int)response.StatusCode
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_httpClient?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Usage Examples
|
||||
// =========================================================================
|
||||
|
||||
public class Program
|
||||
{
|
||||
public static async Task Main(string[] args)
|
||||
{
|
||||
using var client = new WcLicensedProductClient("https://your-wordpress-site.com");
|
||||
|
||||
// Example 1: Validate a license
|
||||
Console.WriteLine("=== Validate License ===");
|
||||
try
|
||||
{
|
||||
var result = await client.ValidateAsync("XXXX-XXXX-XXXX-XXXX", "myapp.example.com");
|
||||
|
||||
if (result.Valid)
|
||||
{
|
||||
Console.WriteLine("License is valid!");
|
||||
Console.WriteLine($"Status: {result.License?.Status ?? "active"}");
|
||||
Console.WriteLine($"Expires: {result.License?.ExpiresAt ?? "Never"}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"License is not valid: {result.Message ?? "Unknown error"}");
|
||||
}
|
||||
}
|
||||
catch (RateLimitException ex)
|
||||
{
|
||||
Console.WriteLine($"Rate limited. Retry after {ex.RetryAfter} seconds.");
|
||||
}
|
||||
catch (WcLicensedProductException ex)
|
||||
{
|
||||
Console.WriteLine($"Error: {ex.Message}");
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
|
||||
// Example 2: Check license status
|
||||
Console.WriteLine("=== Check License Status ===");
|
||||
try
|
||||
{
|
||||
var status = await client.StatusAsync("XXXX-XXXX-XXXX-XXXX");
|
||||
|
||||
Console.WriteLine($"Valid: {(status.Valid ? "Yes" : "No")}");
|
||||
Console.WriteLine($"Status: {status.Status ?? "unknown"}");
|
||||
Console.WriteLine($"Domain: {status.Domain ?? "None"}");
|
||||
Console.WriteLine($"Expires: {status.ExpiresAt ?? "Never"}");
|
||||
Console.WriteLine($"Activations: {status.ActivationsCount}/{status.MaxActivations}");
|
||||
}
|
||||
catch (RateLimitException ex)
|
||||
{
|
||||
Console.WriteLine($"Rate limited. Retry after {ex.RetryAfter} seconds.");
|
||||
}
|
||||
catch (WcLicensedProductException ex)
|
||||
{
|
||||
Console.WriteLine($"Error: {ex.Message}");
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
|
||||
// Example 3: Activate license on a domain
|
||||
Console.WriteLine("=== Activate License ===");
|
||||
try
|
||||
{
|
||||
var result = await client.ActivateAsync("XXXX-XXXX-XXXX-XXXX", "newsite.example.com");
|
||||
|
||||
if (result.Success)
|
||||
{
|
||||
Console.WriteLine("License activated successfully!");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"Activation failed: {result.Message ?? "Unknown error"}");
|
||||
}
|
||||
}
|
||||
catch (RateLimitException ex)
|
||||
{
|
||||
Console.WriteLine($"Rate limited. Retry after {ex.RetryAfter} seconds.");
|
||||
}
|
||||
catch (WcLicensedProductException ex)
|
||||
{
|
||||
Console.WriteLine($"Error ({ex.ErrorCode}): {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
89
docs/client-examples/curl.sh
Normal file
89
docs/client-examples/curl.sh
Normal file
@@ -0,0 +1,89 @@
|
||||
#!/bin/bash
|
||||
# WC Licensed Product API - cURL Examples
|
||||
#
|
||||
# Replace YOUR_SITE_URL with your WordPress site URL
|
||||
# Replace YOUR_LICENSE_KEY with your actual license key
|
||||
# Replace YOUR_DOMAIN with the domain to validate/activate
|
||||
|
||||
BASE_URL="https://YOUR_SITE_URL/wp-json/wc-licensed-product/v1"
|
||||
LICENSE_KEY="XXXX-XXXX-XXXX-XXXX"
|
||||
DOMAIN="example.com"
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Validate License
|
||||
# ------------------------------------------------------------------------------
|
||||
# Validates if a license key is valid for a specific domain
|
||||
# Returns: valid (bool), license details, or error message
|
||||
|
||||
echo "=== Validate License ==="
|
||||
curl -X POST "${BASE_URL}/validate" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"license_key\": \"${LICENSE_KEY}\",
|
||||
\"domain\": \"${DOMAIN}\"
|
||||
}"
|
||||
echo -e "\n"
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Check License Status
|
||||
# ------------------------------------------------------------------------------
|
||||
# Gets the current status of a license without domain validation
|
||||
# Returns: status, domain, expiration date, activation count
|
||||
|
||||
echo "=== Check License Status ==="
|
||||
curl -X POST "${BASE_URL}/status" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"license_key\": \"${LICENSE_KEY}\"
|
||||
}"
|
||||
echo -e "\n"
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Activate License
|
||||
# ------------------------------------------------------------------------------
|
||||
# Activates a license on a specific domain
|
||||
# Returns: success (bool), message
|
||||
|
||||
echo "=== Activate License ==="
|
||||
curl -X POST "${BASE_URL}/activate" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"license_key\": \"${LICENSE_KEY}\",
|
||||
\"domain\": \"${DOMAIN}\"
|
||||
}"
|
||||
echo -e "\n"
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Example with verbose output for debugging
|
||||
# ------------------------------------------------------------------------------
|
||||
echo "=== Verbose Request (for debugging) ==="
|
||||
curl -v -X POST "${BASE_URL}/validate" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"license_key\": \"${LICENSE_KEY}\",
|
||||
\"domain\": \"${DOMAIN}\"
|
||||
}" 2>&1
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Example handling rate limit (429 response)
|
||||
# ------------------------------------------------------------------------------
|
||||
# The API returns a Retry-After header when rate limited
|
||||
# You can use this header to determine when to retry
|
||||
|
||||
echo "=== Handling Rate Limits ==="
|
||||
response=$(curl -s -w "\n%{http_code}" -X POST "${BASE_URL}/validate" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"license_key\": \"${LICENSE_KEY}\",
|
||||
\"domain\": \"${DOMAIN}\"
|
||||
}")
|
||||
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
body=$(echo "$response" | sed '$d')
|
||||
|
||||
if [ "$http_code" == "429" ]; then
|
||||
echo "Rate limited. Please wait before retrying."
|
||||
echo "Response: $body"
|
||||
else
|
||||
echo "HTTP $http_code: $body"
|
||||
fi
|
||||
220
docs/client-examples/javascript-client.js
Normal file
220
docs/client-examples/javascript-client.js
Normal file
@@ -0,0 +1,220 @@
|
||||
/**
|
||||
* WC Licensed Product API Client for JavaScript
|
||||
*
|
||||
* A JavaScript client for interacting with the WC Licensed Product REST API.
|
||||
* Works in both browser and Node.js environments.
|
||||
*
|
||||
* Browser usage:
|
||||
* <script src="javascript-client.js"></script>
|
||||
* const client = new WcLicensedProductClient('https://your-site.com');
|
||||
* const result = await client.validate('XXXX-XXXX-XXXX-XXXX', 'example.com');
|
||||
*
|
||||
* Node.js usage:
|
||||
* const WcLicensedProductClient = require('./javascript-client');
|
||||
* const client = new WcLicensedProductClient('https://your-site.com');
|
||||
*/
|
||||
|
||||
class WcLicensedProductError extends Error {
|
||||
constructor(message, errorCode = null, httpCode = null) {
|
||||
super(message);
|
||||
this.name = 'WcLicensedProductError';
|
||||
this.errorCode = errorCode;
|
||||
this.httpCode = httpCode;
|
||||
}
|
||||
}
|
||||
|
||||
class RateLimitError extends WcLicensedProductError {
|
||||
constructor(retryAfter) {
|
||||
super(`Rate limit exceeded. Retry after ${retryAfter} seconds.`);
|
||||
this.name = 'RateLimitError';
|
||||
this.retryAfter = retryAfter;
|
||||
}
|
||||
}
|
||||
|
||||
class WcLicensedProductClient {
|
||||
/**
|
||||
* Initialize the client
|
||||
* @param {string} siteUrl - Your WordPress site URL (e.g., 'https://example.com')
|
||||
* @param {Object} options - Optional configuration
|
||||
* @param {number} options.timeout - Request timeout in milliseconds (default: 30000)
|
||||
*/
|
||||
constructor(siteUrl, options = {}) {
|
||||
this.baseUrl = siteUrl.replace(/\/$/, '') + '/wp-json/wc-licensed-product/v1';
|
||||
this.timeout = options.timeout || 30000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a license key for a specific domain
|
||||
* @param {string} licenseKey - The license key to validate
|
||||
* @param {string} domain - The domain to validate against
|
||||
* @returns {Promise<Object>} Response data with 'valid' boolean and license info
|
||||
*/
|
||||
async validate(licenseKey, domain) {
|
||||
return this._request('/validate', {
|
||||
license_key: licenseKey,
|
||||
domain: domain,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the status of a license
|
||||
* @param {string} licenseKey - The license key to check
|
||||
* @returns {Promise<Object>} License status data
|
||||
*/
|
||||
async status(licenseKey) {
|
||||
return this._request('/status', {
|
||||
license_key: licenseKey,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate a license on a domain
|
||||
* @param {string} licenseKey - The license key to activate
|
||||
* @param {string} domain - The domain to activate on
|
||||
* @returns {Promise<Object>} Response with 'success' boolean and message
|
||||
*/
|
||||
async activate(licenseKey, domain) {
|
||||
return this._request('/activate', {
|
||||
license_key: licenseKey,
|
||||
domain: domain,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an API request
|
||||
* @private
|
||||
* @param {string} endpoint - API endpoint path
|
||||
* @param {Object} data - Request data
|
||||
* @returns {Promise<Object>} Decoded response data
|
||||
*/
|
||||
async _request(endpoint, data) {
|
||||
const url = this.baseUrl + endpoint;
|
||||
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
||||
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
signal: controller.signal,
|
||||
});
|
||||
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
// Handle rate limiting
|
||||
if (response.status === 429) {
|
||||
const retryAfter = parseInt(response.headers.get('Retry-After') || '60', 10);
|
||||
throw new RateLimitError(retryAfter);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
// Check for API errors
|
||||
if (response.status >= 400 && result.error) {
|
||||
throw new WcLicensedProductError(
|
||||
result.message || 'Unknown error',
|
||||
result.error,
|
||||
response.status
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
if (error instanceof WcLicensedProductError) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (error.name === 'AbortError') {
|
||||
throw new WcLicensedProductError('Request timeout');
|
||||
}
|
||||
|
||||
throw new WcLicensedProductError(`Request failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Usage Examples
|
||||
// =============================================================================
|
||||
|
||||
// Example usage (uncomment to run):
|
||||
|
||||
/*
|
||||
(async () => {
|
||||
const client = new WcLicensedProductClient('https://your-wordpress-site.com');
|
||||
|
||||
// Example 1: Validate a license
|
||||
console.log('=== Validate License ===');
|
||||
try {
|
||||
const result = await client.validate('XXXX-XXXX-XXXX-XXXX', 'myapp.example.com');
|
||||
|
||||
if (result.valid) {
|
||||
console.log('License is valid!');
|
||||
console.log('Status:', result.license?.status || 'active');
|
||||
console.log('Expires:', result.license?.expires_at || 'Never');
|
||||
} else {
|
||||
console.log('License is not valid:', result.message || 'Unknown error');
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof RateLimitError) {
|
||||
console.log(`Rate limited. Retry after ${error.retryAfter} seconds.`);
|
||||
} else {
|
||||
console.log('Error:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
console.log();
|
||||
|
||||
// Example 2: Check license status
|
||||
console.log('=== Check License Status ===');
|
||||
try {
|
||||
const status = await client.status('XXXX-XXXX-XXXX-XXXX');
|
||||
|
||||
console.log('Valid:', status.valid ? 'Yes' : 'No');
|
||||
console.log('Status:', status.status || 'unknown');
|
||||
console.log('Domain:', status.domain || 'None');
|
||||
console.log('Expires:', status.expires_at || 'Never');
|
||||
console.log('Activations:', `${status.activations_count || 0}/${status.max_activations || 1}`);
|
||||
} catch (error) {
|
||||
if (error instanceof RateLimitError) {
|
||||
console.log(`Rate limited. Retry after ${error.retryAfter} seconds.`);
|
||||
} else {
|
||||
console.log('Error:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
console.log();
|
||||
|
||||
// Example 3: Activate license on a domain
|
||||
console.log('=== Activate License ===');
|
||||
try {
|
||||
const result = await client.activate('XXXX-XXXX-XXXX-XXXX', 'newsite.example.com');
|
||||
|
||||
if (result.success) {
|
||||
console.log('License activated successfully!');
|
||||
} else {
|
||||
console.log('Activation failed:', result.message || 'Unknown error');
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof RateLimitError) {
|
||||
console.log(`Rate limited. Retry after ${error.retryAfter} seconds.`);
|
||||
} else {
|
||||
console.log(`Error (${error.errorCode}):`, error.message);
|
||||
}
|
||||
}
|
||||
})();
|
||||
*/
|
||||
|
||||
// Export for Node.js
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = WcLicensedProductClient;
|
||||
module.exports.WcLicensedProductError = WcLicensedProductError;
|
||||
module.exports.RateLimitError = RateLimitError;
|
||||
}
|
||||
188
docs/client-examples/php-client.php
Normal file
188
docs/client-examples/php-client.php
Normal file
@@ -0,0 +1,188 @@
|
||||
<?php
|
||||
/**
|
||||
* WC Licensed Product API Client for PHP
|
||||
*
|
||||
* A simple PHP client for interacting with the WC Licensed Product REST API.
|
||||
*
|
||||
* Requirements:
|
||||
* - PHP 7.4+
|
||||
* - cURL extension
|
||||
*
|
||||
* Usage:
|
||||
* $client = new WcLicensedProductClient('https://your-site.com');
|
||||
* $result = $client->validate('XXXX-XXXX-XXXX-XXXX', 'example.com');
|
||||
*/
|
||||
|
||||
class WcLicensedProductClient
|
||||
{
|
||||
private string $baseUrl;
|
||||
private int $timeout;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $siteUrl Your WordPress site URL (e.g., 'https://example.com')
|
||||
* @param int $timeout Request timeout in seconds
|
||||
*/
|
||||
public function __construct(string $siteUrl, int $timeout = 30)
|
||||
{
|
||||
$this->baseUrl = rtrim($siteUrl, '/') . '/wp-json/wc-licensed-product/v1';
|
||||
$this->timeout = $timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a license key for a specific domain
|
||||
*
|
||||
* @param string $licenseKey The license key to validate
|
||||
* @param string $domain The domain to validate against
|
||||
* @return array Response data
|
||||
* @throws Exception On request failure
|
||||
*/
|
||||
public function validate(string $licenseKey, string $domain): array
|
||||
{
|
||||
return $this->request('/validate', [
|
||||
'license_key' => $licenseKey,
|
||||
'domain' => $domain,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the status of a license
|
||||
*
|
||||
* @param string $licenseKey The license key to check
|
||||
* @return array Response data with status, domain, expiration, etc.
|
||||
* @throws Exception On request failure
|
||||
*/
|
||||
public function status(string $licenseKey): array
|
||||
{
|
||||
return $this->request('/status', [
|
||||
'license_key' => $licenseKey,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate a license on a domain
|
||||
*
|
||||
* @param string $licenseKey The license key to activate
|
||||
* @param string $domain The domain to activate on
|
||||
* @return array Response data
|
||||
* @throws Exception On request failure
|
||||
*/
|
||||
public function activate(string $licenseKey, string $domain): array
|
||||
{
|
||||
return $this->request('/activate', [
|
||||
'license_key' => $licenseKey,
|
||||
'domain' => $domain,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an API request
|
||||
*
|
||||
* @param string $endpoint API endpoint path
|
||||
* @param array $data Request data
|
||||
* @return array Decoded response data
|
||||
* @throws Exception On request failure
|
||||
*/
|
||||
private function request(string $endpoint, array $data): array
|
||||
{
|
||||
$url = $this->baseUrl . $endpoint;
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_URL => $url,
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_POSTFIELDS => json_encode($data),
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_TIMEOUT => $this->timeout,
|
||||
CURLOPT_HTTPHEADER => [
|
||||
'Content-Type: application/json',
|
||||
'Accept: application/json',
|
||||
],
|
||||
]);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$error = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
if ($response === false) {
|
||||
throw new Exception('cURL error: ' . $error);
|
||||
}
|
||||
|
||||
$decoded = json_decode($response, true);
|
||||
|
||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
throw new Exception('Invalid JSON response: ' . json_last_error_msg());
|
||||
}
|
||||
|
||||
// Add HTTP code to response for easier handling
|
||||
$decoded['_http_code'] = $httpCode;
|
||||
|
||||
return $decoded;
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Usage Examples
|
||||
// =============================================================================
|
||||
|
||||
// Uncomment to run examples:
|
||||
|
||||
/*
|
||||
$client = new WcLicensedProductClient('https://your-wordpress-site.com');
|
||||
|
||||
// Example 1: Validate a license
|
||||
try {
|
||||
$result = $client->validate('XXXX-XXXX-XXXX-XXXX', 'myapp.example.com');
|
||||
|
||||
if ($result['valid']) {
|
||||
echo "License is valid!\n";
|
||||
echo "Status: " . ($result['license']['status'] ?? 'active') . "\n";
|
||||
echo "Expires: " . ($result['license']['expires_at'] ?? 'Never') . "\n";
|
||||
} else {
|
||||
echo "License is not valid: " . ($result['message'] ?? 'Unknown error') . "\n";
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
echo "Error: " . $e->getMessage() . "\n";
|
||||
}
|
||||
|
||||
// Example 2: Check license status
|
||||
try {
|
||||
$status = $client->status('XXXX-XXXX-XXXX-XXXX');
|
||||
|
||||
echo "License Status:\n";
|
||||
echo " Valid: " . ($status['valid'] ? 'Yes' : 'No') . "\n";
|
||||
echo " Status: " . ($status['status'] ?? 'unknown') . "\n";
|
||||
echo " Domain: " . ($status['domain'] ?? 'none') . "\n";
|
||||
echo " Expires: " . ($status['expires_at'] ?? 'Never') . "\n";
|
||||
echo " Activations: " . ($status['activations_count'] ?? 0) . "/" . ($status['max_activations'] ?? 1) . "\n";
|
||||
} catch (Exception $e) {
|
||||
echo "Error: " . $e->getMessage() . "\n";
|
||||
}
|
||||
|
||||
// Example 3: Activate license on a domain
|
||||
try {
|
||||
$result = $client->activate('XXXX-XXXX-XXXX-XXXX', 'newsite.example.com');
|
||||
|
||||
if ($result['success'] ?? false) {
|
||||
echo "License activated successfully!\n";
|
||||
} else {
|
||||
echo "Activation failed: " . ($result['message'] ?? 'Unknown error') . "\n";
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
echo "Error: " . $e->getMessage() . "\n";
|
||||
}
|
||||
|
||||
// Example 4: Handling rate limits
|
||||
try {
|
||||
$result = $client->validate('XXXX-XXXX-XXXX-XXXX', 'example.com');
|
||||
|
||||
if (($result['_http_code'] ?? 200) === 429) {
|
||||
$retryAfter = $result['retry_after'] ?? 60;
|
||||
echo "Rate limited. Retry after {$retryAfter} seconds.\n";
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
echo "Error: " . $e->getMessage() . "\n";
|
||||
}
|
||||
*/
|
||||
233
docs/client-examples/python-client.py
Normal file
233
docs/client-examples/python-client.py
Normal file
@@ -0,0 +1,233 @@
|
||||
"""
|
||||
WC Licensed Product API Client for Python
|
||||
|
||||
A simple Python client for interacting with the WC Licensed Product REST API.
|
||||
|
||||
Requirements:
|
||||
- Python 3.7+
|
||||
- requests library (pip install requests)
|
||||
|
||||
Usage:
|
||||
client = WcLicensedProductClient('https://your-site.com')
|
||||
result = client.validate('XXXX-XXXX-XXXX-XXXX', 'example.com')
|
||||
"""
|
||||
|
||||
import requests
|
||||
from typing import Dict, Any, Optional
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass
|
||||
class LicenseStatus:
|
||||
"""License status response data."""
|
||||
valid: bool
|
||||
status: str
|
||||
domain: Optional[str]
|
||||
expires_at: Optional[str]
|
||||
activations_count: int
|
||||
max_activations: int
|
||||
|
||||
|
||||
class WcLicensedProductError(Exception):
|
||||
"""Base exception for API errors."""
|
||||
def __init__(self, message: str, error_code: str = None, http_code: int = None):
|
||||
super().__init__(message)
|
||||
self.error_code = error_code
|
||||
self.http_code = http_code
|
||||
|
||||
|
||||
class RateLimitError(WcLicensedProductError):
|
||||
"""Raised when rate limit is exceeded."""
|
||||
def __init__(self, retry_after: int):
|
||||
super().__init__(f"Rate limit exceeded. Retry after {retry_after} seconds.")
|
||||
self.retry_after = retry_after
|
||||
|
||||
|
||||
class WcLicensedProductClient:
|
||||
"""Client for the WC Licensed Product REST API."""
|
||||
|
||||
def __init__(self, site_url: str, timeout: int = 30):
|
||||
"""
|
||||
Initialize the client.
|
||||
|
||||
Args:
|
||||
site_url: Your WordPress site URL (e.g., 'https://example.com')
|
||||
timeout: Request timeout in seconds
|
||||
"""
|
||||
self.base_url = site_url.rstrip('/') + '/wp-json/wc-licensed-product/v1'
|
||||
self.timeout = timeout
|
||||
self.session = requests.Session()
|
||||
self.session.headers.update({
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
})
|
||||
|
||||
def validate(self, license_key: str, domain: str) -> Dict[str, Any]:
|
||||
"""
|
||||
Validate a license key for a specific domain.
|
||||
|
||||
Args:
|
||||
license_key: The license key to validate
|
||||
domain: The domain to validate against
|
||||
|
||||
Returns:
|
||||
Dict with 'valid' boolean and additional license info
|
||||
|
||||
Raises:
|
||||
WcLicensedProductError: On API error
|
||||
RateLimitError: When rate limited
|
||||
"""
|
||||
return self._request('/validate', {
|
||||
'license_key': license_key,
|
||||
'domain': domain,
|
||||
})
|
||||
|
||||
def status(self, license_key: str) -> LicenseStatus:
|
||||
"""
|
||||
Get the status of a license.
|
||||
|
||||
Args:
|
||||
license_key: The license key to check
|
||||
|
||||
Returns:
|
||||
LicenseStatus object with license details
|
||||
|
||||
Raises:
|
||||
WcLicensedProductError: On API error
|
||||
RateLimitError: When rate limited
|
||||
"""
|
||||
data = self._request('/status', {
|
||||
'license_key': license_key,
|
||||
})
|
||||
|
||||
return LicenseStatus(
|
||||
valid=data.get('valid', False),
|
||||
status=data.get('status', 'unknown'),
|
||||
domain=data.get('domain'),
|
||||
expires_at=data.get('expires_at'),
|
||||
activations_count=data.get('activations_count', 0),
|
||||
max_activations=data.get('max_activations', 1),
|
||||
)
|
||||
|
||||
def activate(self, license_key: str, domain: str) -> Dict[str, Any]:
|
||||
"""
|
||||
Activate a license on a domain.
|
||||
|
||||
Args:
|
||||
license_key: The license key to activate
|
||||
domain: The domain to activate on
|
||||
|
||||
Returns:
|
||||
Dict with 'success' boolean and message
|
||||
|
||||
Raises:
|
||||
WcLicensedProductError: On API error
|
||||
RateLimitError: When rate limited
|
||||
"""
|
||||
return self._request('/activate', {
|
||||
'license_key': license_key,
|
||||
'domain': domain,
|
||||
})
|
||||
|
||||
def _request(self, endpoint: str, data: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""
|
||||
Make an API request.
|
||||
|
||||
Args:
|
||||
endpoint: API endpoint path
|
||||
data: Request data
|
||||
|
||||
Returns:
|
||||
Decoded response data
|
||||
|
||||
Raises:
|
||||
WcLicensedProductError: On API error
|
||||
RateLimitError: When rate limited
|
||||
"""
|
||||
url = self.base_url + endpoint
|
||||
|
||||
try:
|
||||
response = self.session.post(url, json=data, timeout=self.timeout)
|
||||
except requests.RequestException as e:
|
||||
raise WcLicensedProductError(f"Request failed: {e}")
|
||||
|
||||
# Handle rate limiting
|
||||
if response.status_code == 429:
|
||||
retry_after = int(response.headers.get('Retry-After', 60))
|
||||
raise RateLimitError(retry_after)
|
||||
|
||||
try:
|
||||
result = response.json()
|
||||
except ValueError:
|
||||
raise WcLicensedProductError("Invalid JSON response")
|
||||
|
||||
# Check for API errors
|
||||
if response.status_code >= 400 and 'error' in result:
|
||||
raise WcLicensedProductError(
|
||||
result.get('message', 'Unknown error'),
|
||||
error_code=result.get('error'),
|
||||
http_code=response.status_code,
|
||||
)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Usage Examples
|
||||
# =============================================================================
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Initialize the client
|
||||
client = WcLicensedProductClient('https://your-wordpress-site.com')
|
||||
|
||||
# Example 1: Validate a license
|
||||
print("=== Validate License ===")
|
||||
try:
|
||||
result = client.validate('XXXX-XXXX-XXXX-XXXX', 'myapp.example.com')
|
||||
|
||||
if result.get('valid'):
|
||||
print("License is valid!")
|
||||
print(f"Status: {result.get('license', {}).get('status', 'active')}")
|
||||
print(f"Expires: {result.get('license', {}).get('expires_at', 'Never')}")
|
||||
else:
|
||||
print(f"License is not valid: {result.get('message', 'Unknown error')}")
|
||||
|
||||
except RateLimitError as e:
|
||||
print(f"Rate limited. Retry after {e.retry_after} seconds.")
|
||||
except WcLicensedProductError as e:
|
||||
print(f"Error: {e}")
|
||||
|
||||
print()
|
||||
|
||||
# Example 2: Check license status
|
||||
print("=== Check License Status ===")
|
||||
try:
|
||||
status = client.status('XXXX-XXXX-XXXX-XXXX')
|
||||
|
||||
print(f"Valid: {'Yes' if status.valid else 'No'}")
|
||||
print(f"Status: {status.status}")
|
||||
print(f"Domain: {status.domain or 'None'}")
|
||||
print(f"Expires: {status.expires_at or 'Never'}")
|
||||
print(f"Activations: {status.activations_count}/{status.max_activations}")
|
||||
|
||||
except RateLimitError as e:
|
||||
print(f"Rate limited. Retry after {e.retry_after} seconds.")
|
||||
except WcLicensedProductError as e:
|
||||
print(f"Error: {e}")
|
||||
|
||||
print()
|
||||
|
||||
# Example 3: Activate license on a domain
|
||||
print("=== Activate License ===")
|
||||
try:
|
||||
result = client.activate('XXXX-XXXX-XXXX-XXXX', 'newsite.example.com')
|
||||
|
||||
if result.get('success'):
|
||||
print("License activated successfully!")
|
||||
else:
|
||||
print(f"Activation failed: {result.get('message', 'Unknown error')}")
|
||||
|
||||
except RateLimitError as e:
|
||||
print(f"Rate limited. Retry after {e.retry_after} seconds.")
|
||||
except WcLicensedProductError as e:
|
||||
print(f"Error ({e.error_code}): {e}")
|
||||
Reference in New Issue
Block a user