Python License Client
Complete Python implementation for integrating WiLicensor into Python applications.
Installation
pip install requests
Complete Client Class
import requests
import platform
import subprocess
import re
from dataclasses import dataclass
from datetime import datetime
from typing import Optional, Dict
from urllib.parse import urlencode, quote
@dataclass
class LicenseResponse:
key: str = ""
condition: str = ""
type: str = ""
machine_id: str = ""
machine_name: str = ""
product_id: int = 0
email: str = ""
settings: str = ""
end_at: Optional[datetime] = None
activated_at: Optional[datetime] = None
error: Optional[str] = None
@property
def is_valid(self) -> bool:
return self.condition in ("activated", "purchased")
@property
def is_expired(self) -> bool:
if self.condition == "expired":
return True
if self.end_at and self.end_at < datetime.utcnow():
return True
return False
@classmethod
def from_dict(cls, data: dict) -> "LicenseResponse":
end_at = None
if data.get("endAt"):
try:
end_at = datetime.fromisoformat(data["endAt"].replace("Z", "+00:00"))
except:
pass
activated_at = None
if data.get("activatedAt"):
try:
activated_at = datetime.fromisoformat(data["activatedAt"].replace("Z", "+00:00"))
except:
pass
return cls(
key=data.get("key", ""),
condition=data.get("condition", ""),
type=data.get("type", ""),
machine_id=data.get("machineId", ""),
machine_name=data.get("machineName", ""),
product_id=data.get("productId", 0),
email=data.get("email", ""),
settings=data.get("settings", ""),
end_at=end_at,
activated_at=activated_at,
error=data.get("error")
)
class LicenseClient:
def __init__(self, base_url: str, product_name: str, timeout: int = 30):
self.base_url = base_url.rstrip("/")
self.product_name = product_name
self.timeout = timeout
def check_key(self, license_key: str) -> LicenseResponse:
"""Check if a license key is valid."""
try:
url = f"{self.base_url}/api/check?term={quote(license_key)}"
response = requests.get(url, timeout=self.timeout)
return LicenseResponse.from_dict(response.json())
except Exception as e:
return LicenseResponse(error=str(e))
def activate_license(self, email: str) -> LicenseResponse:
"""Find and activate a license with machine binding."""
try:
machine_id = self.get_machine_id()
machine_name = platform.node()
encrypted_key = EncryptKey.make_password(machine_id, "358")
params = {
"key": encrypted_key,
"email": email,
"product": self.product_name,
"machineId": machine_id,
"machineName": machine_name
}
url = f"{self.base_url}/api/find?{urlencode(params)}"
response = requests.get(url, timeout=self.timeout)
return LicenseResponse.from_dict(response.json())
except Exception as e:
return LicenseResponse(error=str(e))
def get_trial(self, email: str) -> LicenseResponse:
"""Generate or retrieve a trial license."""
try:
machine_id = self.get_machine_id()
machine_name = platform.node()
params = {
"product": self.product_name,
"machineId": machine_id,
"machineName": machine_name,
"email": email
}
url = f"{self.base_url}/api/gen?{urlencode(params)}"
response = requests.get(url, timeout=self.timeout)
return LicenseResponse.from_dict(response.json())
except Exception as e:
return LicenseResponse(error=str(e))
def transfer_license(self, email: str, old_machine_id: str) -> LicenseResponse:
"""Transfer license to a new machine."""
try:
new_machine_id = self.get_machine_id()
machine_name = platform.node()
new_encrypted_key = EncryptKey.make_password(new_machine_id, "358")
params = {
"key": new_encrypted_key,
"email": email,
"product": self.product_name,
"machineIdOld": old_machine_id,
"machineIdNew": new_machine_id,
"machineName": machine_name
}
url = f"{self.base_url}/api/replace?{urlencode(params)}"
response = requests.get(url, timeout=self.timeout)
return LicenseResponse.from_dict(response.json())
except Exception as e:
return LicenseResponse(error=str(e))
@staticmethod
def get_machine_id() -> str:
"""Get unique machine identifier."""
system = platform.system()
if system == "Windows":
try:
output = subprocess.check_output(
"wmic baseboard get serialnumber",
shell=True, stderr=subprocess.DEVNULL
).decode()
lines = [l.strip() for l in output.split("\n") if l.strip()]
if len(lines) > 1:
return lines[1]
except:
pass
try:
output = subprocess.check_output(
"wmic logicaldisk get volumeserialnumber",
shell=True, stderr=subprocess.DEVNULL
).decode()
lines = [l.strip() for l in output.split("\n") if l.strip()]
if len(lines) > 1:
return lines[1]
except:
pass
elif system == "Linux":
try:
with open("/etc/machine-id", "r") as f:
return f.read().strip()
except:
pass
elif system == "Darwin": # macOS
try:
output = subprocess.check_output(
"ioreg -rd1 -c IOPlatformExpertDevice | grep IOPlatformSerialNumber",
shell=True, stderr=subprocess.DEVNULL
).decode()
match = re.search(r'"IOPlatformSerialNumber" = "(.+)"', output)
if match:
return match.group(1)
except:
pass
return platform.node()
class EncryptKey:
"""Key encryption algorithm - must match server implementation."""
@staticmethod
def make_password(st: str, identifier: str) -> str:
if len(identifier) != 3:
raise ValueError("Identifier must be 3 characters long")
num = [int(identifier[0]), int(identifier[1]), int(identifier[2])]
st = EncryptKey._boring(st)
st = EncryptKey._inverse_by_base(st, num[0])
st = EncryptKey._inverse_by_base(st, num[1])
st = EncryptKey._inverse_by_base(st, num[2])
result = ""
for ch in st:
result += EncryptKey._change_char(ch, num)
return result
@staticmethod
def _inverse_by_base(st: str, move_base: int) -> str:
if move_base == 0:
move_base = 1
result = ""
for i in range(0, len(st), move_base):
c = min(move_base, len(st) - i)
result += st[i:i+c][::-1]
return result
@staticmethod
def _boring(st: str) -> str:
chars = list(st)
for i in range(len(chars)):
new_place = (i * ord(chars[i])) % len(chars)
ch = chars[i]
chars.pop(i)
chars.insert(new_place, ch)
return "".join(chars)
@staticmethod
def _change_char(ch: str, en_code: list) -> str:
ch = ch.upper()
ch_value = ord(ch)
if 'A' <= ch <= 'H':
return chr(ch_value + 2 * en_code[0])
elif 'I' <= ch <= 'P':
return chr(ch_value - en_code[2])
elif 'Q' <= ch <= 'Z':
return chr(ch_value - en_code[1])
elif '0' <= ch <= '4':
return chr(ch_value + 5)
elif '5' <= ch <= '9':
return chr(ch_value - 5)
else:
return '0'
def parse_settings(settings: str) -> Dict[str, str]:
"""Parse settings string into dictionary."""
result = {}
if not settings:
return result
# Format: "key1": "value1", "key2": "value2"
pattern = r'"([^"]+)"\s*:\s*"([^"]*)"'
for match in re.finditer(pattern, settings):
result[match.group(1)] = match.group(2)
return result
Usage Examples
Check License at Startup
client = LicenseClient("https://license.yourserver.com", "YourProduct")
# Check existing license
result = client.check_key(saved_license_key)
if result.is_valid:
print(f"License valid until: {result.end_at}")
elif result.is_expired:
print("License has expired. Please renew.")
else:
print(f"License error: {result.error or result.condition}")
Activate License
client = LicenseClient("https://license.yourserver.com", "YourProduct")
result = client.activate_license("user@example.com")
if result.is_valid:
# Save license key for future checks
save_license_key(result.key)
print(f"License activated! Type: {result.type}")
else:
print(f"Activation failed: {result.error}")
Request Trial License
client = LicenseClient("https://license.yourserver.com", "YourProduct")
result = client.get_trial("user@example.com")
if result.is_valid:
print(f"Trial started! Expires: {result.end_at}")
Full Application Example
import json
import os
LICENSE_FILE = "license.json"
def load_saved_license():
if os.path.exists(LICENSE_FILE):
with open(LICENSE_FILE, "r") as f:
return json.load(f)
return None
def save_license(license_data):
with open(LICENSE_FILE, "w") as f:
json.dump({
"key": license_data.key,
"email": license_data.email,
"machine_id": license_data.machine_id
}, f)
def check_license(email: str) -> bool:
client = LicenseClient("https://license.yourserver.com", "YourProduct")
# Try saved license first
saved = load_saved_license()
if saved:
result = client.check_key(saved["key"])
if result.is_valid:
print(f"License valid. Type: {result.type}")
return True
elif result.is_expired:
print("License expired. Attempting reactivation...")
# Try to activate
result = client.activate_license(email)
if result.is_valid:
save_license(result)
print(f"License activated! Expires: {result.end_at}")
return True
# Try trial
result = client.get_trial(email)
if result.is_valid:
save_license(result)
print(f"Trial activated! Expires: {result.end_at}")
return True
print(f"License check failed: {result.error or result.condition}")
return False
if __name__ == "__main__":
if check_license("user@example.com"):
print("Application starting...")
# Your application code here
else:
print("Cannot start without valid license.")
Using Settings
result = client.activate_license("user@example.com")
if result.is_valid:
settings = parse_settings(result.settings)
sync_days = int(settings.get("nextSync", "7"))
grace_period = int(settings.get("GracePeriodDays", "0"))
custom_message = settings.get("customMessage", "")
print(f"Sync every {sync_days} days")
if custom_message:
print(f"Message: {custom_message}")