🔐 License System Documentation

এই ডকুমেন্টেশনে লাইসেন্স সিস্টেমের সম্পূর্ণ বিবরণ, API রেফারেন্স, Deploy গাইড এবং Client Integration কোড রয়েছে।

✅ Backward Compatible 🔒 HMAC Security ☠️ Kill Switch ⚡ Cache Signal 🛡️ Auto Block
📋

সিস্টেম ওভারভিউ

পুরো লাইসেন্স সিস্টেমের সারসংক্ষেপ

🏢
Admin Panel
লাইসেন্স তৈরি, ম্যানেজ, ব্লক
👤
User Panel
লাইসেন্স কেনা ও নবায়ন
🌐
Public API
POST /api/verify-license

মূল ফিচারসমূহ

ফিচারবিবরণ
License GenerationAdmin বা User পেমেন্ট করে নতুন লাইসেন্স তৈরি করতে পারে
Domain Bindingপ্রতিটি লাইসেন্স একটি নির্দিষ্ট ডোমেইনের সাথে যুক্ত
Auto BlockInvalid লাইসেন্স ব্যবহার করলে ডোমেইন স্বয়ংক্রিয়ভাবে ব্লক হয়
Cache SignalAdmin ইচ্ছামতো ক্লায়েন্ট সাইটের cache মুছতে পারে
Kill SwitchAdmin যেকোনো সাইটকে রিমোটলি disable করতে পারে
HMAC Securityনতুন সাইটে HMAC signature দিয়ে শক্তিশালী নিরাপত্তা
License Renewalমেয়াদ শেষে UddoktaPay দিয়ে পেমেন্ট করে নবায়ন
🗄️

ডাটাবেস কাঠামো

সকল টেবিলের বিস্তারিত বিবরণ

📦 licenses টেবিল — মূল লাইসেন্স ডেটা

ColumnTypeবিবরণ
idbigint PKAuto increment
user_idbigintলাইসেন্সের মালিক (users টেবিল)
domain_namevarchar(255)Normalized ডোমেইন (www/https ছাড়া, lowercase)
license_keyvarchar(255)Format: LIC-XXXX-XXXX-XXXX
expiry_datedatetimeমেয়াদ শেষের তারিখ
statusint1 = Active 0 = Inactive

🛡️ stolen_domains টেবিল — চুরি লগ

Columnবিবরণ
domain_nameচেষ্টা করা ডোমেইন
ip_addressআক্রমণকারীর IP
cache_signalRandom token — পরিবর্তন হলে ক্লায়েন্ট cache মুছবে
is_blocked1 = Blocked 0 = Unblocked
status0 = Pending 1 = Resolved

☠️ unauthorized_logs

Columnবিবরণ
domainডোমেইন নাম
ipIP ঠিকানা
reasonকারণ
kill_switch1 = wipe_out চালু

🔄 license_renewal_orders

Columnবিবরণ
license_idকোন লাইসেন্সের জন্য
amountপেমেন্টের পরিমাণ
invoice_idUddoktaPay Invoice
statuspending / paid / cancelled
💡
phpMyAdmin SQL: নিচের Deploy সেকশনে সব টেবিলের সম্পূর্ণ SQL CREATE statement দেওয়া আছে।
🌐

verify-license API

ক্লায়েন্ট সাইট লাইসেন্স যাচাইয়ের Public API

Endpoint

POST /api/verify-license Rate Limit: 60/min

Request Parameters

ParameterTypeRequiredবিবরণ
domainstringহ্যাঁক্লায়েন্ট সাইটের ডোমেইন
license_keystringহ্যাঁলাইসেন্স কী (LIC-XXXX-XXXX-XXXX)
signaturestringনাHMAC signature (নতুন ক্লায়েন্টের জন্য)
urlstringনাসম্পূর্ণ URL (logging এর জন্য)

✅ Valid Response

{
  "status": "valid",
  "blocked": false,
  "domain_name": "example.com",
  "license_key": "LIC-ABCD-EFGH-IJKL",
  "expiry_date": "2027-05-15T00:00:00Z",
  "secure_token": "a1b2c3d4...",
  "cache_signal": "xK9mP2...",
  "action": "none"
}

❌ Invalid Response

