外观
Python 快速接入
本文提供完整的 Python 接入示例,包含签名计算、卡密登录和心跳保活。
环境要求
- Python >= 3.6
requests库
bash
pip install requests完整示例
python
import hashlib
import json
import time
import uuid
import requests
# ========== 配置 ==========
APP_KEY = "your_app_key"
APP_SECRET = "your_app_secret"
API_BASE = "http://api.nullverify.com/api/client/v1"
def make_sign(params: dict) -> str:
"""计算请求签名"""
# 按 key 字典序排列参数(不含 sign)
sorted_params = "&".join(
f"{k}={v}" for k, v in sorted(params.items()) if k != "sign"
)
# 拼接签名原文
raw = sorted_params + APP_SECRET
return hashlib.md5(raw.encode()).hexdigest()
def make_common_params() -> dict:
"""生成公共参数"""
return {
"app_key": APP_KEY,
"timestamp": str(int(time.time())),
"nonce": str(uuid.uuid4()),
}
def parse_response(resp: requests.Response) -> dict:
"""解析响应(自动处理加密响应)"""
content_type = resp.headers.get("Content-Type", "")
if "text/plain" in content_type:
# 加密响应:需先解密再解析
# 请根据应用配置的加密算法实现解密逻辑
# 参考 响应加密 文档
raise NotImplementedError("请实现解密逻辑,参考 /guide/response-encryption")
return resp.json()
def card_login(card: str, device_id: str) -> dict:
"""卡密登录"""
params = make_common_params()
params["card"] = card
params["device_id"] = device_id
params["sign"] = make_sign(params)
resp = requests.post(f"{API_BASE}/card/login", data=params)
return parse_response(resp)
def card_heartbeat(card: str, token: str) -> dict:
"""卡密心跳"""
params = make_common_params()
params["card"] = card
params["token"] = token
params["sign"] = make_sign(params)
resp = requests.post(f"{API_BASE}/card/heartbeat", data=params)
return parse_response(resp)
def card_logout(card: str, token: str) -> dict:
"""卡密退出"""
params = make_common_params()
params["card"] = card
params["token"] = token
params["sign"] = make_sign(params)
resp = requests.post(f"{API_BASE}/card/logout", data=params)
return parse_response(resp)
def verify_response_sign(resp: dict) -> bool:
"""验证响应签名"""
result = resp.get("result", {})
sorted_params = "&".join(
f"{k}={v}" for k, v in sorted(result.items())
)
raw = f"{resp['code']}{resp['message']}{sorted_params}{resp['nonce']}{APP_SECRET}"
expected = hashlib.md5(raw.encode()).hexdigest()
return expected == resp.get("sign", "")
# ========== 主流程 ==========
if __name__ == "__main__":
CARD = "your_card_no"
DEVICE_ID = "device_001"
# 1. 登录
result = card_login(CARD, DEVICE_ID)
if result["code"] != 0:
print(f"登录失败: {result['message']}")
exit(1)
token = result["result"]["token"]
hg = result["result"].get("hg", 30)
print(f"登录成功,到期时间: {result['result']['expires']}")
# 验证响应签名
if verify_response_sign(result):
print("响应签名验证通过")
# 2. 心跳保活
try:
while True:
time.sleep(hg)
hb = card_heartbeat(CARD, token)
if hb["code"] != 0:
print(f"心跳失败: {hb['message']}")
break
print(f"心跳正常,到期时间: {hb['result']['expires']}")
except KeyboardInterrupt:
# 3. 退出
card_logout(CARD, token)
print("已退出")关键说明
签名计算
python
# 签名公式: md5(sorted_params + secret)
raw = sorted_params + APP_SECRET
sign = hashlib.md5(raw.encode()).hexdigest()注意事项
- 参数值不要进行 URL encode
sign参数本身不参与签名计算timestamp使用秒级时间戳,不是毫秒
响应处理
启用响应加密后,HTTP 响应体为纯密文字符串,Content-Type 为 text/plain。客户端需根据 Content-Type 判断是否需要解密:
python
resp = requests.post(url, data=params)
if "text/plain" in resp.headers.get("Content-Type", ""):
# 纯密文,需先解密
plaintext = decrypt(resp.text, encrypt_key)
result = json.loads(plaintext)
else:
# 明文 JSON
result = resp.json()详细解密实现请参考 响应加密。
错误处理
建议对所有 API 调用进行错误码判断:
python
resp = card_login(card, device_id)
if resp["code"] == 10210:
print("卡密已过期,请续费")
elif resp["code"] == 10213:
print("超过多开上限,请关闭多余客户端")
elif resp["code"] != 0:
print(f"错误: {resp['message']}")完整错误码列表请参考 错误码对照表。