Licensor

C# License Client

Complete C# implementation for integrating WiLicensor into .NET applications.

Installation

No external dependencies required. Uses built-in System.Net.Http.

Complete Client Class

using System;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
using System.Management;

namespace YourApp.Licensing
{
    public class LicenseResponse
    {
        public string Key { get; set; }
        public string Condition { get; set; }
        public string Type { get; set; }
        public string MachineId { get; set; }
        public string MachineName { get; set; }
        public int ProductId { get; set; }
        public string Email { get; set; }
        public string Settings { get; set; }
        public DateTime? EndAt { get; set; }
        public DateTime? ActivatedAt { get; set; }
        public string Error { get; set; }

        public bool IsValid => Condition == "activated" || Condition == "purchased";
        public bool IsExpired => Condition == "expired" || (EndAt.HasValue && EndAt.Value < DateTime.UtcNow);
    }

    public class LicenseClient
    {
        private readonly string _baseUrl;
        private readonly string _productName;
        private readonly HttpClient _httpClient;

        public LicenseClient(string baseUrl, string productName)
        {
            _baseUrl = baseUrl.TrimEnd('/');
            _productName = productName;
            _httpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };
        }

        /// <summary>
        /// Check if a license key is valid
        /// </summary>
        public async Task<LicenseResponse> CheckKeyAsync(string licenseKey)
        {
            try
            {
                var url = $"{_baseUrl}/api/check?term={Uri.EscapeDataString(licenseKey)}";
                var response = await _httpClient.GetStringAsync(url);
                return JsonSerializer.Deserialize<LicenseResponse>(response,
                    new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
            }
            catch (Exception ex)
            {
                return new LicenseResponse { Error = ex.Message };
            }
        }

        /// <summary>
        /// Find and activate a license with machine binding
        /// </summary>
        public async Task<LicenseResponse> ActivateLicenseAsync(string email)
        {
            try
            {
                var machineId = GetMachineId();
                var machineName = Environment.MachineName;
                var encryptedKey = EncryptKey.MakePassword(machineId, "358");

                var url = $"{_baseUrl}/api/find?" +
                    $"key={Uri.EscapeDataString(encryptedKey)}" +
                    $"&email={Uri.EscapeDataString(email)}" +
                    $"&product={Uri.EscapeDataString(_productName)}" +
                    $"&machineId={Uri.EscapeDataString(machineId)}" +
                    $"&machineName={Uri.EscapeDataString(machineName)}";

                var response = await _httpClient.GetStringAsync(url);
                return JsonSerializer.Deserialize<LicenseResponse>(response,
                    new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
            }
            catch (Exception ex)
            {
                return new LicenseResponse { Error = ex.Message };
            }
        }

        /// <summary>
        /// Generate or retrieve a trial license
        /// </summary>
        public async Task<LicenseResponse> GetTrialAsync(string email)
        {
            try
            {
                var machineId = GetMachineId();
                var machineName = Environment.MachineName;

                var url = $"{_baseUrl}/api/gen?" +
                    $"product={Uri.EscapeDataString(_productName)}" +
                    $"&machineId={Uri.EscapeDataString(machineId)}" +
                    $"&machineName={Uri.EscapeDataString(machineName)}" +
                    $"&email={Uri.EscapeDataString(email)}";

                var response = await _httpClient.GetStringAsync(url);
                return JsonSerializer.Deserialize<LicenseResponse>(response,
                    new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
            }
            catch (Exception ex)
            {
                return new LicenseResponse { Error = ex.Message };
            }
        }

        /// <summary>
        /// Transfer license to a new machine
        /// </summary>
        public async Task<LicenseResponse> TransferLicenseAsync(string email, string oldMachineId)
        {
            try
            {
                var newMachineId = GetMachineId();
                var machineName = Environment.MachineName;
                var newEncryptedKey = EncryptKey.MakePassword(newMachineId, "358");

                var url = $"{_baseUrl}/api/replace?" +
                    $"key={Uri.EscapeDataString(newEncryptedKey)}" +
                    $"&email={Uri.EscapeDataString(email)}" +
                    $"&product={Uri.EscapeDataString(_productName)}" +
                    $"&machineIdOld={Uri.EscapeDataString(oldMachineId)}" +
                    $"&machineIdNew={Uri.EscapeDataString(newMachineId)}" +
                    $"&machineName={Uri.EscapeDataString(machineName)}";

                var response = await _httpClient.GetStringAsync(url);
                return JsonSerializer.Deserialize<LicenseResponse>(response,
                    new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
            }
            catch (Exception ex)
            {
                return new LicenseResponse { Error = ex.Message };
            }
        }

        /// <summary>
        /// Get unique machine identifier
        /// </summary>
        public static string GetMachineId()
        {
            try
            {
                using (var searcher = new ManagementObjectSearcher("SELECT SerialNumber FROM Win32_BaseBoard"))
                {
                    foreach (var obj in searcher.Get())
                    {
                        return obj["SerialNumber"]?.ToString() ?? "";
                    }
                }
            }
            catch { }

            // Fallback to volume serial
            try
            {
                using (var searcher = new ManagementObjectSearcher("SELECT VolumeSerialNumber FROM Win32_LogicalDisk WHERE DeviceID='C:'"))
                {
                    foreach (var obj in searcher.Get())
                    {
                        return obj["VolumeSerialNumber"]?.ToString() ?? "";
                    }
                }
            }
            catch { }

            return Environment.MachineName;
        }
    }

    /// <summary>
    /// Key encryption algorithm - must match server implementation
    /// </summary>
    public static class EncryptKey
    {
        public static string MakePassword(string st, string identifier)
        {
            if (identifier.Length != 3)
                throw new ArgumentException("Identifier must be 3 characters long");

            int[] num = {
                int.Parse(identifier[0].ToString()),
                int.Parse(identifier[1].ToString()),
                int.Parse(identifier[2].ToString())
            };

            st = Boring(st);
            st = InverseByBase(st, num[0]);
            st = InverseByBase(st, num[1]);
            st = InverseByBase(st, num[2]);

            var sb = new System.Text.StringBuilder();
            foreach (char ch in st)
            {
                sb.Append(ChangeChar(ch, num));
            }
            return sb.ToString();
        }

        private static string InverseByBase(string st, int moveBase)
        {
            if (moveBase == 0) moveBase = 1;
            var sb = new System.Text.StringBuilder();
            for (int i = 0; i < st.Length; i += moveBase)
            {
                int c = Math.Min(moveBase, st.Length - i);
                sb.Append(InverseString(st.Substring(i, c)));
            }
            return sb.ToString();
        }

        private static string InverseString(string st)
        {
            char[] arr = st.ToCharArray();
            Array.Reverse(arr);
            return new string(arr);
        }

        private static string Boring(string st)
        {
            var chars = st.ToCharArray();
            for (int i = 0; i < chars.Length; i++)
            {
                int newPlace = i * (int)chars[i];
                newPlace = newPlace % chars.Length;

                char ch = chars[i];
                var list = new System.Collections.Generic.List<char>(chars);
                list.RemoveAt(i);
                list.Insert(newPlace, ch);
                chars = list.ToArray();
            }
            return new string(chars);
        }

        private static char ChangeChar(char ch, int[] enCode)
        {
            ch = char.ToUpper(ch);
            int chValue = (int)ch;

            if (ch >= 'A' && ch <= 'H')
                return (char)(chValue + 2 * enCode[0]);
            else if (ch >= 'I' && ch <= 'P')
                return (char)(chValue - enCode[2]);
            else if (ch >= 'Q' && ch <= 'Z')
                return (char)(chValue - enCode[1]);
            else if (ch >= '0' && ch <= '4')
                return (char)(chValue + 5);
            else if (ch >= '5' && ch <= '9')
                return (char)(chValue - 5);
            else
                return '0';
        }
    }
}

Usage Examples

Check License at Startup

var client = new LicenseClient("https://license.yourserver.com", "YourProduct");

// Check existing license
var result = await client.CheckKeyAsync(savedLicenseKey);

if (result.IsValid)
{
    Console.WriteLine($"License valid until: {result.EndAt}");
}
else if (result.IsExpired)
{
    Console.WriteLine("License has expired. Please renew.");
}
else
{
    Console.WriteLine($"License error: {result.Error ?? result.Condition}");
}

Activate License

var client = new LicenseClient("https://license.yourserver.com", "YourProduct");

var result = await client.ActivateLicenseAsync("user@example.com");

if (result.IsValid)
{
    // Save license key for future checks
    SaveLicenseKey(result.Key);
    Console.WriteLine($"License activated! Type: {result.Type}");
}
else
{
    Console.WriteLine($"Activation failed: {result.Error}");
}

Request Trial License

var client = new LicenseClient("https://license.yourserver.com", "YourProduct");

var result = await client.GetTrialAsync("user@example.com");

if (result.IsValid)
{
    Console.WriteLine($"Trial started! Expires: {result.EndAt}");
}

NinjaTrader Integration

public class MyStrategy : Strategy
{
    private LicenseClient _licenseClient;
    private bool _isLicensed;

    protected override void OnStateChange()
    {
        if (State == State.SetDefaults)
        {
            Name = "MyStrategy";
        }
        else if (State == State.Configure)
        {
            _licenseClient = new LicenseClient(
                "https://license.yourserver.com",
                "MyStrategy"
            );
        }
        else if (State == State.DataLoaded)
        {
            // Check license asynchronously
            Task.Run(async () =>
            {
                var result = await _licenseClient.ActivateLicenseAsync("user@email.com");
                _isLicensed = result.IsValid;

                if (!_isLicensed)
                {
                    Print($"License validation failed: {result.Error ?? result.Condition}");
                }
            }).Wait();
        }
    }

    protected override void OnBarUpdate()
    {
        if (!_isLicensed)
        {
            return; // Skip if not licensed
        }

        // Your strategy logic here
    }
}

Parsing Settings

The settings field contains custom key-value pairs:

public static Dictionary<string, string> ParseSettings(string settings)
{
    var result = new Dictionary<string, string>();
    if (string.IsNullOrEmpty(settings)) return result;

    // Format: "key1": "value1", "key2": "value2"
    var regex = new System.Text.RegularExpressions.Regex(
        "\"([^\"]+)\"\\s*:\\s*\"([^\"]*)\""
    );

    foreach (System.Text.RegularExpressions.Match match in regex.Matches(settings))
    {
        result[match.Groups[1].Value] = match.Groups[2].Value;
    }

    return result;
}

// Usage
var settings = ParseSettings(result.Settings);
if (settings.TryGetValue("nextSync", out var syncDays))
{
    Console.WriteLine($"Sync every {syncDays} days");
}

Back to Overview | Python Client | JavaScript Client