{
  "status": "invalid",
  "blocked": true,
  "message": "License key or domain is
  incorrect or has expired.",
  "action": "none"
}

Response Fields ব্যাখ্যা

Fieldমানবিবরণ
statusvalid / invalidলাইসেন্স বৈধ কিনা
blockedtrue / falseডোমেইন blocked কিনা
secure_tokenmd5 hashঅতিরিক্ত যাচাইয়ের জন্য
cache_signalrandom stringপরিবর্তন হলে ক্লায়েন্ট cache মুছবে
actionnone / wipe_outwipe_out হলে সাইট disable করতে হবে
⚠️
action: wipe_out — এই response পেলে ক্লায়েন্ট সাইটকে নিজেকে disable করতে হবে। Admin Kill Switch চালু করেছে।
🔑

HMAC Security

নতুন ক্লায়েন্টের জন্য উন্নত নিরাপত্তা

⚠️ পুরনো পদ্ধতি (Legacy)

  • Signature নেই
  • Invalid হলে stolen_domains এ লগ হয়
  • সহজ কিন্তু কম secure

✅ নতুন পদ্ধতি (HMAC)

  • HMAC-SHA256 signature
  • Cleaner — stolen_domains log করে না
  • বেশি secure

HMAC Signature তৈরি করার নিয়ম

// PHP
$signature = hash_hmac('sha256', $domain, 'your_secret_salt_key');

// উদাহরণ
$domain    = 'example.com';
$signature = hash_hmac('sha256', 'example.com', 'your_secret_salt_key');
// → "a3f9b2c1d4e5f6..."
⚙️
Salt পরিবর্তন: app/Http/Controllers/Admin/LicenseController.php এ HMAC_SALT ও TOKEN_SALT constants পরিবর্তন করুন। পরিবর্তন করলে সব ক্লায়েন্টে নতুন salt দিতে হবে।
⚙️

Admin প্যানেল

লাইসেন্স ম্যানেজমেন্ট ও নিয়ন্ত্রণ

নতুন লাইসেন্স তৈরি — /admin/licenses

১

ইউজার সিলেক্ট করুন

ড্রপডাউন থেকে ক্লায়েন্টের অ্যাকাউন্ট বেছে নিন

২

ডোমেইন দিন

www বা https দিলেও স্বয়ংক্রিয়ভাবে normalize হবে

৩

মেয়াদ সিলেক্ট করুন

১ বছর / ২ বছর / ৩ বছর

৪

Submit করুন

স্বয়ংক্রিয়ভাবে LIC-XXXX-XXXX-XXXX format এ key তৈরি হবে

Stolen Reports — Action বাটনসমূহ

বাটনকাজ
⚡ Cache Clearক্লায়েন্ট সাইটের cache মুছার signal পাঠানো (৩০ মিনিটের মধ্যে কার্যকর)
🔒 Blockডোমেইন block করা — ক্লায়েন্ট সাইট বন্ধ হবে
🔓 Unblockডোমেইন unblock করা
☠️ Kill Switchরিমোট wipe-out চালু/বন্ধ করা
🔄 StatusPending → Resolved toggle
🗑️ Deleteরিপোর্ট মুছা

রিনিউয়াল অর্ডার ম্যানেজমেন্ট — /admin/license-renewal-orders

User সাইট থেকে renew request করলে এখানে দেখা যাবে।

Actionফলাফল
Mark as Paidলাইসেন্স আরো ১ বছর extend হবে, status = paid
Mark as Unpaidপূর্বের অবস্থায় ফেরানো যাবে
👤

User প্যানেল

ক্লায়েন্টের নিজস্ব লাইসেন্স ম্যানেজমেন্ট

বিনামূল্যে লাইসেন্স

✅
Admin যদি free_license_generation = true করে দেয়

ইউজার সরাসরি লাইসেন্স পাবে, কোনো পেমেন্ট লাগবে না।

পেমেন্ট করে লাইসেন্স

💳
UddoktaPay Gateway ব্যবহার করে

মূল্য = license_generation_price × মেয়াদ

User লাইসেন্স Actions

