开发文档

TextPress 开发者文档

从入门到精通,帮助你快速掌握 TextPress 开发技巧

API参考 授权验证

授权验证 安全

TextPress 提供完整的授权验证 API,帮助开发者保护知识产权,实现正版授权验证。

授权码验证

验证用户授权码是否有效

域名绑定

支持多域名授权管理

有效期管理

支持永久或限时授权

实时验证

支持 CORS 跨域请求

授权验证流程

工作原理

用户购买产品后获得授权码,在产品中配置授权码和域名,产品启动时调用 API 验证授权状态。

1

用户购买产品

用户在平台购买你的主题或插件,系统自动生成唯一授权码

2

绑定使用域名

用户在个人中心绑定要使用产品的域名(支持多个域名)

3

配置授权码

用户在产品后台设置中填入授权码

4

验证授权

产品调用 API 验证授权码和域名是否匹配

验证授权接口

GET POST /api/license/verify

验证授权码是否有效,支持 GET 和 POST 两种请求方式。

请求参数

参数类型必填说明
license_keystring用户购买后获得的授权码
domainstring当前使用的域名
product_slugstring产品标识,用于验证授权码是否匹配特定产品

请求示例

HTTP
GET /api/license/verify?license_key=XXXX-XXXX-XXXX-XXXX&domain=example.com&product_slug=my-theme

成功响应

JSON
{
    "valid": true,
    "code": "SUCCESS",
    "message": "授权有效",
    "license": {
        "product": "My Theme",
        "product_slug": "my-theme",
        "version": "v1.2.0",
        "domain": "example.com",
        "domains": ["example.com", "dev.example.com"],
        "max_domains": 3,
        "expires_at": "2025-12-31 23:59:59",
        "created_at": "2024-01-01 00:00:00"
    }
}

错误响应

JSON
{
    "valid": false,
    "code": "DOMAIN_NOT_AUTHORIZED",
    "message": "域名未授权",
    "authorized_domains": ["other-domain.com"],
    "max_domains": 3
}

响应字段说明

字段类型说明
validboolean授权是否有效
codestring状态码,用于程序判断
messagestring提示信息,可直接展示给用户
licenseobject授权详情(仅当 valid=true 时返回)
license.productstring产品名称
license.product_slugstring产品标识
license.versionstring产品最新版本
license.domainstring当前验证的域名
license.domainsarray已授权的所有域名
license.max_domainsint最大可绑定域名数
license.expires_atstring授权到期时间,"永久" 表示永不过期

批量检查授权接口

GET POST /api/license/check

检查指定域名是否拥有某些产品的授权,用于市场安装判断。

请求参数

参数类型必填说明
domainstring要检查的域名
product_idsstring/array产品ID列表,逗号分隔或数组,不传则返回该域名所有授权

请求示例

HTTP
GET /api/license/check?domain=example.com&product_ids=1,2,3

响应示例

JSON
{
    "success": true,
    "domain": "example.com",
    "authorized_products": [1, 3],
    "licenses": {
        "1": {
            "product_id": 1,
            "product_name": "Premium Theme",
            "product_slug": "premium-theme",
            "version": "v1.2.0",
            "download_url": "https://example.com/uploads/products/1/premium-theme.zip",
            "license_key": "XXXX-XXXX-XXXX-XXXX",
            "expires_at": "2025-12-31 23:59:59"
        },
        "3": {
            "product_id": 3,
            "product_name": "Pro Plugin",
            "product_slug": "pro-plugin",
            "version": "v2.0.0",
            "download_url": "https://example.com/uploads/products/3/pro-plugin.zip",
            "license_key": "YYYY-YYYY-YYYY-YYYY",
            "expires_at": "永久"
        }
    }
}

使用场景

此接口主要用于市场功能,在加载市场列表时检查当前域名是否已购买某些付费产品,如果已授权则显示"安装"按钮而非"购买"按钮。

JavaScript
// 检查付费产品的授权状态
async function checkLicenses(productIds) {
    const url = new URL('https://textpress.cn/api/license/check');
    url.searchParams.set('domain', window.location.hostname);
    url.searchParams.set('product_ids', productIds.join(','));
    
    const response = await fetch(url.toString());
    const data = await response.json();
    
    if (data.success) {
        // data.authorized_products 包含已授权的产品ID
        // data.licenses 包含授权详情和下载地址
        return data.licenses;
    }
    return {};
}

错误码说明

错误处理

validfalse 时,请根据 code 字段判断具体错误类型并给用户友好提示。

错误码说明处理建议
SUCCESS验证成功授权有效,正常使用
MISSING_LICENSE_KEY缺少授权码参数提示用户输入授权码
MISSING_DOMAIN缺少域名参数自动获取当前域名后重试
INVALID_LICENSE授权码无效提示用户检查授权码是否正确
LICENSE_EXPIRED授权已过期提示用户续费,可提供续费链接
LICENSE_REVOKED授权已被撤销提示联系客服处理
PRODUCT_MISMATCH授权码与产品不匹配检查是否使用了正确的授权码
DOMAIN_NOT_AUTHORIZED域名未授权提示用户在用户中心绑定当前域名

