授权 SDK 规范
vv3.1
授权 SDK 规范(v3)
依据:
ps-help/v3/doc/售卖方式与授权购买链路_v3.md、帮助中心 LICENSE_API_DOC(v3)。 目标:Node / Python / Java 三语言 SDK 行为一致(机器码、签名、接口、缓存、错误码、购买页跳转)。
1. 机器码算法(跨语言一致)
同一台机器上三语言 SDK 必须生成相同的 machineCode。
指纹来源按优先级选取,取到有效值即停止:
fingerprint = 硬件序列号 → 系统机器 ID → hostname | os | arch(兜底)
优先级 1:硬件序列号(BIOS SN,重装系统不变)
| 系统 | 来源 | 获取方式 | 权限 |
|---|---|---|---|
| Windows | BIOS SerialNumber | wmic bios get serialnumber(降级 PowerShell Get-CimInstance Win32_BIOS) |
普通用户 |
| macOS | 硬件序列号 | system_profiler SPHardwareDataType 提取 Serial Number |
普通用户 |
| Linux | 产品序列号 | 读 /sys/class/dmi/id/product_serial(降级 dmidecode -s system-serial-number) |
需 root |
优先级 2:系统机器 ID(安装时生成,同机不变)
| 系统 | 来源 | 获取方式 | 权限 |
|---|---|---|---|
| Windows | MachineGuid | 注册表 HKLM\SOFTWARE\Microsoft\Cryptography |
普通用户 |
| macOS | IOPlatformUUID | ioreg -d2 -c IOPlatformPlatformDevice |
普通用户 |
| Linux | machine-id | 读 /etc/machine-id(降级 /var/lib/dbus/machine-id) |
所有用户 |
优先级 3:兜底(hostname | os | arch)
当硬件序列号和系统机器 ID 均获取失败时使用。
| 字段 | Node | Python | Java |
|---|---|---|---|
| hostname | os.hostname() |
socket.gethostname() |
InetAddress.getLocalHost().getHostName() |
| os | os.platform() |
sys.platform |
System.getProperty("os.name") |
| arch | os.arch() |
platform.machine() |
System.getProperty("os.arch") |
统一处理
- 所有字段去除首尾空白并转小写;
- 过滤厂商占位值(
To be filled by O.E.M./None/0/Default/Not Available/Not Specified); - 拼接符统一为
|,整体再toLowerCase()。
machineCode = 'M' + base64url( sha256( fingerprint ) ).slice(0, 32)
machineCode 至少 8 位,全平台唯一,且 SDK 内部缓存(进程内)。
2. 签名规则(software/generate、software/upgrade 必须)
签名串按固定顺序、换行分隔(缺省填空值:edition 空串、expiryDays 0、licenseCode 空串):
productUniqueCode \n machineCode \n edition \n expiryDays \n clientOrderId \n licenseCode \n timestamp
signature = base64url( HMAC-SHA256( licenseApiSecret, 签名串 ) );timestamp 为毫秒,平台校验与服务器时间差 ≤ 5 分钟(防重放)。
3. 接口清单
前缀:https://www.powersoftware.app/frontApi(可通过 baseUrl 覆盖)。
| 接口 | 路径 | 鉴权 | SDK 方法 |
|---|---|---|---|
| 激活 | POST /license/activate |
无 | activate(licenseCode, machineCode) |
| 校验 | POST /license/verify |
无 | verify(licenseCode, machineCode, activationToken) |
| 解绑 | POST /license/deactivate |
登录(浏览器场景) | deactivate(licenseCode, machineCode) |
| 软件内发码 | POST /license/software/generate |
HMAC 签名 | generateForSoftware({...}) |
| 软件内升级/续费 | POST /license/software/upgrade |
HMAC 签名 | upgradeForSoftware({...}) |
| 领取试用 | POST /license/trial/claim |
无 | claimTrial(machineCode) |
generateForSoftware / upgradeForSoftware 自动补 timestamp + signature;claimTrial 需产品为先用后付。
4. 本地凭证与校验缓存
- 本地只存:
licenseCode、activationToken、最近一次 verify 结果({ valid, edition, expiryTime, trialExpiryTime }+ 时间戳)。 其中trialExpiryTime为试用到期时间快照(ISO8601,非试用转购买为null),可用于高阶功能宽限。 - 不存可解密的完整授权信息(防逆向无意义,只作缓存)。
- 校验缓存 TTL 60s:点击付费功能时先查缓存,未过期直接用;过期或失败再调服务端
verify。 - 服务端吊销/退款/升级会主动失效缓存(平台侧已实现),SDK 无需感知。
5. 付费功能拦截与购买页跳转
付费菜单按钮点击:
verifyCached(licenseCode, machineCode, activationToken)返回有效且未过期 → 放行;- 返回无效/过期/未激活 → 弹窗提示"需要购买激活授权";
- 生成
machineCode,跳转购买页(产品标识为productUniqueCode,携带机器码):
https://www.powersoftware.app/product/license/purchase?productUniqueCode={productUniqueCode}&machineCode={machineCode}
(多语言站点在路径前加语言前缀,如 /en-US/product/license/purchase;productUniqueCode 由构造器传入。)
5.1 购买站选择:.app(国际站) vs .cn(国内站)
幂栈网有两个站点,SDK 的 purchaseUrl() 通过 base 参数切换:
powersoftware.app(国际站) |
powersoftware.cn(国内站) |
|
|---|---|---|
| 支付方式 | 支付宝 + PayPal | 仅支付宝 |
| 国家/币种 | Cloudflare 按 IP 自动识别(CN→CNY,其余→USD) | 固定 country=CN,人民币 |
| 语言 | 按 URL 前缀 / Accept-Language 自动检测 |
固定 zh-CN |
| 适用用户 | 国际用户 / 海外 | 中国大陆用户 |
SDK 默认 .app,不做自动判断。 客户端软件需自行决定传哪个 base。
判断方式
方式一:按系统语言(推荐)
import locale
def get_purchase_base():
sys_lang = locale.getdefaultlocale()[0] or ""
if sys_lang.startswith("zh"):
return "https://www.powersoftware.cn"
return "https://www.powersoftware.app"
function getPurchaseBase() {
const lang = process.env.LANG || "";
return lang.toLowerCase().startsWith("zh")
? "https://www.powersoftware.cn"
: "https://www.powersoftware.app";
}
String getPurchaseBase() {
return "zh".equals(Locale.getDefault().getLanguage())
? "https://www.powersoftware.cn"
: "https://www.powersoftware.app";
}
方式二:用户设置项 — 设置界面提供“地区”选项,用户自行选择。
方式三:硬编码 — 仅面向国内用户的软件直接写死 base="https://www.powersoftware.cn"。
详见 语言与国家逻辑 第 2.4 节。
6. 错误码(SDK 抛错统一携带 errorCode)
codeNotFound、revoked、expired、machineLimit、tooManyAttempts、signatureInvalid、apiSecretMissing、productNotEnabled、trialNotEnabled、trialAlreadyPurchased、trialAlreadyPurchased、machineCodeInvalid、orderAlreadyUsed、editionRequired、productNotFound、trialFirstRequired、alreadyOwned 等;网络/超时错误统一为 NETWORK_ERROR。
7. 包结构
ps-sdk/
node/ @mizhanchengxi/ps-license-sdk(ESM,零依赖)
python/ ps-license-sdk(py3,零依赖)
java/ com.powersoftware:sdk(Java 8+,JDK 自带 HTTP/加密)
三个包均提供:machineCode()、sign()、LicenseClient(上述 6 方法 + verifyCached + purchaseUrl)。