validpin / client
Official Validpin PHP client for license verification โ zero-dependency, domain-locked
Requires
- php: ^7.0 || ^8.0
- ext-curl: *
Requires (Dev)
- phpunit/phpunit: ^9.6 || ^10.5 || ^11.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
๐ validpin/client
Official Validpin PHP Client โ License Verification for PHP
Zero-dependency license verification client for the Validpin platform. Verify, enforce, and cache license keys with a single line of code.
โจ Features
- โ DOMAIN LOCKING โ Verify against the exact domain the license was issued for
- ๐ 24-HOUR CACHE โ
cache_enabled: true(default) avoids re-hitting the API on every request - ๐ก๏ธ
enforce()โ Stops execution on invalid/expired licenses; perfect for backend bootstrapping - ๐ง
guard()โ Drop-in license gate: Laravel middleware and vanilla PHP callable, with TTL cache + 72h grace period for API outages - โ๏ธ Configurable endpoint โ
api_urloption points at any Validpin-compatible server (self-hosted / local) - ๐ฆ Zero dependencies โ No Composer packages required at runtime; plain cURL
- ๐งฉ PSR-4 autoloading โ
Validpin\ValidpinClientvia Composer - ๐ฆ Phar support โ Single-file
validpin.pharfor drop-in installs without Composer
๐ฆ Installation
composer require validpin/client
No Composer? Download
validpin.pharfrom the GitHub Releases andrequireit directly.
๐ Quick Start
<?php require 'vendor/autoload.php'; use Validpin\ValidpinClient; // 1. Initialize with your Website API Key $validpin = new ValidpinClient('lcs_your_website_api_key', [ 'domain' => 'my-customer-site.com', // domain lock (required for production) 'cache_dir' => __DIR__ . '/cache', // recommended: writable cache folder ]); // 2. The license key provided by your user $licenseKey = 'A1B2-C3D4-E5F6-G7H8'; // 3. Verify if ($validpin->verify($licenseKey)) { echo "Access Granted!\n"; // Run your application logic here... } else { echo "Access Denied: " . $validpin->getLastError() . "\n"; exit; } // 4. Enforce mode (stops execution on failure) $validpin->enforce($licenseKey, 'Your custom message here.'); echo "Welcome back!\n";
๐ API Reference
new ValidpinClient($apiKey, array $options = [])
| Option | Type | Required | Description |
|---|---|---|---|
$apiKey |
string |
โ | Your Website API Key (lcs_...) from the Validpin dashboard |
api_url |
string |
โ | Override verification endpoint (default: https://api.validpin.com/v1/verify) |
domain |
string |
โ | Domain lock override (default: auto-detected from HTTP_HOST / SERVER_NAME) |
cache_enabled |
bool |
โ | Enable 24h caching (default: true) |
cache_dir |
string |
โ | Cache directory (default: sys_get_temp_dir()) |
timeout |
int |
โ | Request timeout in seconds (default: 10) |
verify_ssl |
bool |
โ | Verify SSL certificates (default: true) |
debug |
bool |
โ | Log errors to the PHP error log (default: false) |
verify($licenseKey): bool
Returns true if the license is valid (and caches the result for 24h). Returns false otherwise โ inspect getLastError() for the reason.
verifyStatus($licenseKey): string
Like verify(), but returns a tri-state result:
| Status | Meaning |
|---|---|
ValidpinClient::STATUS_VALID |
License accepted by the API |
ValidpinClient::STATUS_INVALID |
License explicitly rejected (invalid/expired) |
ValidpinClient::STATUS_UNREACHABLE |
API could not be reached (network/HTTP error) |
Used internally by guard() to decide when the grace period applies.
enforce($licenseKey, $message = 'Access denied: Invalid or expired license key.')
Calls verify() and exits with the message if the license is invalid. Returns true on success.
getLastError(): ?string
Human-readable reason for the last failure (license_not_found, license_expired, Request timed out, etc.).
activateLicense($licenseKey, $domain = null): bool
Verifies the key (optionally against a different $domain for this call) and, on success, records the activation in the gate storage โ so a subsequent guard() passes without another API call. Returns true on success.
๐ง License Gate โ guard()
guard() bundles the license-gate logic (cache + grace period + activation) into a single object usable two ways:
- Laravel middleware โ the returned object is invokable with
($request, $next)and also exposeshandle(). - Vanilla PHP โ call it with a key (
$gate($licenseKey)returnsbool) or with no arguments to auto-detect the key from thelicense_keycookie /X-License-Keyheader.
Semantics: the last successful verify() is cached for cacheTtl (12h by default). If the API is unreachable, the last successful result stays valid for gracePeriod (72h by default). A hard "license invalid" response is denied immediately โ no grace.
Options
| Option | Type | Default | Description |
|---|---|---|---|
onMissing |
callable | string |
redirect to activationUrl |
Handler when no license key is found (middleware mode). Callable receives $request. |
onInvalid |
callable | string |
redirect to activationUrl |
Handler when verification fails (middleware mode). |
cacheTtl |
int | string | DateInterval |
'12 hours' |
How long a successful result is cached without re-verifying. |
gracePeriod |
int | string | DateInterval |
'72 hours' |
How long the last success stays valid while the API is unreachable. |
storage |
StorageAdapter |
FileStorage (temp dir) |
Cache backend. Any PSR-16 implementation works (e.g. Laravel's Psr16Adapter). |
activationUrl |
string |
'/activate-license' |
Default redirect target when no handler is given. |
Laravel middleware
<?php namespace App\Http\Middleware; use Closure; use Illuminate\Http\Request; use Validpin\ValidpinClient; class EnsureLicensed { public function handle(Request $request, Closure $next) { $client = new ValidpinClient(config('services.validpin.account_token'), [ 'domain' => config('services.validpin.domain'), 'api_url' => config('services.validpin.api_url'), 'cache_enabled' => true, 'timeout' => 10, ]); $gate = $client->guard([ 'onMissing' => function () { return redirect()->route('license.activate'); }, 'onInvalid' => function () { return redirect()->route('license.activate', ['error' => 'invalid']); }, ]); return $gate($request, $next); } }
Vanilla PHP
<?php require 'vendor/autoload.php'; use Validpin\ValidpinClient; $client = new ValidpinClient('lcs_your_website_api_key', [ 'domain' => 'my-customer-site.com', 'api_url' => 'https://api.validpin.com/v1/verify', ]); $gate = $client->guard(); // Explicit key โ bool if (! $gate($licenseKeyFromUser)) { header('Location: /activate-license'); exit; } // Or auto-detect from cookie 'license_key' / header 'X-License-Key' $gate(); // โ bool // Activation: verify once and persist so the gate passes afterwards if ($client->activateLicense($licenseKey, 'my-customer-site.com')) { setcookie('license_key', $licenseKey, time() + 86400, '/'); }
Storage adapters
guard() accepts any object exposing PSR-16-style get / set / delete methods. Two are bundled:
Validpin\FileStorageโ default; JSON files undersys_get_temp_dir()/validpin_guard(configurable via constructor).Validpin\ArrayStorageโ in-memory; handy for tests and CLI tools.
use Validpin\FileStorage; $gate = $client->guard([ 'storage' => new FileStorage(__DIR__.'/storage/validpin'), ]);
๐งช Development
composer install # dev dependencies (PHPUnit) composer test # lint + PHPUnit test suite composer lint # syntax check only # Rebuild the single-file phar composer phar:build
๐๏ธ Project Structure
validpin/client/
โโโ src/ValidpinClient.php # PSR-4 source (namespace Validpin)
โโโ src/Guard.php # License gate (Laravel middleware + generic callable)
โโโ src/StorageAdapter.php # Storage interface (PSR-16-style)
โโโ src/FileStorage.php # Default file-based storage
โโโ src/ArrayStorage.php # In-memory storage (tests/CLI)
โโโ tests/ # PHPUnit suite
โโโ index.php # Example usage script
โโโ build-phar.php # Phar build script (php -d phar.readonly=0)
โโโ validpin.phar # Single-file build artifact
โโโ composer.json
๐ Security
- License verification should always happen server-side โ never trust client-only checks
- Keep your Website API Key (
lcs_...) out of client-side code and version control - Use
verify_ssl => truein production (default)
๐ License
MIT ยฉ Clerk Global LTD
๐ฌ Support
- Platform: https://validpin.com
- Issues: Clerk-Global-LTD/validpin-client-php
- Main Repo Issues: Clerk-Global-LTD/validpin