Integration Examples
Common patterns and complete examples for integrating WiLicensor.
License Check Flow
+----------------+ +----------------+ +----------------+
| Application | | WiLicensor | | Local Cache |
| Startup | | Server | | |
+-------+--------+ +-------+--------+ +-------+--------+
| | |
| 1. Check local cache | |
|--------------------------------------------->|
| | |
|<---------------------------------------------|
| (cached license found) |
| | |
| 2. Validate with server |
|--------------------->| |
| | |
|<---------------------| |
| (validation result) |
| | |
| 3. Update cache | |
|--------------------------------------------->|
| | |
Offline Grace Period
Handle temporary network failures gracefully:
C#
public class LicenseManager
{
private const int GRACE_PERIOD_DAYS = 7;
private readonly LicenseClient _client;
private readonly string _cacheFile = "license_cache.json";
public async Task<bool> CheckLicenseAsync(string email)
{
// Try online check first
try
{
var result = await _client.ActivateLicenseAsync(email);
if (result.IsValid)
{
SaveToCache(result);
return true;
}
}
catch (HttpRequestException)
{
// Network error - check cache
var cached = LoadFromCache();
if (cached != null)
{
var daysSinceValidation = (DateTime.UtcNow - cached.LastValidated).TotalDays;
if (daysSinceValidation <= GRACE_PERIOD_DAYS)
{
Console.WriteLine($"Offline mode: {GRACE_PERIOD_DAYS - (int)daysSinceValidation} days remaining");
return true;
}
}
}
return false;
}
private void SaveToCache(LicenseResponse response)
{
var cache = new CachedLicense
{
Key = response.Key,
Condition = response.Condition,
Type = response.Type,
EndAt = response.EndAt,
LastValidated = DateTime.UtcNow
};
File.WriteAllText(_cacheFile, JsonSerializer.Serialize(cache));
}
private CachedLicense LoadFromCache()
{
if (!File.Exists(_cacheFile)) return null;
return JsonSerializer.Deserialize<CachedLicense>(File.ReadAllText(_cacheFile));
}
}
public class CachedLicense
{
public string Key { get; set; }
public string Condition { get; set; }
public string Type { get; set; }
public DateTime? EndAt { get; set; }
public DateTime LastValidated { get; set; }
}
Python
import json
import os
from datetime import datetime, timedelta
GRACE_PERIOD_DAYS = 7
CACHE_FILE = "license_cache.json"
def check_license_with_grace(client, email):
"""Check license with offline grace period."""
# Try online check first
try:
result = client.activate_license(email)
if result.is_valid:
save_cache(result)
return True
except Exception as e:
print(f"Network error: {e}")
# Check cache for grace period
cached = load_cache()
if cached:
last_validated = datetime.fromisoformat(cached["last_validated"])
days_since = (datetime.utcnow() - last_validated).days
if days_since <= GRACE_PERIOD_DAYS:
remaining = GRACE_PERIOD_DAYS - days_since
print(f"Offline mode: {remaining} days remaining")
return True
return False
def save_cache(result):
cache = {
"key": result.key,
"condition": result.condition,
"type": result.type,
"end_at": result.end_at.isoformat() if result.end_at else None,
"last_validated": datetime.utcnow().isoformat()
}
with open(CACHE_FILE, "w") as f:
json.dump(cache, f)
def load_cache():
if not os.path.exists(CACHE_FILE):
return None
with open(CACHE_FILE, "r") as f:
return json.load(f)
Feature Flags via Settings
Use the settings field to enable/disable features:
Server Setup
In the WiLicensor admin panel, set the key's settings field:
"features": "basic,reports", "maxUsers": "5", "theme": "dark"
Client Usage
const result = await client.activateLicense(email);
if (result.isValid) {
const settings = parseSettings(result.settings);
// Parse features list
const features = (settings.features || '').split(',').map(f => f.trim());
// Check feature access
const hasReports = features.includes('reports');
const hasAdvanced = features.includes('advanced');
// Get limits
const maxUsers = parseInt(settings.maxUsers || '1', 10);
console.log(`Features: ${features.join(', ')}`);
console.log(`Max users: ${maxUsers}`);
}
Subscription Renewal Check
public async Task CheckSubscriptionAsync(string email)
{
var result = await _client.ActivateLicenseAsync(email);
if (result.Condition == "expired")
{
ShowRenewalDialog(result.EndAt);
return;
}
if (result.Type == "subscription" && result.EndAt.HasValue)
{
var daysRemaining = (result.EndAt.Value - DateTime.UtcNow).TotalDays;
if (daysRemaining <= 7)
{
ShowRenewalWarning((int)daysRemaining);
}
}
}
private void ShowRenewalWarning(int daysRemaining)
{
MessageBox.Show(
$"Your subscription expires in {daysRemaining} days. Please renew to continue using all features.",
"Subscription Expiring",
MessageBoxButtons.OK,
MessageBoxIcon.Information
);
}
Machine Transfer Flow
When a user moves to a new computer:
def transfer_to_new_machine(client, email, old_machine_file):
"""Transfer license from old machine to current machine."""
# Load old machine ID from transferred file
with open(old_machine_file, "r") as f:
old_data = json.load(f)
old_machine_id = old_data["machine_id"]
# Transfer license
result = client.transfer_license(email, old_machine_id)
if result.is_valid:
print("License transferred successfully!")
# Save new machine info
save_license({
"key": result.key,
"machine_id": result.machine_id,
"email": email
})
return True
else:
if "already activated today" in (result.error or ""):
print("Please wait 24 hours before transferring again.")
else:
print(f"Transfer failed: {result.error}")
return False
Multi-Product Licensing
Handle multiple products with a single client:
class MultiProductLicenseManager {
constructor(baseUrl) {
this.baseUrl = baseUrl;
this.licenses = {};
}
async checkProduct(productName, email) {
const client = new LicenseClient(this.baseUrl, productName);
const result = await client.activateLicense(email);
this.licenses[productName] = {
isValid: result.isValid,
type: result.type,
settings: parseSettings(result.settings)
};
return result.isValid;
}
async checkAllProducts(products, email) {
const results = {};
for (const product of products) {
results[product] = await this.checkProduct(product, email);
}
return results;
}
hasAccess(productName) {
return this.licenses[productName]?.isValid || false;
}
getSettings(productName) {
return this.licenses[productName]?.settings || {};
}
}
// Usage
const manager = new MultiProductLicenseManager('https://license.yourserver.com');
const access = await manager.checkAllProducts(
['BasicModule', 'ProModule', 'EnterpriseModule'],
'user@example.com'
);
console.log('Access:', access);
// { BasicModule: true, ProModule: true, EnterpriseModule: false }
if (manager.hasAccess('ProModule')) {
enableProFeatures();
}
Webhook Integration (Server-Side)
If you're receiving webhooks from payment providers:
// Express webhook handler
app.post('/my-webhook/payment', async (req, res) => {
const { email, productName, customerId } = req.body;
// Create license via WiLicensor API (admin endpoint)
try {
const response = await fetch('https://license.yourserver.com/keys/add', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Cookie': 'admin-session-cookie' // Authenticated session
},
body: JSON.stringify({
email: email,
name: customerId,
type: 'lifetime',
productId: await getProductId(productName),
condition: 'purchased'
})
});
if (response.ok) {
// Send license email to customer
await sendLicenseEmail(email, productName);
}
res.json({ success: true });
} catch (error) {
console.error('License creation failed:', error);
res.status(500).json({ error: 'Failed to create license' });
}
});
Error Handling Best Practices
public async Task<LicenseStatus> ValidateLicenseAsync(string email)
{
try
{
var result = await _client.ActivateLicenseAsync(email);
// Handle specific conditions
switch (result.Condition)
{
case "activated":
return new LicenseStatus(true, "License is active");
case "purchased":
return new LicenseStatus(true, "License purchased, pending activation");
case "expired":
var expiredDate = result.EndAt?.ToString("d") ?? "unknown";
return new LicenseStatus(false, $"License expired on {expiredDate}");
case "deactivated":
return new LicenseStatus(false, "License has been deactivated. Contact support.");
default:
return new LicenseStatus(false, $"Unknown status: {result.Condition}");
}
}
catch (HttpRequestException ex)
{
// Network error
return new LicenseStatus(false, "Unable to reach license server. Check your connection.");
}
catch (TaskCanceledException)
{
// Timeout
return new LicenseStatus(false, "License server timeout. Please try again.");
}
catch (JsonException ex)
{
// Invalid response
return new LicenseStatus(false, "Invalid response from license server.");
}
catch (Exception ex)
{
// Unexpected error
Console.WriteLine($"License error: {ex}");
return new LicenseStatus(false, "An unexpected error occurred.");
}
}
public class LicenseStatus
{
public bool IsValid { get; }
public string Message { get; }
public LicenseStatus(bool isValid, string message)
{
IsValid = isValid;
Message = message;
}
}
Back to Overview | C# Client | Python Client | JavaScript Client