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:
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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user