License Manager Documentation

Complete integration guides for all programming languages

Overview

API Endpoint

POST /api/v1/license/verify

Verify license status and get detailed information about validity, expiry, and installation counts.

Request Parameters

  • license_key - The license key to verify (string, required)
  • app_identifier - Your app name or ID (string, required)
  • instance_id - Unique installation identifier (string, required)
  • device_fingerprint - Device identifier (string, optional)
  • user_agent - Browser/client info (string, optional)

Response Format (Valid License)

{
  "status": "valid",
  "license_key": "LIC-ABC123-DEF456-XYZ789",
  "product_name": "My Software Pro",
  "customer_name": "John Doe",
  "customer_email": "john@example.com",
  "expires_at": "2025-12-31T23:59:59Z",
  "days_remaining": 315,
  "max_installations": 3,
  "current_installations": 1,
  "timestamp": 1708956000000,
  "signature": "abc123def456..."
}

Possible Response Statuses

  • valid - License is active and valid (HTTP 200)
  • inactive - License is inactive or suspended (HTTP 403)
  • expired - License has expired (HTTP 410)
  • limit_exceeded - Max installations reached (HTTP 429)
  • invalid - License not found (HTTP 404)
  • error - Server error (HTTP 500)

Implementation Guides

JavaScript / Node.js

async function verifyLicense(licenseKey, appId, instanceId) {
  try {
    const response = await fetch('https://your-license-server.com/api/v1/license/verify', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        license_key: licenseKey,
        app_identifier: appId,
        instance_id: instanceId,
        user_agent: navigator.userAgent,
      }),
    });

    if (!response.ok) {
      const error = await response.json();
      console.error('License verification failed:', error.error);
      return { valid: false, error: error.error };
    }

    const data = await response.json();
    
    // Verify signature (optional but recommended)
    if (!verifySignature(data)) {
      console.error('Invalid response signature');
      return { valid: false, error: 'Invalid signature' };
    }

    if (data.status === 'valid') {
      console.log(`License valid until ${data.expires_at}`);
      return { valid: true, data };
    }

    return { valid: false, error: data.error };
  } catch (error) {
    console.error('Verification error:', error);
    return { valid: false, error: 'Network error' };
  }
}

// Check license on app startup
document.addEventListener('DOMContentLoaded', async () => {
  const instanceId = localStorage.getItem('app_instance_id') || 
                    generateUniqueId();
  localStorage.setItem('app_instance_id', instanceId);
  
  const result = await verifyLicense(LICENSE_KEY, 'MyApp', instanceId);
  
  if (!result.valid) {
    // Show license error, disable features, etc.
    showLicenseError(result.error);
  }
});

function generateUniqueId() {
  return 'inst_' + Math.random().toString(36).substr(2, 9);
}

Installation: No additional dependencies needed. Uses native fetch API.

TypeScript

interface VerificationRequest {
  license_key: string;
  app_identifier: string;
  instance_id: string;
  device_fingerprint?: string;
  user_agent?: string;
}

interface VerificationResponse {
  status: 'valid' | 'invalid' | 'expired' | 'inactive' | 'limit_exceeded' | 'error';
  license_key?: string;
  product_name?: string;
  customer_name?: string;
  customer_email?: string;
  expires_at?: string;
  days_remaining?: number;
  max_installations?: number;
  current_installations?: number;
  timestamp: number;
  signature?: string;
  error?: string;
}

export class LicenseManager {
  private apiUrl: string;
  private licenseKey: string;
  private instanceId: string;
  private appId: string;

  constructor(apiUrl: string, appId: string, licenseKey: string) {
    this.apiUrl = apiUrl;
    this.appId = appId;
    this.licenseKey = licenseKey;
    this.instanceId = this.getOrCreateInstanceId();
  }

