88 lines
3.0 KiB
C#
88 lines
3.0 KiB
C#
using System.Net.Http;
|
|
using System.Net.Http.Json;
|
|
using System.Text.Json;
|
|
using System.Text.Json.Serialization;
|
|
|
|
namespace ytLive.Services;
|
|
|
|
/// <summary>
|
|
/// Validates LlamaCasty license keys against the Polar.sh customer-portal API.
|
|
/// No auth required for client-side validation — the endpoint is public by design.
|
|
/// </summary>
|
|
public class PolarLicenseService
|
|
{
|
|
private const string ValidateUrl = "https://api.polar.sh/v1/customer-portal/license-keys/validate";
|
|
public const string OrganizationId = "c05fb364-b967-4f6c-adf2-8a144e46d085";
|
|
|
|
private readonly HttpClient _http;
|
|
|
|
public PolarLicenseService(HttpClient? http = null)
|
|
{
|
|
_http = http ?? new HttpClient { Timeout = TimeSpan.FromSeconds(15) };
|
|
}
|
|
|
|
public async Task<LicenseValidationResult> ValidateAsync(string key, CancellationToken ct = default)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(key))
|
|
return new LicenseValidationResult { IsValid = false, Error = "Key is empty" };
|
|
|
|
try
|
|
{
|
|
var request = new { key = key.Trim(), organization_id = OrganizationId };
|
|
var response = await _http.PostAsJsonAsync(ValidateUrl, request, ct);
|
|
|
|
if (!response.IsSuccessStatusCode)
|
|
{
|
|
var body = await response.Content.ReadAsStringAsync(ct);
|
|
return new LicenseValidationResult
|
|
{
|
|
IsValid = false,
|
|
Error = $"Validation failed ({(int)response.StatusCode}): {body}"
|
|
};
|
|
}
|
|
|
|
var result = await response.Content.ReadFromJsonAsync<PolarValidateResponse>(ct);
|
|
if (result == null)
|
|
return new LicenseValidationResult { IsValid = false, Error = "Empty response from Polar" };
|
|
|
|
var status = result.Status?.ToLowerInvariant();
|
|
return new LicenseValidationResult
|
|
{
|
|
IsValid = status == "granted",
|
|
Status = status ?? "unknown",
|
|
ExpiresAt = result.ExpiresAt,
|
|
Error = status == "granted" ? null : $"License status: {status}"
|
|
};
|
|
}
|
|
catch (TaskCanceledException)
|
|
{
|
|
return new LicenseValidationResult { IsValid = false, Error = "Validation timed out" };
|
|
}
|
|
catch (HttpRequestException ex)
|
|
{
|
|
return new LicenseValidationResult { IsValid = false, Error = $"Network error: {ex.Message}" };
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return new LicenseValidationResult { IsValid = false, Error = $"Unexpected error: {ex.Message}" };
|
|
}
|
|
}
|
|
|
|
private sealed class PolarValidateResponse
|
|
{
|
|
[JsonPropertyName("status")]
|
|
public string? Status { get; set; }
|
|
|
|
[JsonPropertyName("expires_at")]
|
|
public DateTime? ExpiresAt { get; set; }
|
|
}
|
|
}
|
|
|
|
public sealed class LicenseValidationResult
|
|
{
|
|
public bool IsValid { get; set; }
|
|
public string? Status { get; set; }
|
|
public DateTime? ExpiresAt { get; set; }
|
|
public string? Error { get; set; }
|
|
}
|