URLকাজ
/my-licensesসকল লাইসেন্স দেখা
/license/generateনতুন লাইসেন্স কেনা
/license/renew/{id}মেয়াদ শেষে নবায়ন করা
/license/update/{id}ডোমেইন পরিবর্তন
/license/delete/{id}লাইসেন্স মুছা
🛡️

Stolen Domain সিস্টেম

অবৈধ ব্যবহার স্বয়ংক্রিয়ভাবে ধরা ও ব্লক করা

কীভাবে কাজ করে

১

ক্লায়েন্ট verify করে

POST /api/verify-license এ domain + license_key পাঠায়

২

Server চেক করে

ডোমেইন ও কী মিলিয়ে দেখে, মেয়াদ চেক করে

৩ক

Valid হলে

is_blocked = 0 হয় (auto unblock), status: valid রিটার্ন করে

৩খ

Invalid হলে

stolen_domains এ লগ হয়, is_blocked = 1, cache_signal নতুন হয়

৪

Admin দেখতে পান

Stolen Reports এ নতুন entry আসে। Block/Unblock করা যায়।

Domain Normalize নিয়ম

InputNormalized Output
https://www.example.com/pathexample.com
http://example.com/example.com
www.EXAMPLE.COMexample.com
Example.Comexample.com
☠️

Kill Switch

রিমোট wipe-out — ক্লায়েন্ট সাইট disable করা

🚨
সতর্কতা: Kill Switch চালু করলে ক্লায়েন্ট সাইট সম্পূর্ণ বন্ধ হয়ে যেতে পারে। সাবধানে ব্যবহার করুন।

Kill Switch বন্ধ

{ "action": "none" }

সাইট স্বাভাবিকভাবে কাজ করবে

Kill Switch চালু ☠️

{ "action": "wipe_out" }

ক্লায়েন্ট সাইট নিজেকে disable করবে

কীভাবে চালু/বন্ধ করবেন

১

Admin প্যানেল খুলুন

Stolen Reports সেকশনে যান

২

☠️ বাটনে ক্লিক করুন

নির্দিষ্ট ডোমেইনের Kill Switch বাটনে ক্লিক করুন

৩

Confirm করুন

পরের বার ক্লায়েন্ট verify করলে action=wipe_out পাবে

⚡

Cache Signal সিস্টেম

ক্লায়েন্ট সাইটের cache রিমোটলি মুছা

উদ্দেশ্য

ক্লায়েন্ট সাইট performance এর জন্য লাইসেন্স ভেরিফিকেশন cache করে রাখতে পারে। Cache Signal ব্যবহার করে Admin যেকোনো সময় ক্লায়েন্ট সাইটের cache মুছতে পারে — সাইটকে সরাসরি access না করেই।

কীভাবে কাজ করে

১

Admin ⚡ বাটনে ক্লিক করে

Stolen Reports এ নির্দিষ্ট ডোমেইনের Cache Clear বাটন

২

নতুন cache_signal তৈরি হয়

stolen_domains টেবিলে নতুন random token সেভ হয়

৩

ক্লায়েন্ট verify করে

API response এ নতুন cache_signal পায়

৪

ক্লায়েন্ট পার্থক্য দেখে

পুরনো signal আলাদা → cache মুছে → নতুন signal সেভ করে

⚠️
সীমাবদ্ধতা: Blocked domain এর cache clear করা যাবে না। আগে Unblock করুন, তারপর Cache Clear করুন।
🚀

Server Deploy নির্দেশিকা

প্রথমবার এবং আপডেট deploy করার নিয়ম

phpMyAdmin এ চালানোর SQL (সব টেবিল)

SSH না থাকলে এই SQL গুলো phpMyAdmin → SQL ট্যাবে paste করে Execute করুন:

-- ১. stolen_domains এ নতুন column
ALTER TABLE `stolen_domains`
  ADD COLUMN IF NOT EXISTS `cache_signal` varchar(255) DEFAULT NULL,
  ADD COLUMN IF NOT EXISTS `is_blocked` tinyint(1) NOT NULL DEFAULT 0;

-- ২. users টেবিলে free_license_generation
ALTER TABLE `users`
  ADD COLUMN IF NOT EXISTS `free_license_generation` tinyint(1) NOT NULL DEFAULT 0;

