首页|接入文档
API v1

npm SDK

API v1

npm SDK

@ttqsoft/verify 是甜甜圈网络验证官方 JavaScript / TypeScript SDK。SDK 会自动处理公共参数、随机 nonce、时间戳和 HMAC-SHA256 签名,适合 Node.js、Electron、桌面客户端本地服务、服务端等环境。

appSecret 会参与签名,请勿放在浏览器前端代码中。纯网页应用应通过自己的服务端转发调用。

安装

npm install @ttqsoft/verify

初始化

import { TtqVerifyClient } from "@ttqsoft/verify";

const client = new TtqVerifyClient({
  baseUrl: "https://ttqsoft.com",
  appKey: "YOUR_APP_KEY",
  appSecret: "YOUR_APP_SECRET",
});
参数类型说明
baseUrlstring验证服务器地址,例如 https://ttqsoft.com 或自部署地址
appKeystring软件 App Key
appSecretstring软件 App Secret,仅应在可信环境使用

卡密模式

const login = await client.card.login({
  card: "XXXX-XXXX-XXXX-XXXX",
  deviceId: "device-001",
});

console.log(login.token);
console.log(login.expires);
console.log(login.hg);

await client.card.heartbeat({
  card: "XXXX-XXXX-XXXX-XXXX",
  token: login.token,
});

await client.card.logout({
  card: "XXXX-XXXX-XXXX-XXXX",
  token: login.token,
});

常用方法:

方法说明
client.card.login(params)卡密登录,首次使用时激活卡密并绑定设备
client.card.heartbeat(params)会话心跳,刷新 token 有效期
client.card.logout(params)登出并释放多开名额
client.card.recharge(params)使用未激活卡密为当前卡密续期
client.card.unbindDevice(params)解绑设备,需软件开启解绑功能
client.card.getConfig(params)获取卡密配置
client.card.setConfig(params)更新卡密配置,最大 512 字符

用户模式

await client.user.register({
  username: "alice",
  password: "123456",
});

const login = await client.user.login({
  username: "alice",
  password: "123456",
  deviceId: "device-001",
});

await client.user.heartbeat({ token: login.token });
await client.user.logout({ token: login.token });

用户登录后可以读写自己的云端数据,适合保存软件配置、偏好、进度和云存档。单个用户在同一软件下最多保存 5MB 云端数据,单个 keyvalue 最多 1MB:

await client.user.setData({
  token: login.token,
  key: "settings",
  value: JSON.stringify({ theme: "dark", volume: 80 }),
});

const data = await client.user.getData({
  token: login.token,
  key: "settings",
});

const settings = JSON.parse(data.value);

await client.user.deleteData({
  token: login.token,
  key: "settings",
});

常用方法:

方法说明
client.user.register(params)终端用户注册
client.user.login(params)用户名密码登录
client.user.heartbeat(params)用户会话心跳
client.user.logout(params)用户登出
client.user.unbindDevice(params)解绑用户设备
client.user.getData(params)读取当前用户自己的云端数据
client.user.setData(params)保存当前用户自己的云端数据
client.user.deleteData(params)删除当前用户自己的云端数据

软件接口

const config = await client.software.getConfig();
const notice = await client.software.getNotice();

try {
  const latest = await client.software.getLatestVersion({ version: "1.0.0" });
  console.log(latest.version, latest.url);
} catch (err) {
  // VerifyErrorCode.AlreadyLatestVersion 表示已是最新版
}
方法说明
client.software.getConfig()获取软件全局配置
client.software.getNotice()获取软件公告
client.software.getLatestVersion(params)检查新版本

远程功能

const remoteVar = await client.af.getRemoteVar({ key: "announcement" });
const remoteData = await client.af.getRemoteData({ key: "myKey" });

await client.af.setRemoteData({ action: "create", key: "myKey", value: "hello" });
await client.af.setRemoteData({ action: "update", key: "myKey", value: "world" });
await client.af.setRemoteData({ action: "delete", key: "myKey" });

const result = await client.af.callRemoteFunc<{ ok: boolean }>({
  funcName: "checkAccess",
  params: { level: 3 },
  card: "XXXX-XXXX-XXXX-XXXX",
  token: login.token,
});

console.log(result.value);
方法说明
client.af.getRemoteVar(params)获取远程变量
client.af.getRemoteData(params)获取远程数据
client.af.setRemoteData(params)新建、修改或删除远程数据
client.af.callRemoteFunc(params)调用远程函数

错误处理

所有接口失败时会抛出 VerifyError

import { VerifyError, VerifyErrorCode } from "@ttqsoft/verify";

try {
  await client.card.login({ card: "XXXX", deviceId: "device-001" });
} catch (err) {
  if (err instanceof VerifyError) {
    if (err.code === VerifyErrorCode.CardExpired) {
      console.log("卡密已过期");
    } else if (err.code === VerifyErrorCode.TokenExpired) {
      console.log("登录状态已失效,请重新登录");
    } else {
      console.log(err.code, err.message);
    }
  }
}

完整卡密生命周期示例

import { existsSync, readFileSync, writeFileSync } from "node:fs";
import { TtqVerifyClient, VerifyError, VerifyErrorCode } from "@ttqsoft/verify";

const client = new TtqVerifyClient({
  baseUrl: "https://ttqsoft.com",
  appKey: "YOUR_APP_KEY",
  appSecret: "YOUR_APP_SECRET",
});

const CARD = "XXXX-XXXX-XXXX-XXXX";
const DEVICE_ID = "device-001";
const SESSION_FILE = "./session.json";

interface Session { token: string; hg: number }

function loadSession(): Session | null {
  if (!existsSync(SESSION_FILE)) return null;
  try { return JSON.parse(readFileSync(SESSION_FILE, "utf8")) as Session; }
  catch { return null; }
}

function saveSession(session: Session) {
  writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2));
}

async function authenticate() {
  const saved = loadSession();
  if (saved?.token) {
    try {
      await client.card.heartbeat({ card: CARD, token: saved.token });
      return saved;
    } catch (err) {
      if (!(err instanceof VerifyError && err.code === VerifyErrorCode.TokenExpired)) throw err;
    }
  }

  const login = await client.card.login({ card: CARD, deviceId: DEVICE_ID });
  const session = { token: login.token, hg: login.hg };
  saveSession(session);
  return session;
}

const session = await authenticate();
setInterval(() => {
  void client.card.heartbeat({ card: CARD, token: session.token });
}, session.hg * 1000);