DEVELOPER DOCS

NetVerify API

卡密激活、设备绑定与在线校验。所有接口使用 JSON,生产环境必须走 HTTPS。

控制台登录
v1 API 已默认关闭(ENABLE_V1_API=0)。新客户端必须使用下面的 v2 设备密钥接口;v1 示例仅用于兼容旧版本。

接入流程

01
激活

客户端提交 card key 和机器码,换取短期 token。

02
校验

启动/定时调用 verify,服务端检查卡密、设备与有效期。

03
解绑

用户换机时调用 deactivate,释放设备名额。

V2 设备密钥接入(推荐)

Android 端使用 Keystore P-256 私钥签名 challenge,服务端验签后下发短期 ECDSA P-256 授权票据。单纯复制 token 无法通过校验。

01
取服务端公钥

GET /api/v2/public-key,把 PEM 固定在 APK 里。

02
申请 challenge

POST /api/v2/challenge,拿到 message 和 120 秒 nonce。

03
签名并激活

用 Keystore 私钥签名,调用 /api/v2/activate 或 /api/v2/verify。

POST /api/v2/challenge
{
  "app_key": "app_xxx",
  "app_secret": "sk_xxx",
  "license_key": "NV-XXXX-XXXX-XXXX",
  "device_id": "stable-device-id",
  "purpose": "activate"
}

→ { "challenge_id": "...", "message": "netverify:v2:activate:...:...", "expires_at": "..." }

POST /api/v2/activate
{
  "...": "app_key / app_secret / license_key / device_id",
  "device_name": "Pixel 8",
  "public_key": "BASE64_X509_DER",
  "challenge_id": "...",
  "signature": "BASE64_DER_ECDSA_SIGNATURE",
  "apk_cert_sha256": "可选,APK 签名指纹"
}

Android SDK 在源码包 android-sdk/,包含 Keystore 密钥生成、challenge 签名、授权票据验签和 APK 签名指纹采集。

基础信息

Base URLhttps://72-249-200-157.sslip.io
Content-Typeapplication/json
鉴权app_key + app_secret
字符集UTF-8

POST /api/v1/activate(v1 旧版,默认关闭)

首次激活或重新登录。成功后返回 token,并绑定 device_id。

{
  "app_key": "app_xxxxxxxx",
  "app_secret": "sk_xxxxxxxx",
  "license_key": "NV-XXXX-XXXX-XXXX",
  "device_id": "machine-fingerprint-001",
  "device_name": "Windows PC"
}

成功响应:

{
  "ok": true,
  "data": {
    "token": "short-lived-token",
    "token_expires_at": "2026-10-28T12:00:00+00:00",
    "license": {
      "license_key": "NV-XXXX-XXXX-XXXX",
      "status": "active",
      "duration_days": 30,
      "max_devices": 1,
      "activated_at": "2026-10-27T12:00:00+00:00",
      "expires_at": "2026-11-26T12:00:00+00:00",
      "last_verified_at": "2026-10-27T12:00:00+00:00"
    },
    "app": {
      "name": "Desktop Client",
      "latest_version": "1.0.0",
      "download_url": "https://example.com/download",
      "notice": "欢迎使用"
    },
    "server_time": "2026-10-27T12:00:00+00:00"
  }
}

POST /api/v1/verify(v1 旧版,默认关闭)

每次启动或定时校验调用。token 过期后重新调用 activate。

{
  "app_key": "app_xxxxxxxx",
  "app_secret": "sk_xxxxxxxx",
  "license_key": "NV-XXXX-XXXX-XXXX",
  "device_id": "machine-fingerprint-001",
  "token": "short-lived-token"
}

成功响应:

{
  "ok": true,
  "data": {
    "valid": true,
    "token_expires_at": "2026-10-28T12:00:00+00:00",
    "license": { "...": "同上" },
    "app": { "...": "同上" },
    "server_time": "2026-10-27T12:01:00+00:00"
  }
}

POST /api/v1/deactivate(v1 旧版,默认关闭)

释放当前设备。建议在软件“解绑设备”或退出登录时调用。

{
  "app_key": "app_xxxxxxxx",
  "app_secret": "sk_xxxxxxxx",
  "license_key": "NV-XXXX-XXXX-XXXX",
  "device_id": "machine-fingerprint-001"
}

Python 客户端示例(v1 旧版)

import hashlib
import platform
import uuid
import requests

BASE = "https://your-domain.example"
APP_KEY = "app_xxxxxxxx"
APP_SECRET = "sk_xxxxxxxx"
LICENSE_KEY = "NV-XXXX-XXXX-XXXX"


def device_id() -> str:
    raw = f"{platform.node()}|{platform.machine()}|{uuid.getnode()}"
    return hashlib.sha256(raw.encode()).hexdigest()[:32]


def activate():
    r = requests.post(f"{BASE}/api/v1/activate", json={
        "app_key": APP_KEY,
        "app_secret": APP_SECRET,
        "license_key": LICENSE_KEY,
        "device_id": device_id(),
        "device_name": platform.node(),
    }, timeout=15)
    r.raise_for_status()
    return r.json()["data"]


def verify(token: str):
    r = requests.post(f"{BASE}/api/v1/verify", json={
        "app_key": APP_KEY,
        "app_secret": APP_SECRET,
        "license_key": LICENSE_KEY,
        "device_id": device_id(),
        "token": token,
    }, timeout=15)
    return r.json()

错误码

code含义
app_not_foundApp Key 不存在
app_disabled应用已停用
app_secret_invalidApp Secret 错误
invalid_license卡密无效、已过期、已吊销或未绑定(统一返回,防枚举)
device_limit设备数已满
device_key_mismatch设备密钥与已绑定密钥不一致
challenge_invalidchallenge 过期或已使用
signature_invalid设备签名验证失败
apk_mismatchAPK 签名指纹不匹配
entitlement_invalid授权票据签名/设备绑定无效
invalid_tokentoken 无效
session_expiredtoken 过期,需要重新激活
rate_limited请求过于频繁

接入建议

  • device_id 使用稳定机器码(CPU/主板/磁盘/安装 ID 的哈希),不要每次启动随机生成。
  • 客户端保存 token,启动时先 verify;token 过期再 activate。
  • 先用一次 activate 完成设备绑定,避免每次启动都占用新设备名额。
  • App Secret 会进入客户端,务必加壳/混淆;服务端仍以卡密、设备绑定和有效期为准。
  • 生产环境只通过 HTTPS 暴露 API;不要把数据库或管理后台直接开放到公网。