-- ৩. generalsettings এ license price
ALTER TABLE `generalsettings`
  ADD COLUMN IF NOT EXISTS `license_generation_price` decimal(10,2) DEFAULT 1000.00;

-- ৪. unauthorized_logs টেবিল তৈরি
CREATE TABLE IF NOT EXISTS `unauthorized_logs` (
  `id`           bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
  `domain`       varchar(255) NOT NULL,
  `ip`           varchar(255) DEFAULT NULL,
  `reason`       text NOT NULL,
  `url`          text DEFAULT NULL,
  `attempted_at` timestamp NOT NULL,
  `kill_switch`  tinyint(1) NOT NULL DEFAULT 0,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- ৫. license_renewal_orders টেবিল তৈরি
CREATE TABLE IF NOT EXISTS `license_renewal_orders` (
  `id`         bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
  `user_id`    bigint(20) UNSIGNED NOT NULL,
  `license_id` bigint(20) UNSIGNED NOT NULL,
  `amount`     decimal(10,2) NOT NULL DEFAULT 1000.00,
  `invoice_id` varchar(255) DEFAULT NULL,
  `status`     varchar(255) NOT NULL DEFAULT 'pending',
  `paid_at`    timestamp NULL DEFAULT NULL,
  `notes`      text DEFAULT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

SSH থাকলে (Terminal)

php artisan migrate
php artisan config:cache
php artisan route:cache
php artisan cache:clear
💻

Client Site Integration

ক্লায়েন্ট সাইটে লাইসেন্স চেক করার PHP কোড

সহজ পদ্ধতি (Legacy — পুরনো সাইটের জন্য)

<?php
function verifyLicense($domain, $licenseKey, $serverUrl) {
    $ch = curl_init();
    curl_setopt_array($ch, [
        CURLOPT_URL            => $serverUrl . '/api/verify-license',
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST           => true,
        CURLOPT_POSTFIELDS     => http_build_query([
            'domain'      => $domain,
            'license_key' => $licenseKey,
            'url'         => (isset($_SERVER['HTTPS']) ? 'https' : 'http')
                             . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'],
        ]),
        CURLOPT_TIMEOUT => 10,
    ]);
    $response = curl_exec($ch);
    curl_close($ch);
    return json_decode($response, true);
}

$result = verifyLicense(
    'example.com',
    'LIC-ABCD-EFGH-IJKL',
    'https://yourserver.com'
);

if ($result['status'] === 'valid') {
    if ($result['action'] === 'wipe_out') {
        die('Site disabled by administrator.');
    }
    // cache_signal চেক
    $saved = @file_get_contents('license_signal.txt');
    if ($saved !== $result['cache_signal']) {
        clearLocalCache(); // নিজের cache clear function
        file_put_contents('license_signal.txt', $result['cache_signal']);
    }
    // ✅ স্বাভাবিক কাজ চালিয়ে যান
} else {
    die('License invalid or domain blocked.');
}

উন্নত পদ্ধতি (HMAC — নতুন সাইটের জন্য)

<?php
define('LICENSE_SERVER', 'https://yourserver.com');
define('LICENSE_DOMAIN',  'example.com');
define('LICENSE_KEY',     'LIC-ABCD-EFGH-IJKL');
define('HMAC_SALT',       'your_secret_salt_key'); // server এর HMAC_SALT

function verifyLicenseHmac() {
    $domain    = LICENSE_DOMAIN;
    $signature = hash_hmac('sha256', $domain, HMAC_SALT);

    $ch = curl_init();
    curl_setopt_array($ch, [
        CURLOPT_URL            => LICENSE_SERVER . '/api/verify-license',
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST           => true,
        CURLOPT_POSTFIELDS     => http_build_query([
            'domain'      => $domain,
            'license_key' => LICENSE_KEY,
            'signature'   => $signature,
            'url'         => (isset($_SERVER['HTTPS']) ? 'https' : 'http')
                             . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'],
        ]),
        CURLOPT_TIMEOUT        => 10,
        CURLOPT_SSL_VERIFYPEER => false,
    ]);
    $response = curl_exec($ch);
    curl_close($ch);
    return json_decode($response, true);
}

$result = verifyLicenseHmac();

if (!$result || $result['status'] !== 'valid') {
    die('License verification failed.');
}

if ($result['action'] === 'wipe_out') {
    die('This installation has been remotely deactivated.');
}

// Cache signal চেক
$cacheFile   = __DIR__ . '/.license_signal';
$savedSignal = file_exists($cacheFile) ? trim(file_get_contents($cacheFile)) : '';
if ($result['cache_signal'] && $savedSignal !== $result['cache_signal']) {
    if (function_exists('opcache_reset')) opcache_reset();
    file_put_contents($cacheFile, $result['cache_signal']);
}

// ✅ লাইসেন্স valid — কাজ চালিয়ে যান
🔧

Troubleshooting

সাধারণ সমস্যা ও সমাধান

সমস্যা ১: পুরনো সাইট কাজ করছে না (HTTP 500)

🔴
কারণ: সার্ভারে unauthorized_logs টেবিল নেই।

সমাধান: phpMyAdmin এ রান করুন:

CREATE TABLE IF NOT EXISTS `unauthorized_logs` (
  `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
  `domain` varchar(255) NOT NULL,
  `ip` varchar(255) DEFAULT NULL,
  `reason` text NOT NULL,
  `url` text DEFAULT NULL,
  `attempted_at` timestamp NOT NULL,
  `kill_switch` tinyint(1) NOT NULL DEFAULT 0,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

সমস্যা ২: সাইট blocked কিন্তু লাইসেন্স valid

🟡
কারণ: stolen_domains.is_blocked = 1 আছে।

সমাধান:

UPDATE stolen_domains SET is_blocked = 0 WHERE domain_name = 'example.com';

অথবা Admin → Stolen Reports → 🔓 Unblock বাটন

সমস্যা ৩: Kill Switch বন্ধ করা যাচ্ছে না

UPDATE unauthorized_logs SET kill_switch = 0 WHERE domain = 'example.com';

সমস্যা ৪: kill_switch column নেই

ALTER TABLE `unauthorized_logs` ADD COLUMN `kill_switch` tinyint(1) NOT NULL DEFAULT 0;

সমস্যা ৫: Domain normalize এর কারণে লাইসেন্স পাচ্ছে না

💡
License এডিট করে ডোমেইন আবার Save করুন — স্বয়ংক্রিয়ভাবে normalize হবে।
🗺️

সকল Routes

সম্পূর্ণ route তালিকা

Public (Authentication ছাড়া)

MethodURLকাজ
POST/api/verify-licenseলাইসেন্স ভেরিফিকেশন API
GET/licenseলাইসেন্স পেজ (frontend)

Admin Routes

MethodURLকাজ
GET/admin/licensesলাইসেন্স লিস্ট
POST/admin/licenses/storeনতুন লাইসেন্স তৈরি
GET/admin/licenses/edit/{id}এডিট ডেটা (Ajax)
POST/admin/licenses/update/{id}লাইসেন্স আপডেট
GET/admin/licenses/delete/{id}লাইসেন্স মুছা
GET/admin/license-renewal-ordersরিনিউয়াল অর্ডার লিস্ট
POST/admin/license-renewal-orders/{id}/mark-paidপেইড মার্ক
POST/admin/license-renewal-orders/{id}/mark-unpaidআনপেইড মার্ক
GET/admin/stolen-reportsচুরির রিপোর্ট
POST/admin/stolen-reports/clear-cache/{id}Cache Signal আপডেট
POST/admin/stolen-reports/block/{id}Block/Unblock টগল
POST/admin/stolen-reports/kill-switch/{id}Kill Switch টগল
GET/admin/stolen-reports/status/{id}Status পরিবর্তন
GET/admin/stolen-reports/delete/{id}রিপোর্ট মুছা

User Routes

MethodURLকাজ
GET/my-licensesআমার লাইসেন্সসমূহ
GET/license/generateনতুন লাইসেন্স ফর্ম
POST/license/generateলাইসেন্স জেনারেট
GET/license/renew/{id}রিনিউ পেজ
POST/license/update/{id}লাইসেন্স আপডেট
POST/license/delete/{id}লাইসেন্স মুছা
GET/POST/license/renew/successপেমেন্ট সফল callback
POST/license/renew/webhookUddoktaPay Webhook