NVNetVerify Lab客户端接入实验室
LIVE API PLAYGROUND

把授权接口,
真正跑起来。

这是连接 sq.lbcm.wang 的真实客户端演示。签名在本站服务端生成,浏览器不会接触 AppSecret。

authorization-flow
01 Browser → Demo Backend
02 nonce + timestamp + HMAC-SHA256
03 POST https://sq.lbcm.wang/api/login
04 Bearer Token → heartbeat / status
✓ AppSecret stays server-side
2验证模式
8可测试接口
HMACSHA-256 签名
LIVE真实环境
01 / 在线控制台

选择模式并调用真实接口

所有操作都会请求生产授权服务,并在管理后台留下测试记录。

账号时间制

未登录
软件标识demo_soft_account本次 HWIDWEB-220F546EBA2CEBFA

注册测试账号

账号登录

API 响应

{ }

执行左侧操作后,这里会显示授权服务器的真实 JSON 响应。

02 / 接入教程

从签名到心跳,一次接通

生产客户端应自行实现签名与 Token 管理;下面的示例使用占位密钥,不包含本站真实 AppSecret。

STEP 1

准备软件参数

从管理后台的软件详情获取 soft_keyapp_secret。密钥应保存在受保护的服务端或客户端安全存储中。

STEP 2

生成请求签名

移除 sign 和空值字段,按键名 ASCII 升序排列,拼成查询串,末尾追加 &key=AppSecret,执行 HMAC-SHA256。

STEP 3

登录并保存 Token

账号模式传账号密码;卡密模式传卡密。成功后保存 64 位 Token,后续请求放入 Authorization: Bearer ...

STEP 4

按周期发送心跳

按响应中的 heartbeat_interval 调用心跳。根据状态决定客户端行为,网络短暂失败时结合宽限期处理。

签名算法

每次请求必须使用全新的 nonce,时间戳使用当前 Unix 秒。服务端默认只接受时间偏差在 5 分钟内的请求。

params = remove(sign, null, "")
params = sortByKeyASCII(params)
canonical = "key1=value1&key2=value2&key=APP_SECRET"
sign = HMAC_SHA256(canonical, APP_SECRET)

PHP 接入示例

$params = [
  'soft_key' => 'YOUR_SOFT_KEY',
  'username' => 'demo_user',
  'password' => 'Demo1234',
  'timestamp' => time(),
  'nonce' => bin2hex(random_bytes(12)),
];
ksort($params, SORT_STRING);
$pairs = [];
foreach ($params as $k => $v) {
  if ($v !== '' && $v !== null) $pairs[] = $k . '=' . $v;
}
$canonical = implode('&', $pairs) . '&key=' . $appSecret;
$params['sign'] = hash_hmac('sha256', $canonical, $appSecret);

JavaScript / Node.js 示例

import crypto from 'node:crypto';
const params = { soft_key, username, password,
  timestamp: Math.floor(Date.now() / 1000),
  nonce: crypto.randomBytes(12).toString('hex') };
const canonical = Object.keys(params).sort()
  .filter(k => params[k] !== '' && params[k] != null)
  .map(k => `${k}=${params[k]}`).join('&') + `&key=${appSecret}`;
params.sign = crypto.createHmac('sha256', appSecret)
  .update(canonical).digest('hex');

C# 接入示例

var sorted = new SortedDictionary<string, string>(StringComparer.Ordinal) {
  ["soft_key"] = softKey,
  ["timestamp"] = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString(),
  ["nonce"] = Convert.ToHexString(RandomNumberGenerator.GetBytes(12)).ToLower()
};
var canonical = string.Join("&", sorted.Select(x => $"{x.Key}={x.Value}"))
  + "&key=" + appSecret;
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(appSecret));
var sign = Convert.ToHexString(hmac.ComputeHash(Encoding.UTF8.GetBytes(canonical))).ToLower();

授权状态处理

active授权正常,开放业务功能
expired授权过期,引导用户兑换续费
invalid未激活或无效,不开放受保护功能
banned账号封禁,立即清除 Token 并退出
hwid_mismatch设备不匹配,提示解绑或联系客服

点数扣费与退款

每笔业务生成唯一 biz_id;网络超时重试必须复用同一个 ID。退款只提交原扣费 ID,退款点数由服务端从原流水读取,客户端不能指定。

POST /api/consume_points
{ "biz_id": "order_20260906_0001", "points": 10 }

POST /api/refund_points
{ "biz_id": "order_20260906_0001", "reason": "业务失败" }