PHPackages                             finext/license-client - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. finext/license-client

ActiveLibrary

finext/license-client
=====================

Client SDK for self-hosted Finext products to activate, verify, and heartbeat against the Finext License Server.

v1.0.1(1mo ago)08↓66.7%proprietaryPHPPHP ^8.2

Since Jul 17Pushed 1mo agoCompare

[ Source](https://github.com/KetanGupta0/license-client)[ Packagist](https://packagist.org/packages/finext/license-client)[ RSS](/packages/finext-license-client/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependencies (9)Versions (3)Used By (0)

Finext License Client
=====================

[](#finext-license-client)

Client SDK for self-hosted Finext products to activate, verify, and heartbeat against the [Finext License Server](https://finextsolution.com). Ships as a Composer package consumed by each product (artiqpay, dating-app, ecommerce, finext-website, finsolvitonline, fixpay, ms2pay, proxypayonline, etc.) — not part of the license-server repo itself, since it has an independent release lifecycle and must never leak server-side secrets or paths into redistributed product code.

Installation
------------

[](#installation)

```
composer require finext/license-client
```

During local development (before this package is published), require it via a Composer path repository in the host app's `composer.json`:

```
{
    "repositories": [
        { "type": "path", "options": { "symlink": true }, "url": "../license-client" }
    ],
    "require": {
        "finext/license-client": "@dev"
    }
}
```

Publish the config file:

```
php artisan vendor:publish --tag=license-client-config
```

Configuration
-------------

[](#configuration)

All values are set via `.env`; see `config/license-client.php` for the full list. The essentials:

```
LICENSE_SERVER_URL=https://license.finextsolution.com

# Issued once per product from the license-server admin panel
# (Product → API Keys). Shipped inside this product's source.
LICENSE_PRODUCT_ID=5
LICENSE_BOOTSTRAP_KEY_ID=pak_xxxxxxxxxxxxxxxxxxxx
LICENSE_BOOTSTRAP_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

# Entered by the customer at install time
LICENSE_KEY=XXXXX-XXXXX-XXXXX-XXXXX

# Pinned Ed25519 public key so a compromised/MITM'd public-key endpoint
# can't silently swap in a different signing key. Get the current value
# from GET /api/v1/license/public-key on the license server.
LICENSE_PINNED_KEY_ID=v1
LICENSE_PINNED_PUBLIC_KEY=base64-encoded-public-key

LICENSE_DEFAULT_GRACE_PERIOD_DAYS=3
LICENSE_HTTP_TIMEOUT_SECONDS=10
```

Local state (installation ID, per-activation secret, encrypted verification cache, cached public key) is written under `storage/app/license/` with `0600` permissions and should stay out of version control.

Security model
--------------

[](#security-model)

Two different guarantees protect two different directions of traffic:

- **Server → client (responses):** every response is signed with Ed25519 and verified against the pinned/cached public key before any of its contents are trusted. A response that fails verification is treated exactly like a network failure — never surfaced as a real answer. This is a genuine cryptographic authenticity guarantee.
- **Client → server (requests):** signed with HMAC-SHA256 under one of two secrets:
    - The **bootstrap secret** (shipped in source, one per product) signs only the very first `/activate` call. Because it ships with the product, it is necessarily extractable by anyone with the source — it's a deterrence measure, not a real security boundary.
    - The **activation secret** is generated by the server on successful activation, never shipped, and is the real security boundary for every call afterward (`verify`, `heartbeat`, `deactivate`).

This reflects the fundamental limit of licensing a product whose full source is handed to the customer: it deters casual bypass, it does not achieve DRM-level enforcement.

The local cache (`EncryptedLocalCache`) is encrypted with `sodium_crypto_secretbox` using a key derived from `product_id` + activation secret, so it fails closed if tampered with, copied to another installation, or read without the activation secret.

Usage
-----

[](#usage)

### Activating an installation

[](#activating-an-installation)

```
php artisan license:activate
# or: php artisan license:activate LICENSE-KEY --domain=example.com --label="Production"
```

Falls back to `LICENSE_KEY` from config, then interactively prompts if neither the argument nor config value is set.

### Checking license validity (recommended entry point)

[](#checking-license-validity-recommended-entry-point)

```
use Finext\LicenseClient\Facades\License;

$result = License::check();
// [
//     'valid' => true,
//     'status' => 'active', // or grace_period, grace_period_expired, not_activated, unreachable_no_cache
//     'source' => 'live',    // or 'cache'
//     'days_remaining' => 29,
//     'data' => [...],       // full verified envelope data, when available
// ]
```

`check()` never throws. It verifies live against the server when reachable; if the server can't be reached, it falls back to the signed local cache and applies the offline grace period (`grace_period_days` from the server, or `LICENSE_DEFAULT_GRACE_PERIOD_DAYS` if no cache exists yet).

### Gating routes with the middleware

[](#gating-routes-with-the-middleware)

```
Route::middleware('license.valid')->group(function () {
    // ...
});
```

Aborts with `403` and a support message when `check()['valid']` is false.

### Other operations

[](#other-operations)

```
License::installationId();      // stable UUID for this installation
License::isActivated();         // whether an activation secret is stored
License::verify($domain);       // one-off live verify call
License::heartbeat($appVersion);
License::deactivate();          // deactivates server-side and clears local state
License::forget();              // clears local state only (secret + cache)
```

Testing this package in isolation
---------------------------------

[](#testing-this-package-in-isolation)

```
composer install
vendor/bin/phpunit
```

Tests run as plain PHPUnit (no Laravel container bootstrap) — all pure-logic classes (`HmacSigner`, `ResponseVerifier`, `EncryptedLocalCache`, `GracePeriodPolicy`) avoid Laravel facades so they can be exercised directly, including a golden-vector cross-check of the canonical JSON format against the license server's own implementation.

Compatibility
-------------

[](#compatibility)

PHP ^8.2 with the `sodium` extension, Laravel (illuminate/support|http|console) ^10, ^11, ^12, or ^13.

###  Health Score

40

—

FairBetter than 86% of packages

Maintenance91

Actively maintained with recent releases

Popularity6

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity47

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~3 days

Total

2

Last Release

43d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/87747774?v=4)[Chandra Kishor Gupta](/maintainers/KetanGupta0)[@KetanGupta0](https://github.com/KetanGupta0)

---

Top Contributors

[![KetanGupta0](https://avatars.githubusercontent.com/u/87747774?v=4)](https://github.com/KetanGupta0 "KetanGupta0 (2 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/finext-license-client/health.svg)

```
[![Health](https://phpackages.com/badges/finext-license-client/health.svg)](https://phpackages.com/packages/finext-license-client)
```

###  Alternatives

[laravel/mcp

Rapidly build MCP servers for your Laravel applications.

80732.6M276](/packages/laravel-mcp)[mike-bronner/laravel-model-caching

Automatic caching for Eloquent models.

2.4k161.4k2](/packages/mike-bronner-laravel-model-caching)[psalm/plugin-laravel

Psalm plugin for Laravel

3365.5M359](/packages/psalm-plugin-laravel)[forjedio/inertia-table

Backend-driven dynamic tables for Laravel + Inertia.js

272.5k](/packages/forjedio-inertia-table)[aedart/athenaeum

Athenaeum is a mono repository; a collection of various PHP packages

255.2k](/packages/aedart-athenaeum)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
