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
validbooleanWhether the license is valid and active
license_typestring|nullstandard
licensed_tostring|nullName of the license holder
domainstring|nullRegistered domain (if activated)
expires_atISO8601|nullLicense expiry date (null = perpetual)
support_expires_atISO8601|nullSupport/updates expiry date
support_activebooleanWhether support is still active
failure_reasonstring|nullReason 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:

  1. Step 1 — License Input: Show a form asking for the license key (and optionally the buyer's Laravel username).
  2. Step 2 — Server-Side Verification: Call the verification API from your backend (never from client-side JavaScript in production).
  3. Step 3 — Domain Activation: Pass the installation domain in the domain parameter to bind the license to this domain.
  4. Step 4 — Store Locally: Cache the verification result locally (database or file) so the app works offline.
  5. 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 domain parameter for domain locking
  • Handle network failures gracefully
  • Check activations_used / activations_limit for slot info
  • Check support_active before 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 Documentation

Installer SDK Package

Ready-to-use PHP installer with license verification, step-by-step wizard, and customizable branding.

Download SDK

What's in the SDK

File Copy To Purpose
AppTrovoLicense.phpapp/Services/License verification SDK with caching, offline support
InstallController.phpapp/Http/Controllers/5-step installer (requirements, license, database, setup, done)
CheckLicenseMiddleware.phpapp/Http/Middleware/Runtime license check with auto-reverification
views/*.blade.phpresources/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

1 Requirements
2 License
3 Database
4 Setup
5 Complete
  1. Step 1 — Requirements: Checks PHP version, extensions (BCMath, cURL, GD, PDO, etc.) and folder permissions (storage/, bootstrap/cache).
  2. 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.
  3. Step 3 — Database: Buyer enters MySQL credentials. The installer tests the connection before proceeding.
  4. Step 4 — Setup: Set app name, URL, and create the admin account. Runs migrations, seeds data, generates app key.
  5. 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.