Add license validation to your React Native, Flutter, or native mobile app.
Call our API from your app. Here's the simplest version:
const response = await fetch('https://api.unifiedlicensing.com/api/v1/validate-license', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
api_key: 'ul_YOUR_API_KEY',
product_key: 'YOUR_PRODUCT_KEY',
license_key: 'PLAYER-LICENSE-KEY',
machine_id: 'device-unique-id',
platform: 'mobile'
})
});
const result = await response.json();
if (result.valid) { /* unlock features */ }
Get your API Key (Settings tab, starts with ul_) and Product Key (Products tab) from your vendor dashboard.
import React, { useState, useEffect } from 'react';
import { View, Text, TextInput, TouchableOpacity, ActivityIndicator } from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
import DeviceInfo from 'react-native-device-info';
const API = 'https://api.unifiedlicensing.com/api/v1';
export default function LicenseScreen({ onLicensed }) {
const [key, setKey] = useState('');
const [loading, setLoading] = useState(true);
const [error, setError] = useState('');
useEffect(() => { checkSaved(); }, []);
async function checkSaved() {
const saved = await AsyncStorage.getItem('license_key');
if (saved) {
const r = await validate(saved);
if (r.valid) { onLicensed(); return; }
}
setLoading(false);
}
async function validate(licenseKey) {
const id = await DeviceInfo.getUniqueId();
const res = await fetch(`${API}/validate-license`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
api_key: 'ul_YOUR_API_KEY',
product_key: 'YOUR_PRODUCT_KEY',
license_key: licenseKey,
machine_id: id,
platform: 'mobile'
})
});
return res.json();
}
async function activate() {
setLoading(true); setError('');
const r = await validate(key.trim());
if (r.valid) {
await AsyncStorage.setItem('license_key', key.trim());
onLicensed();
} else {
setError(r.error || 'Invalid license');
}
setLoading(false);
}
if (loading) return ;
return (
<View style={{flex:1,justifyContent:'center',padding:20,backgroundColor:'#081220'}}>
<Text style={{color:'#C7A34C',fontSize:24,textAlign:'center',marginBottom:20}}>Enter License Key</Text>
<TextInput value={key} onChangeText={setKey} placeholder="XXXX-XXXX-XXXX"
style={{borderWidth:1,borderColor:'#C7A34C',padding:12,borderRadius:8,color:'#EFEAE0',marginBottom:12}} />
<TouchableOpacity onPress={activate} style={{backgroundColor:'#C7A34C',padding:14,borderRadius:8,alignItems:'center'}}>
<Text style={{color:'#081220',fontWeight:'bold',fontSize:16}}>Activate</Text>
</TouchableOpacity>
{error ? <Text style={{color:'#B5502E',marginTop:10,textAlign:'center'}}>{error}</Text> : null}
</View>
);
}
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
import 'package:device_info_plus/device_info_plus.dart';
class LicenseService {
static const _api = 'https://api.unifiedlicensing.com/api/v1';
static const _apiKey = 'ul_YOUR_API_KEY';
static const _productKey = 'YOUR_PRODUCT_KEY';
static Future<bool> validate(String licenseKey) async {
final deviceInfo = DeviceInfoPlugin();
final androidInfo = await deviceInfo.androidInfo;
final machineId = androidInfo.id;
final res = await http.post(
Uri.parse('$_api/validate-license'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode({
'api_key': _apiKey, 'product_key': _productKey,
'license_key': licenseKey, 'machine_id': machineId, 'platform': 'mobile'
}),
);
final data = jsonDecode(res.body);
if (data['valid'] == true) {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('license_key', licenseKey);
}
return data['valid'] == true;
}
static Future<bool> checkSaved() async {
final prefs = await SharedPreferences.getInstance();
final saved = prefs.getString('license_key');
if (saved != null) return validate(saved);
return false;
}
}
// In your Activity
suspend fun validateLicense(key: String): Boolean = withContext(Dispatchers.IO) {
val androidId = Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID)
val body = JSONObject().apply {
put("api_key", "ul_YOUR_API_KEY")
put("product_key", "YOUR_PRODUCT_KEY")
put("license_key", key)
put("machine_id", androidId)
put("platform", "mobile")
}
val req = Request.Builder()
.url("https://api.unifiedlicensing.com/api/v1/validate-license")
.post(body.toString().toRequestBody("application/json".toMediaType()))
.build()
val resp = OkHttpClient().newCall(req).execute()
val json = JSONObject(resp.body?.string() ?: "{}")
if (json.optBoolean("valid", false)) {
getSharedPreferences("app", MODE_PRIVATE).edit().putString("license_key", key).apply()
}
json.optBoolean("valid", false)
}
func validateLicense(_ key: String) async throws -> Bool {
let deviceID = UIDevice.current.identifierForVendor?.uuidString ?? "unknown"
var req = URLRequest(url: URL(string: "https://api.unifiedlicensing.com/api/v1/validate-license")!)
req.httpMethod = "POST"
req.addValue("application/json", forHTTPHeaderField: "Content-Type")
req.httpBody = try JSONEncoder().encode([
"api_key": "ul_YOUR_API_KEY",
"product_key": "YOUR_PRODUCT_KEY",
"license_key": key,
"machine_id": deviceID,
"platform": "mobile"
])
let (data, _) = try await URLSession.shared.data(for: req)
let json = try JSONSerialization.jsonObject(with: data) as? [String: Any]
let valid = json?["valid"] as? Bool ?? false
if valid { UserDefaults.standard.set(key, forKey: "license_key") }
return valid
}
Cache the last successful validation. On next launch, check cache first (instant), then re-validate in background.
// Start trial
const res = await fetch('https://api.unifiedlicensing.com/api/v1/start-trial', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
api_key: 'ul_YOUR_API_KEY', product_key: 'YOUR_PRODUCT_KEY',
machine_id: machineId, platform: 'mobile'
})
});
const data = await res.json();
// data.trial_token, data.remaining_days
await AsyncStorage.setItem('trial_token', data.trial_token);
// Check trial on app start
const check = await fetch('https://api.unifiedlicensing.com/api/v1/check-trial', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
api_key: 'ul_YOUR_API_KEY', product_key: 'YOUR_PRODUCT_KEY',
trial_token: savedToken, machine_id: machineId, platform: 'mobile'
})
});
| Platform | Identifier | API |
|---|---|---|
| Android | ANDROID_ID | Settings.Secure.ANDROID_ID |
| iOS | identifierForVendor | UIDevice.current.identifierForVendor |
| React Native | Device unique ID | react-native-device-info |
| Flutter | Device ID | device_info_plus |
| Problem | Fix |
|---|---|
| Network error | Check your API URL includes https:// and the full path /api/v1/validate-license |
| "Invalid license" for valid key | Check api_key and product_key match your dashboard |
| Works on iOS but not Android | Check Android network security config. Ensure cleartext is not required (use HTTPS). |
| License valid but features locked | Your app isn't reading the validation result correctly. Check the response parsing. |
| App rejected by Apple | Make sure you're not mentioning external purchases in your app description |