集成示例

以下是在你的主题或插件中集成授权验证的完整示例代码:

PHP
<?php
/**
 * 授权验证类 - 完整实现
 */
class LicenseManager {
    private $apiUrl = 'https://textpress.cn/api/license/verify';
    private $productSlug = 'my-theme';
    private $cacheFile;
    private $cacheTime = 86400; // 24小时
    
    public function __construct() {
        $this->cacheFile = __DIR__ . '/.license_cache';
    }
    
    /**
     * 验证授权
     */
    public function verify($licenseKey) {
        // 先检查缓存
        $cached = $this->getCache($licenseKey);
        if ($cached !== false) {
            return $cached;
        }
        
        // 调用 API 验证
        $result = $this->callApi($licenseKey);
        
        // 缓存结果
        if ($result) {
            $this->setCache($licenseKey, $result);
        }
        
        return $result;
    }
    
    /**
     * 调用验证 API
     */
    private function callApi($licenseKey) {
        $params = [
            'license_key' => $licenseKey,
            'domain' => $_SERVER['HTTP_HOST'],
            'product_slug' => $this->productSlug
        ];
        
        $ch = curl_init();
        curl_setopt_array($ch, [
            CURLOPT_URL => $this->apiUrl . '?' . http_build_query($params),
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 10,
            CURLOPT_SSL_VERIFYPEER => true,
            CURLOPT_HTTPHEADER => ['Accept: application/json']
        ]);
        
        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        
        if ($httpCode !== 200 || !$response) {
            return ['valid' => false, 'code' => 'NETWORK_ERROR', 'message' => '网络请求失败'];
        }
        
        return json_decode($response, true);
    }
    
    /**
     * 获取缓存
     */
    private function getCache($licenseKey) {
        if (!file_exists($this->cacheFile)) return false;
        
        $data = json_decode(file_get_contents($this->cacheFile), true);
        if (!$data || $data['key'] !== md5($licenseKey)) return false;
        if (time() - $data['time'] > $this->cacheTime) return false;
        
        return $data['result'];
    }
    
    /**
     * 设置缓存
     */
    private function setCache($licenseKey, $result) {
        $data = [
            'key' => md5($licenseKey),
            'time' => time(),
            'result' => $result
        ];
        file_put_contents($this->cacheFile, json_encode($data));
    }
    
    /**
     * 清除缓存
     */
    public function clearCache() {
        if (file_exists($this->cacheFile)) {
            unlink($this->cacheFile);
        }
    }
}

// 使用示例
$license = new LicenseManager();
$result = $license->verify('XXXX-XXXX-XXXX-XXXX');

if ($result['valid']) {
    echo '授权有效,到期时间:' . $result['license']['expires_at'];
} else {
    echo '授权无效:' . $result['message'];
}
JavaScript
/**
 * 授权验证模块
 */
const LicenseManager = {
    apiUrl: 'https://textpress.cn/api/license/verify',
    productSlug: 'my-theme',
    cacheKey: 'license_cache',
    cacheTime: 24 * 60 * 60 * 1000, // 24小时
    
    // 验证授权
    async verify(licenseKey) {
        // 检查缓存
        const cached = this.getCache(licenseKey);
        if (cached) return cached;
        
        // 调用 API
        try {
            const params = new URLSearchParams({
                license_key: licenseKey,
                domain: window.location.hostname,
                product_slug: this.productSlug
            });
            
            const response = await fetch(`${this.apiUrl}?${params}`);
            const data = await response.json();
            
            // 缓存结果
            this.setCache(licenseKey, data);
            
            return data;
        } catch (error) {
            console.error('授权验证失败:', error);
            return { valid: false, code: 'NETWORK_ERROR', message: '网络请求失败' };
        }
    },
    
    // 获取缓存
    getCache(licenseKey) {
        try {
            const data = JSON.parse(localStorage.getItem(this.cacheKey));
            if (!data || data.key !== this.hash(licenseKey)) return null;
            if (Date.now() - data.time > this.cacheTime) return null;
            return data.result;
        } catch {
            return null;
        }
    },
    
    // 设置缓存
    setCache(licenseKey, result) {
        localStorage.setItem(this.cacheKey, JSON.stringify({
            key: this.hash(licenseKey),
            time: Date.now(),
            result
        }));
    },
    
    // 简单哈希
    hash(str) {
        let hash = 0;
        for (let i = 0; i < str.length; i++) {
            hash = ((hash << 5) - hash) + str.charCodeAt(i);
            hash |= 0;
        }
        return hash.toString(16);
    },
    
    // 显示错误提示
    showError(data) {
        const messages = {
            'INVALID_LICENSE': '授权码无效,请检查是否输入正确',
            'LICENSE_EXPIRED': '授权已过期,请续费后继续使用',
            'DOMAIN_NOT_AUTHORIZED': '当前域名未授权,请在用户中心绑定域名',
            'NETWORK_ERROR': '网络连接失败,请检查网络后重试'
        };
        alert(messages[data.code] || data.message);
    }
};

