v4 SDK: This FAQ covers the current v4 SDKs (Python, PHP, JavaScript, .NET). See the API Reference for full documentation.

UnifiedLicensing Developer FAQ

Common questions about trial protection, security, and SDK implementation.

Q: What happens if users delete localStorage/sessionStorage in my web app?

A: The trial continues from server-side tracking. Users cannot restart trials by clearing storage.

Server Protection: Trial start time is tracked by device fingerprint on our servers. Storage deletion is just an inconvenience, not a security bypass.
// User clears storage, SDK recovers trial status from server const result = await manager.validateLicense(null, { trialToken: storedTrialToken }); if (result.valid) { console.log(`Trial continues: ${result.license_info.days_remaining} days left`); }
Q: Can users bypass trials by using incognito mode or different browsers?

A: No. Our browser fingerprinting creates consistent device IDs across sessions and browsers on the same machine.

// Fingerprint includes hardware-specific data const fingerprint = [ navigator.userAgent, screen.width + 'x' + screen.height, navigator.hardwareConcurrency, canvas.toDataURL(), // GPU fingerprint audioContext.fingerprint // Audio fingerprint ].join('|');
Q: Are trial analytics available in all SDK platforms?

A: Yes, trial tracking is now available in .NET, JavaScript, Python, and PHP SDKs.

// .NET await manager.TrackTrialStartAsync("tr_abc123", new List<string> { "basic" }); await manager.TrackFeatureUsageAsync("tr_abc123", "export", new Dictionary<string, object> { ["format"] = "pdf" }); await manager.TrackTrialConversionAsync("tr_abc123", 49.99m, "professional"); // JavaScript await manager.trackTrialStart("tr_abc123", ["basic"]); await manager.trackFeatureUsage("tr_abc123", "export", { format: "pdf" }); await manager.trackTrialConversion("tr_abc123", 49.99, "professional"); // Python await manager.track_trial_start("tr_abc123", ["basic"]) await manager.track_feature_usage("tr_abc123", "export", {"format": "pdf"}) await manager.track_trial_conversion("tr_abc123", 49.99, "professional") // PHP $client->trackTrialStart("tr_abc123", ["basic"]); $client->trackFeatureUsage("tr_abc123", "export", ["format" => "pdf"]); $client->trackTrialConversion("tr_abc123", 49.99, "professional");
Q: How secure is client-side validation for HTML5 games and Chrome extensions?

A: Client-side validation is for UX only. Security comes from server-side validation and multiple protection layers.

Security Layers:
  • Server-side validation (authoritative)
  • Browser fingerprinting (survives storage clearing)
  • Multiple storage layers (localStorage + sessionStorage + IndexedDB)
  • Heartbeat validation (periodic server checks)
  • Grace periods (offline support)
  • Feature degradation (gradual vs hard blocks)
Q: What's the difference between hardware binding levels?

A: Hardware binding determines how strictly licenses are tied to specific devices.

// Strict: All hardware components components = [processor, memory, disk_serial, mac_address, motherboard] // Loose: Core components only components = [processor, machine_type, mac_address] // None: System identifiers only components = [hostname, username]
Recommendation: Use "loose" for most applications. "Strict" for high-security needs. "None" for flexible deployment.
Q: How do I implement trial recovery after storage deletion?

A: Use the validateLicense() method with the trial token to recover from server records.

// Check for existing trial on app start async function initializeApp() { const stored = localStorage.getItem('license_key'); const trialToken = localStorage.getItem('trial_token'); if (!stored && trialToken) { // Storage cleared - re-validate with trial token const result = await manager.validateLicense(stored, { trialToken: trialToken }); if (result.valid) { // Continue existing trial showTrialMode(result.license_info.days_remaining); } else { // No active trial showTrialExpired(); } } }
Q: Can I obfuscate my client-side code for additional protection?

A: Yes, but remember obfuscation is not security - it's just making reverse engineering harder.

// Basic obfuscation example const _0x1234 = ['license_key', 'validate', 'features']; function checkIntegrity() { const expectedHash = 'abc123def456'; const currentHash = btoa(validateLicense.toString()).substring(0, 12); if (currentHash !== expectedHash) { throw new Error('Code integrity violation'); } }
Important: Obfuscation slows down attackers but doesn't stop them. Always rely on server-side validation for real security.
Q: How do I handle offline scenarios in my application?

A: Implement graceful degradation with time-based feature restrictions.

class OfflineManager { getDegradationLevel() { const lastValidation = localStorage.getItem('last_validation'); const hoursSince = (Date.now() - lastValidation) / (1000 * 60 * 60); if (hoursSince < 1) return 'full'; // All features if (hoursSince < 6) return 'most'; // Most features if (hoursSince < 24) return 'basic'; // Basic features return 'minimal'; // Minimal features } }
Q: What's the recommended approach for Chrome extension licensing?

A: Use background scripts for validation and chrome.storage.local for persistence.

// manifest.json { "permissions": ["storage", "activeTab"], "host_permissions": ["https://api.unifiedlicensing.com/*"] } // background.js chrome.runtime.onInstalled.addListener(async () => { const result = await licensing.validateLicense('license-key'); chrome.storage.local.set({ licensed: result.isValid }); }); // Periodic validation chrome.alarms.create('license-check', { periodInMinutes: 60 });
Q: How do I track trial analytics without impacting user experience?

A: Use async tracking with error handling that doesn't block the UI.

// Non-blocking tracking async function trackFeatureSafely(feature) { try { // Fire and forget licensing.trackFeatureUsage(feature).catch(console.warn); } catch (error) { // Never interrupt user experience for analytics console.warn('Analytics failed:', error); } } // Batch tracking for high-frequency events const eventQueue = []; setInterval(() => { if (eventQueue.length > 0) { licensing.trackBatch(eventQueue.splice(0)); } }, 60000); // Flush every minute

Looking for security-specific questions?

Security FAQ →