使用文档

时光授权系统文档

从快速开始到 API 对接,全面了解授权系统的使用与集成

PHP 示例

本章提供完整可运行的 PHP 代码,所有函数都已通过单元测试。可以直接 require 到你的项目中使用。

1. 独立授权类(无 WordPress 依赖)

适用于:纯 PHP 项目、ThinkPHP、Laravel 等。

<?php
/**
 * Shiguang License Client
 *
 * 独立的授权客户端类,可用于任何 PHP 项目
 * 要求:PHP 7.2+,需要 cURL 扩展
 *
 * @version 1.0.0
 * @license MIT
 */

class ShiguangLicenseClient {
    private $server_url;
    private $product_slug;
    private $api_secret;

    public function __construct($server_url, $product_slug, $api_secret) {
        $this->server_url = rtrim($server_url, '/');
        $this->product_slug = $product_slug;
        $this->api_secret = $api_secret;
    }

    /**
     * 标准化域名
     */
    public static function normalizeDomain($input) {
        $domain = strtolower(trim($input));
        $domain = preg_replace('#^https?://#', '', $domain);
        if (($pos = strpos($domain, '/')) !== false) {
            $domain = substr($domain, 0, $pos);
        }
        if (($pos = strpos($domain, ':')) !== false) {
            $domain = substr($domain, 0, $pos);
        }
        $domain = preg_replace('#^www\.#', '', $domain);
        return $domain;
    }

    /**
     * 生成 API 签名
     */
    private function sign($domain, $license_key, $serial = 1) {
        $sign_data = $domain . '|' . $license_key . '|' . $this->product_slug . '|' . $serial;
        return hash_hmac('sha256', $sign_data, $this->api_secret);
    }

    /**
     * 在线验证授权
     */
    public function verifyOnline($license_key, $domain = null) {
        if ($domain === null) {
            $domain = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '';
        }
        $domain = self::normalizeDomain($domain);

        $serial = 1;
        $signature = $this->sign($domain, $license_key, $serial);

        $ch = curl_init();
        curl_setopt_array($ch, [
            CURLOPT_URL            => $this->server_url . '/api/verify.php',
            CURLOPT_POST           => true,
            CURLOPT_POSTFIELDS     => http_build_query([
                'license_key' => $license_key,
                'domain'      => $domain,
                'product'     => $this->product_slug,
                'serial'      => $serial,
                'signature'   => $signature,
            ]),
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT        => 15,
            CURLOPT_SSL_VERIFYPEER => true,
            CURLOPT_SSL_VERIFYHOST => 2,
        ]);

        $response = curl_exec($ch);
        $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $error = curl_error($ch);
        curl_close($ch);

        if ($response === false) {
            return ['status' => 'error', 'message' => '网络错误:' . $error];
        }

        $data = json_decode($response, true);
        if (!$data) {
            return ['status' => 'error', 'message' => '服务器返回无效响应 (HTTP ' . $http_code . ')'];
        }

        return $data;
    }

    /**
     * 带本地缓存的授权检查(缓存 7 天)
     *
     * @param string $license_key 授权码
     * @param string|null $domain  域名(默认当前访问域名)
     * @param int $cache_days       缓存天数(默认 7)
     * @param string|null $cache_file 缓存文件路径(默认 /tmp/shiguang_license.cache)
     * @return array 授权信息
     */
    public function checkLicense($license_key, $domain = null, $cache_days = 7, $cache_file = null) {
        if ($cache_file === null) {
            $cache_file = sys_get_temp_dir() . '/shiguang_license_' . md5($license_key) . '.cache';
        }

        // 读取缓存
        if (file_exists($cache_file)) {
            $cached = @json_decode(file_get_contents($cache_file), true);
            if ($cached && isset($cached['last_check']) && isset($cached['info'])) {
                $age = time() - strtotime($cached['last_check']);
                if ($age < $cache_days * 86400 && ($cached['info']['status'] ?? '') === 'active') {
                    // 缓存有效,但本地再校验一次域名
                    $current = self::normalizeDomain($domain ?? ($_SERVER['HTTP_HOST'] ?? ''));
                    if (($cached['info']['domain'] ?? '') === $current) {
                        return $cached['info'];
                    }
                }
            }
        }

        // 在线验证
        $result = $this->verifyOnline($license_key, $domain);

        if (($result['status'] ?? '') === 'success') {
            $info = [
                'status'      => 'active',
                'license_key' => $license_key,
                'domain'      => $result['license']['domain'],
                'type'        => $result['license']['type'],
                'expires_at'  => $result['license']['expires_at'],
                'max_sites'   => $result['license']['max_sites'],
                'last_check'  => date('Y-m-d H:i:s'),
            ];

            // 写缓存
            @file_put_contents($cache_file, json_encode([
                'last_check' => $info['last_check'],
                'info'       => $info,
            ]));

            return $info;
        }

        return ['status' => 'error', 'message' => $result['message'] ?? '验证失败'];
    }

    /**
     * 仅检查授权是否有效(快捷方法)
     */
    public function isLicensed($license_key, $domain = null) {
        $info = $this->checkLicense($license_key, $domain);
        return ($info['status'] ?? '') === 'active';
    }
}

2. 使用示例

基本用法

<?php
require_once 'ShiguangLicenseClient.php';

$client = new ShiguangLicenseClient(
    'https://license.example.com',    // 授权服务器地址
    'my-awesome-plugin',              // 产品 slug
    'your-api-secret-here'            // API 密钥
);

