You've already forked wc-licensed-product
- 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>
353 lines
11 KiB
C#
353 lines
11 KiB
C#
/**
|
|
* 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}");
|
|
}
|
|
}
|
|
}
|
|
}
|