License Verification & Integration Guide
Protect your products sold on Laravel by integrating our license verification API into your application installer or admin panel.
Overview
Every product purchased on Laravel is issued a unique license key (UUID format). Authors can verify these keys using our public REST API to ensure buyers have a valid, active license before granting access to features, updates, or support.
Base URL
https://mp.chetsapp.de/api/v1/licenses/verify
Quick Start
A single GET request is all you need:
GET https://mp.chetsapp.de/api/v1/licenses/verify?license_key=YOUR_KEY&product_slug=YOUR_SLUG
Response (valid):
{
"valid": true,
"license_id": 142,
"product_id": 28,
"license_type": "standard",
"licensed_to": "Acme Corp",
"domain": "acme.com",
"expires_at": null,
"support_expires_at": "2027-04-08T00:00:00Z",
"support_active": true,
"activations_used": 1,
"activations_limit": 3,
"failure_reason": null
}
Response (invalid):
{
"valid": false,
"failure_reason": "Invalid license key for this product."
}
Request Parameters
| Parameter | Required | Type | Description |
|---|---|---|---|
license_key |
Yes | string (max 64) | The UUID license key provided to the buyer after purchase |
product_slug |
Yes | string (max 255) | Your product's URL slug on Laravel |
domain |
No | string (max 255) | Domain to validate against the license registration (optional) |
Response Fields
| Field | Type | Description |
|---|---|---|
valid | boolean | Whether the license is valid and active |
license_type | string|null | standard |
licensed_to | string|null | Name of the license holder |
domain | string|null | Registered domain (if activated) |
expires_at | ISO8601|null | License expiry date (null = perpetual) |
support_expires_at | ISO8601|null | Support/updates expiry date |
support_active | boolean | Whether support is still active |
failure_reason | string|null | Reason for failure (only when valid=false) |
Failure Reasons
| Reason | Cause |
|---|---|
Product not found. | The product_slug doesn't match any product |
Invalid license key for this product. | Key doesn't exist or doesn't belong to this product |
License has been deactivated. | Admin or system deactivated the license |
License has expired. | The expires_at date has passed |
License is registered to a different domain. | Domain mismatch (only when both request and license have domains) |
Rate Limiting
The API allows 120 requests per minute per IP address. Cache verification results locally to avoid hitting limits.
Integration Examples
PHP (Laravel / Plain PHP)
/**
* Verify a license key against AppTrovo API.
*/
function verifyLicense(string $licenseKey, string $productSlug, ?string $domain = null): array
{
$params = [
'license_key' => $licenseKey,
'product_slug' => $productSlug,
];
if ($domain) {
$params['domain'] = $domain;
}
$url = 'https://apptrovo.com/api/v1/licenses/verify?' . http_build_query($params);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
CURLOPT_SSL_VERIFYPEER => true,
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200 || !$response) {
return ['valid' => false, 'failure_reason' => 'Unable to reach license server.'];
}
return json_decode($response, true);
}
// Usage in your installer or admin panel:
$result = verifyLicense(
$_POST['license_key'],
'my-awesome-saas-script',
$_SERVER['HTTP_HOST'] ?? null
);
if ($result['valid']) {
// License is valid — proceed with installation
// Optionally store license info locally for offline checks
file_put_contents('storage/license.json', json_encode([
'license_key' => $_POST['license_key'],
'license_type' => $result['license_type'],
'licensed_to' => $result['licensed_to'],
'verified_at' => date('c'),
]));
} else {
// Show error to user
die('License verification failed: ' . $result['failure_reason']);
}
JavaScript / Node.js
async function verifyLicense(licenseKey, productSlug, domain = null) {
const params = new URLSearchParams({
license_key: licenseKey,
product_slug: productSlug,
});
if (domain) params.set('domain', domain);
try {
const res = await fetch(
`https://apptrovo.com/api/v1/licenses/verify?${params}`
);
return await res.json();
} catch (err) {
return { valid: false, failure_reason: 'Unable to reach license server.' };
}
}
// Usage
const result = await verifyLicense('your-uuid-key', 'my-product-slug', 'client-domain.com');
if (result.valid) {
console.log(`Licensed to: ${result.licensed_to}, Type: ${result.license_type}`);
} else {
console.error(`Invalid: ${result.failure_reason}`);
}
Python
import requests
def verify_license(license_key: str, product_slug: str, domain: str = None) -> dict:
params = {"license_key": license_key, "product_slug": product_slug}
if domain:
params["domain"] = domain
try:
r = requests.get("https://apptrovo.com/api/v1/licenses/verify", params=params, timeout=10)
return r.json()
except Exception:
return {"valid": False, "failure_reason": "Unable to reach license server."}
# Usage
result = verify_license("your-uuid-key", "my-product-slug", "client-domain.com")
if result["valid"]:
print(f"Licensed to: {result['licensed_to']}")
else:
print(f"Invalid: {result['failure_reason']}")
Installer Integration Pattern
Here's the recommended flow for integrating license verification into your product's web installer:
- Step 1 — License Input: Show a form asking for the license key (and optionally the buyer's Laravel username).
- Step 2 — Server-Side Verification: Call the verification API from your backend (never from client-side JavaScript in production).
- Step 3 — Domain Activation: Pass the installation domain in the
domainparameter to bind the license to this domain. - Step 4 — Store Locally: Cache the verification result locally (database or file) so the app works offline.
- Step 5 — Periodic Re-check: Re-verify the license periodically (e.g., once per week) to catch deactivations or expirations.
Example: Laravel Installer Middleware
// app/Http/Middleware/VerifyLicense.php
class VerifyLicense
{
public function handle($request, Closure $next)
{
$license = cache()->remember('apptrovo_license', 86400, function () {
$stored = json_decode(file_get_contents(storage_path('license.json')), true);
if (!$stored) return null;
// Re-verify with AppTrovo API
$result = verifyLicense(
$stored['license_key'],
config('app.apptrovo_product_slug'),
$request->getHost()
);
return $result['valid'] ? $result : null;
});
if (!$license) {
return redirect('/install'); // Redirect to license activation page
}
return $next($request);
}
}
Best Practices
Do
- Verify server-side (never client-only)
- Cache results locally for offline operation
- Re-verify periodically (weekly)
- Use the
domainparameter for domain locking - Handle network failures gracefully
- Check
activations_used/activations_limitfor slot info - Check
support_activebefore allowing update checks
Don't
- Don't verify on every page load (use caching)
- Don't hardcode your product slug client-side
- Don't block the app entirely on network failure
- Don't store the license key in public JavaScript
- Don't skip SSL verification in production
- Don't ignore the rate limit (120/min)
Feature Gating & Activation Info
// Check activation slots and support status
$license = json_decode(file_get_contents(storage_path('license.json')), true);
echo "Domains used: {$license['activations_used']} / {$license['activations_limit']}";
if ($license['support_active']) {
// Allow automatic update checks
// Show support ticket form
} else {
// Show "Renew support" prompt
}
Checking for Product Updates
Use the public product API to check for newer versions. Combine this with license verification to ensure only valid, supported licenses receive updates.
// Check for updates via the public product API
$product = json_decode(
file_get_contents('https://apptrovo.com/api/v2/products/your-product-slug'),
true
);
$latestVersion = $product['data']['current_version'] ?? null;
$installedVersion = config('app.version'); // Your local version
if ($latestVersion && version_compare($latestVersion, $installedVersion, '>')) {
// New version available — prompt for update
// Only allow download if support_active is true
}
Testing Your Integration
Use the test environment to verify your integration before going live:
Test API: https://mp.chetsapp.de/api/v1/licenses/verify
Production API: https://apptrovo.com/api/v1/licenses/verify
Laravel Installer SDK (Drop-in Package)
We provide a complete, ready-to-use installer package for Laravel products. It includes a 5-step web installer, license verification, and runtime middleware.
Download the Installer SDK
Contains: License SDK class, InstallController, CheckLicenseMiddleware, 5 Blade views, README.
View SDK DocumentationInstaller SDK Package
Ready-to-use PHP installer with license verification, step-by-step wizard, and customizable branding.
What's in the SDK
| File | Copy To | Purpose |
|---|---|---|
AppTrovoLicense.php | app/Services/ | License verification SDK with caching, offline support |
InstallController.php | app/Http/Controllers/ | 5-step installer (requirements, license, database, setup, done) |
CheckLicenseMiddleware.php | app/Http/Middleware/ | Runtime license check with auto-reverification |
views/*.blade.php | resources/views/installer/ | Beautiful installer UI with Tailwind CSS |
Installation Steps (5 minutes)
Step 1: Copy the SDK files into your Laravel project:
cp AppTrovoLicense.php your-app/app/Services/AppTrovoLicense.php
cp InstallController.php your-app/app/Http/Controllers/InstallController.php
cp CheckLicenseMiddleware.php your-app/app/Http/Middleware/CheckLicenseMiddleware.php
cp -r views/ your-app/resources/views/installer/
Step 2: Add your product slug to .env.example:
APPTROVO_PRODUCT_SLUG=your-product-slug-on-apptrovo
Step 3: Add installer routes to routes/web.php:
use App\Http\Controllers\InstallController;
Route::middleware('web')->prefix('install')->group(function () {
Route::get('/', [InstallController::class, 'requirements'])->name('install.requirements');
Route::get('/license', [InstallController::class, 'license'])->name('install.license');
Route::post('/license', [InstallController::class, 'verifyLicense'])->name('install.verifyLicense');
Route::get('/database', [InstallController::class, 'database'])->name('install.database');
Route::post('/database',[InstallController::class, 'saveDatabase'])->name('install.saveDatabase');
Route::get('/setup', [InstallController::class, 'setup'])->name('install.setup');
Route::post('/setup', [InstallController::class, 'install'])->name('install.install');
Route::get('/complete', [InstallController::class, 'complete'])->name('install.complete');
});
Step 4: Register the middleware in bootstrap/app.php:
->withMiddleware(function (Middleware $middleware) {
$middleware->alias([
'license' => \App\Http\Middleware\CheckLicenseMiddleware::class,
]);
})
Step 5: Protect your app routes:
// All your application routes behind license check
Route::middleware('license')->group(function () {
Route::get('/', [HomeController::class, 'index']);
// ... rest of your routes
});
Installer Flow
- Step 1 — Requirements: Checks PHP version, extensions (BCMath, cURL, GD, PDO, etc.) and folder permissions (storage/, bootstrap/cache).
- Step 2 — License: Buyer enters their AppTrovo license key. The SDK calls our API to verify it and binds the license to the installation domain.
- Step 3 — Database: Buyer enters MySQL credentials. The installer tests the connection before proceeding.
- Step 4 — Setup: Set app name, URL, and create the admin account. Runs migrations, seeds data, generates app key.
- Step 5 — Complete: Success page with post-install checklist (remove installer routes, set up cron, etc.).
Runtime License Middleware
The CheckLicenseMiddleware runs on every request and handles:
- Checks local cache file (
storage/.apptrovo_license) - If cache is fresh (< 7 days) — allows request immediately
- If cache is stale (> 7 days) — re-verifies with API in background
- If API confirms invalid — blocks access and clears cache
- If API is unreachable — allows request (graceful offline support)
- Shares license data with all views via
$apptrovo_license
Feature Gating in Your Product
use App\Services\AppTrovoLicense;
$license = new AppTrovoLicense(env('APPTROVO_PRODUCT_SLUG'), storage_path());
// Check activation slots
$cached = $license->checkLocal();
echo "Domains: {$cached['activations_used']} / {$cached['activations_limit']}";
// Gate updates by support status
if ($license->isSupportActive()) {
// Allow: auto-update checks, support ticket form, priority support
} else {
// Show: "Renew your support" banner with link to AppTrovo
}
// Use in Blade templates (shared by middleware)
// @if($apptrovo_license['support_active'])
// <!-- Show update checker -->
// @endif
Need Help?
If you need assistance integrating license verification into your product, visit our Help Center or contact support@apptrovo.com.