// 使用示例
async function checkLicense() {
    const result = await LicenseManager.verify('XXXX-XXXX-XXXX-XXXX');
    if (result.valid) {
        console.log('授权有效', result.license);
    } else {
        LicenseManager.showError(result);
    }
}
PHP (WordPress)
<?php
/**
 * WordPress 主题/插件授权验证
 */

// 添加设置页面
add_action('admin_menu', function() {
    add_options_page('授权管理', '授权管理', 'manage_options', 'license-settings', 'render_license_page');
});

// 渲染设置页面
function render_license_page() {
    if (isset($_POST['license_key'])) {
        update_option('my_theme_license', sanitize_text_field($_POST['license_key']));
        delete_transient('my_theme_license_check');
        echo '<div class="notice notice-success"><p>授权码已保存</p></div>';
    }
    
    $license = get_option('my_theme_license', '');
    $status = verify_license_status();
    ?>
    <div class="wrap">
        <h1>授权管理</h1>
        <form method="post">
            <table class="form-table">
                <tr>
                    <th>授权码</th>
                    <td>
                        <input type="text" name="license_key" value="<?= esc_attr($license) ?>" class="regular-text">
                        <p class="description">请输入购买时获得的授权码</p>
                    </td>
                </tr>
                <tr>
                    <th>授权状态</th>
                    <td>
                        <?php if ($status['valid']): ?>
                            <span style="color:green">已授权</span>
                            - 到期时间:<?= $status['license']['expires_at'] ?>
                        <?php else: ?>
                            <span style="color:red">未授权</span>
                            - <?= $status['message'] ?>
                        <?php endif; ?>
                    </td>
                </tr>
            </table>
            <?php submit_button('保存授权码'); ?>
        </form>
    </div>
    <?php
}

// 验证授权状态
function verify_license_status() {
    $license = get_option('my_theme_license', '');
    if (!$license) {
        return ['valid' => false, 'code' => 'NO_LICENSE', 'message' => '请输入授权码'];
    }
    
    // 使用 transient 缓存
    $cached = get_transient('my_theme_license_check');
    if ($cached !== false) return $cached;
    
    // 调用 API
    $response = wp_remote_get('https://textpress.cn/api/license/verify?' . http_build_query([
        'license_key' => $license,
        'domain' => $_SERVER['HTTP_HOST'],
        'product_slug' => 'my-theme'
    ]));
    
    if (is_wp_error($response)) {
        return ['valid' => false, 'code' => 'NETWORK_ERROR', 'message' => '网络请求失败'];
    }
    
    $result = json_decode(wp_remote_retrieve_body($response), true);
    set_transient('my_theme_license_check', $result, DAY_IN_SECONDS);
    
    return $result;
}

// 在后台显示授权提示
add_action('admin_notices', function() {
    $status = verify_license_status();
    if (!$status['valid']) {
        echo '<div class="notice notice-warning"><p>主题未授权:' . esc_html($status['message']) . 
             ' <a href="' . admin_url('options-general.php?page=license-settings') . '">去授权</a></p></div>';
    }
});

最佳实践

缓存验证结果

将结果缓存 24 小时,减少 API 调用,提升性能

优雅降级

网络失败时使用缓存结果,避免影响用户体验

友好提示

给用户清晰的错误提示和解决方案

安全存储

授权码存数据库,不要硬编码在代码中

安全建议

重要提示

授权验证应在服务端进行,不要仅依赖前端验证。前端验证可以作为用户体验优化,但核心功能限制必须在服务端实现。

服务端验证

核心功能的授权检查必须在服务端进行,前端验证容易被绕过

HTTPS 传输

确保 API 请求使用 HTTPS,防止授权码在传输过程中被截获

隐藏授权码

在前端展示时对授权码进行脱敏处理,如只显示前后几位

定期验证

建议每天验证一次,及时发现授权状态变化

常见问题

Q 授权验证失败但网络正常?

A: 请检查:1) 授权码是否正确;2) 当前域名是否已在用户中心绑定;3) 授权是否已过期。

Q 如何支持本地开发环境?

A: 可以将 localhost 或本地 IP 添加到授权域名中,或在开发环境跳过验证。

Q API 请求超时怎么办?

A: 建议设置合理的超时时间(如 10 秒),超时后使用缓存结果或允许临时使用。

Q 可以同时在多个域名使用吗?

A: 取决于授权类型,响应中的 max_domains 字段表示最大可绑定域名数。

相关文档

反馈 顶部