  async verify(): Promise<VerificationResponse> {
    const request: VerificationRequest = {
      license_key: this.licenseKey,
      app_identifier: this.appId,
      instance_id: this.instanceId,
      user_agent: navigator.userAgent,
    };

    try {
      const response = await fetch(`${this.apiUrl}/api/v1/license/verify`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(request),
      });

      if (!response.ok) {
        return await response.json();
      }

      return await response.json();
    } catch (error) {
      return {
        status: 'error',
        error: 'Network request failed',
        timestamp: Date.now(),
      };
    }
  }

  isLicenseValid(response: VerificationResponse): boolean {
    return response.status === 'valid';
  }

  isExpiringSoon(response: VerificationResponse): boolean {
    return response.days_remaining ? response.days_remaining < 30 : false;
  }

  private getOrCreateInstanceId(): string {
    const stored = localStorage.getItem('license_instance_id');
    if (stored) return stored;

    const newId = `inst_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
    localStorage.setItem('license_instance_id', newId);
    return newId;
  }
}

Usage: Provides type-safe license verification with session management.

PHP

<?php

class LicenseManager {
    private string $apiUrl;
    private string $licenseKey;
    private string $appId;

    public function __construct(string $apiUrl, string $appId, string $licenseKey) {
        $this->apiUrl = rtrim($apiUrl, '/');
        $this->appId = $appId;
        $this->licenseKey = $licenseKey;
    }

    public function verify(): array {
        $instanceId = $this->getOrCreateInstanceId();
        
        $payload = [
            'license_key' => $this->licenseKey,
            'app_identifier' => $this->appId,
            'instance_id' => $instanceId,
            'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
        ];

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $this->apiUrl . '/api/v1/license/verify');
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Content-Type: application/json',
            'Accept: application/json',
        ]);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, 10);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($response === false) {
            return ['status' => 'error', 'error' => 'Network request failed'];
        }

        return json_decode($response, true) ?? ['status' => 'error'];
    }

    public function isValid(array $response): bool {
        return ($response['status'] ?? '') === 'valid';
    }

    public function isExpiringSoon(array $response): bool {
        return ($response['days_remaining'] ?? 999) < 30;
    }

    private function getOrCreateInstanceId(): string {
        $storageFile = sys_get_temp_dir() . '/license_instance_id_' . md5($this->appId);
        
        if (file_exists($storageFile)) {
            return trim(file_get_contents($storageFile));
        }

        $instanceId = 'inst_' . time() . '_' . bin2hex(random_bytes(8));
        file_put_contents($storageFile, $instanceId);
        return $instanceId;
    }
}

// Usage example:
$manager = new LicenseManager(
    'https://your-license-server.com',
    'MyApp',
    'LIC-YOUR-LICENSE-KEY'
);

$response = $manager->verify();

if ($manager->isValid($response)) {
    echo "License is valid until: " . $response['expires_at'];
} else {
    echo "License verification failed: " . $response['error'];
}

if ($manager->isExpiringSoon($response)) {
    echo "Warning: License expires in {$response['days_remaining']} days";
}
?>

Requirements: PHP 8.0+ with cURL extension enabled.

Python

import requests
import json
import uuid
import hashlib
from pathlib import Path
from datetime import datetime
from typing import Dict, Any

class LicenseManager:
    def __init__(self, api_url: str, app_id: str, license_key: str):
        self.api_url = api_url.rstrip('/')
        self.app_id = app_id
        self.license_key = license_key
        self.session = requests.Session()
        self.session.timeout = 10

    def verify(self) -> Dict[str, Any]:
        instance_id = self._get_or_create_instance_id()
        
        payload = {
            'license_key': self.license_key,
            'app_identifier': self.app_id,
            'instance_id': instance_id,
        }

        try:
            response = self.session.post(
                f'{self.api_url}/api/v1/license/verify',
                json=payload,
                headers={'Content-Type': 'application/json'},
            )

            if response.status_code == 200:
                return response.json()
            else:
                return response.json() if response.text else {
                    'status': 'error',
                    'error': f'HTTP {response.status_code}'
                }
        except requests.RequestException as e:
            return {'status': 'error', 'error': str(e)}

    @staticmethod
    def is_valid(response: Dict[str, Any]) -> bool:
        return response.get('status') == 'valid'

    @staticmethod
    def is_expiring_soon(response: Dict[str, Any]) -> bool:
        days_remaining = response.get('days_remaining', 999)
        return days_remaining < 30

    def _get_or_create_instance_id(self) -> str:
        storage_file = Path.home() / f'.license_instance_{self.app_id}'
        
        if storage_file.exists():
            return storage_file.read_text().strip()

        instance_id = f'inst_{uuid.uuid4().hex[:16]}'
        storage_file.write_text(instance_id)
        return instance_id


# Usage example:
if __name__ == '__main__':
    manager = LicenseManager(
        'https://your-license-server.com',
        'MyApp',
        'LIC-YOUR-LICENSE-KEY'
    )

    response = manager.verify()

    if manager.is_valid(response):
        print(f"License valid until: {response['expires_at']}")
        print(f"Days remaining: {response['days_remaining']}")
    else:
        print(f"License verification failed: {response.get('error')}")

    if manager.is_expiring_soon(response):
        print(f"Warning: License expires in {response['days_remaining']} days")

Installation: pip install requests

Java

import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;
import com.google.gson.Gson;
import com.google.gson.JsonObject;

public class LicenseManager {
    private final String apiUrl;
    private final String appId;
    private final String licenseKey;
    private final HttpClient httpClient;
    private final Gson gson;

    public LicenseManager(String apiUrl, String appId, String licenseKey) {
        this.apiUrl = apiUrl.replaceAll("/$", "");
        this.appId = appId;
        this.licenseKey = licenseKey;
        this.httpClient = HttpClient.newHttpClient();
        this.gson = new Gson();
    }

    public Map<String, Object> verify() {
        try {
            String instanceId = getOrCreateInstanceId();
            
            JsonObject payload = new JsonObject();
            payload.addProperty("license_key", licenseKey);
            payload.addProperty("app_identifier", appId);
            payload.addProperty("instance_id", instanceId);

            HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(apiUrl + "/api/v1/license/verify"))
                .header("Content-Type", "application/json")
                .POST(HttpRequest.BodyPublishers.ofString(
                    gson.toJson(payload)
                ))
                .timeout(java.time.Duration.ofSeconds(10))
                .build();

            HttpResponse<String> response = httpClient.send(
                request,
                HttpResponse.BodyHandlers.ofString()
            );

            return gson.fromJson(
                response.body(),
                Map.class
            );
        } catch (Exception e) {
            return Map.of(
                "status", "error",
                "error", e.getMessage()
            );
        }
    }

    public static boolean isValid(Map<String, Object> response) {
        return "valid".equals(response.get("status"));
    }

    public static boolean isExpiringSoon(Map<String, Object> response) {
        Object daysObj = response.get("days_remaining");
        int daysRemaining = daysObj instanceof Number ? 
            ((Number) daysObj).intValue() : 999;
        return daysRemaining < 30;
    }

    private String getOrCreateInstanceId() throws Exception {
        Path storageFile = Path.of(
            System.getProperty("user.home"),
            ".license_instance_" + appId
        );

        if (Files.exists(storageFile)) {
            return Files.readString(storageFile).trim();
        }

        String instanceId = "inst_" + System.currentTimeMillis() + 
                           "_" + java.util.UUID.randomUUID().toString().substring(0, 8);
        Files.writeString(storageFile, instanceId);
        return instanceId;
    }

    // Usage example:
    public static void main(String[] args) {
        LicenseManager manager = new LicenseManager(
            "https://your-license-server.com",
            "MyApp",
            "LIC-YOUR-LICENSE-KEY"
        );

        Map<String, Object> response = manager.verify();

        if (isValid(response)) {
            System.out.println("License valid until: " + response.get("expires_at"));
        } else {
            System.out.println("License verification failed: " + response.get("error"));
        }

        if (isExpiringSoon(response)) {
            System.out.println("Warning: License expires in " + 
                response.get("days_remaining") + " days");
        }
    }
}

Dependencies: Add GSON library for JSON parsing.

Security & Best Practices

1. Caching Verification Results

Cache license verification for 24 hours to reduce server calls:

// Store response with timestamp
const lastVerification = JSON.parse(localStorage.getItem('license_check'));
const now = Date.now();

if (lastVerification && now - lastVerification.timestamp < 24 * 60 * 60 * 1000) {
  // Use cached result
  return lastVerification.data;
} else {
  // Perform fresh verification
}

2. Signature Verification

Verify the HMAC signature in the response to ensure authenticity:

const crypto = require('crypto');

function verifySignature(response, secret) {
  const { license_key, status, timestamp, signature } = response;
  const data = `${license_key}:${status}:${timestamp}`;
  
  const computed = crypto
    .createHmac('sha256', secret)
    .update(data)
    .digest('hex');
  
  return computed === signature;
}

3. Graceful Degradation

Always handle network failures gracefully:

// On verification failure, allow access with a warning
try {
  const result = await verifyLicense();
  if (!result.valid) {
    showWarning('License verification failed. Features may be limited.');
  }
} catch (error) {
  // Network error - allow access but warn user
  console.warn('Could not verify license. Assuming valid.');
}

4. Secure Storage

  • Never hardcode license keys in source code
  • Use environment variables for sensitive data
  • Don't transmit license keys over unencrypted connections
  • Store instance IDs locally, not sensitive license data

5. Rate Limiting

Implement exponential backoff for failed requests:

async function verifyWithRetry(licenseKey, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await verifyLicense(licenseKey);
    } catch (error) {
      if (i < maxRetries - 1) {
        const delay = Math.pow(2, i) * 1000;
        await new Promise(r => setTimeout(r, delay));
      }
    }
  }
}

Troubleshooting

License Not Found (404)

Check that:

  • The license key is correctly copied from the admin panel
  • The license key format is valid
  • The license hasn't been deleted from the system

License Expired (410)

The license expiry date has passed. Renew the license in the admin panel or contact support.

Installation Limit Exceeded (429)

The license has been installed on more devices than allowed. Check max_installations and deactivate unused instances.

Network Timeout

The verification server didn't respond in time. Implement retry logic and cache previous results.

For questions or issues, contact support or visit the admin dashboard.