// 检查授权
$license_key = 'LIC-9C20E-C6A64-B6DE1-D4F90';
if ($client->isLicensed($license_key)) {
    echo "授权有效!\n";
} else {
    echo "授权无效\n";
}

// 获取详细信息
$info = $client->checkLicense($license_key);
print_r($info);

// 输出:
// Array (
//     [status] => active
//     [license_key] => LIC-9C20E-C6A64-B6DE1-D4F90
//     [domain] => example.com
//     [type] => standard
//     [expires_at] =>                       // null 表示永久
//     [max_sites] => 1
//     [last_check] => 2026-07-22 14:30:12
// )

作为中间件使用

<?php
// 中间件:验证所有需要授权的 API 调用

function require_license_or_die($license_key) {
    $client = new ShiguangLicenseClient(
        getenv('LICENSE_SERVER'),
        getenv('PRODUCT_SLUG'),
        getenv('API_SECRET')
    );

    if (!$client->isLicensed($license_key)) {
        http_response_code(403);
        echo json_encode(['error' => '授权无效,请购买后使用']);
        exit;
    }
}

// 在敏感 API 入口调用
if ($_SERVER['REQUEST_URI'] === '/api/premium-feature') {
    require_license_or_die($_POST['license_key'] ?? '');
}

3. 单元测试示例

用 PHPUnit 测试授权客户端:

<?php
// tests/ShiguangLicenseClientTest.php

use PHPUnit\Framework\TestCase;

class ShiguangLicenseClientTest extends TestCase {
    private $client;
    private $test_license_key = 'LIC-TEST1-TEST2-TEST3-TEST4';
    private $test_domain = 'test.example.com';

    protected function setUp(): void {
        $this->client = new ShiguangLicenseClient(
            getenv('LICENSE_SERVER') ?: 'https://license.example.com',
            getenv('PRODUCT_SLUG') ?: 'test-product',
            getenv('API_SECRET') ?: 'test-secret'
        );
    }

    public function testNormalizeDomain() {
        $this->assertEquals(
            'example.com',
            ShiguangLicenseClient::normalizeDomain('HTTPS://WWW.Example.COM/path')
        );
        $this->assertEquals(
            'example.com',
            ShiguangLicenseClient::normalizeDomain('example.com:8080')
        );
        $this->assertEquals(
            'localhost',
            ShiguangLicenseClient::normalizeDomain('http://localhost/wp-admin')
        );
    }

    public function testVerifyOnlineWithValidKey() {
        $result = $this->client->verifyOnline($this->test_license_key, $this->test_domain);
        $this->assertEquals('success', $result['status']);
        $this->assertArrayHasKey('license', $result);
    }

    public function testVerifyOnlineWithInvalidKey() {
        $result = $this->client->verifyOnline('LIC-INVALID-INVALID-INVALID-INVALID', $this->test_domain);
        $this->assertNotEquals('success', $result['status']);
    }

    public function testCheckLicenseWithCache() {
        // 第一次:在线验证
        $start = microtime(true);
        $info1 = $this->client->checkLicense($this->test_license_key, $this->test_domain, 7, '/tmp/test_license.cache');
        $online_time = microtime(true) - $start;
        $this->assertEquals('active', $info1['status']);

        // 第二次:应该命中缓存(< 1ms)
        $start = microtime(true);
        $info2 = $this->client->checkLicense($this->test_license_key, $this->domain ?? $this->test_domain, 7, '/tmp/test_license.cache');
        $cache_time = microtime(true) - $start;

        $this->assertEquals('active', $info2['status']);
        $this->assertLessThan(0.01, $cache_time); // 缓存命中 < 10ms
        $this->assertGreaterThan($cache_time * 10, $online_time); // 在线验证至少慢 10 倍
    }
}

运行测试:

LICENSE_SERVER=https://license.example.com \
PRODUCT_SLUG=test-product \
API_SECRET=test-secret \
vendor/bin/phpunit tests/ShiguangLicenseClientTest.php

4. Composer 包配置

如果你想发布为 Composer 包:

{
    "name": "shiguang/license-client",
    "description": "时光授权系统 PHP 客户端",
    "type": "library",
    "license": "MIT",
    "require": {
        "php": ">=7.2",
        "ext-curl": "*",
        "ext-json": "*",
        "ext-openssl": "*"
    },
    "autoload": {
        "psr-4": {
            "Shiguang\\License\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Shiguang\\License\\Tests\\": "tests/"
        }
    }
}

然后在你的项目中使用:

composer require shiguang/license-client
<?php
require_once 'vendor/autoload.php';

use Shiguang\License\ShiguangLicenseClient;

$client = new ShiguangLicenseClient(
    'https://license.example.com',
    'my-plugin',
    'secret'
);

if ($client->isLicensed('LIC-XXXXX-XXXXX-XXXXX-XXXXX')) {
    // 业务逻辑
}

5. 常见错误处理

错误原因解决
签名错误 api_secret 与服务器端不一致 联系授权方核对密钥
授权不存在 授权码拼写错误或不存在 从用户中心复制授权码
域名不匹配 购买时的域名与实际访问域名不同 在用户中心修改域名
授权已暂停 管理员暂停了授权 联系客服
授权已过期 超过 expires_at 续费
网络错误 无法连接授权服务器 检查服务器网络、防火墙
下一步:阅读 API 参考 → 授权验证接口,了解所有可用的 API 端点。