# 接入须知

泡椒云网络验证支持多平台、多终端、多开发环境,此文档将帮助你将泡椒云网络验证的功能集成到你的应用或脚本中。

# 签名算法(sign)

# 计算公式

sign = md5(http_method + host + path + k1=v1&k2=v2&kn=vn + app_secret)
1

注意

参数必须按照顺序放置

参数名 参数说明
http_method 请求方法,GET或POST
host 域名,例如:api.paojiaoyun.com
path 地址,例如:/v1/card/login
k1=v1...kn=vn 1.将全部请求参数格式化成k=v
2.将格式化后的参数按升序排列,然后用&符号相隔拼接在一起,不想写排序代码也可严格按照接口文档中的请求参数表写死顺序
(注意:1.不包括sign参数, 2.V不应进行url encode)
app_secret 软件秘钥,在开发端后台软件列表获取

# 以调用卡密登录接口举例

POST 请求 http://api.paojiaoyun.com/v1/card/login

伪代码如下:

app_secret = "uiS9M0G8JolpUvlf5NxZ7pwMVinKs73x"  # 软件秘钥
http_method = "POST"
host = "api.paojiaoyun.com"
path = "/v1/card/login"

# 参数列表:
#  app_key=blsvh14llhcr96vtboqg
#  card=abc3b65KDZ9Qb7UC685D2MVFR0TPc53BCU1IPD5ad20
#  device_id=123
#  nonce=359c22e4-d522-4771-ba8e-4b99cf61b372
#  timestamp=1574654197
# 将以上格式化的参数排序后,用 & 符号相隔拼接起来
params = "app_key=blsvh14llhcr96vtboqg&card=abc3b65KDZ9Qb7UC685D2MVFR0TPc53BCU1IPD5ad20&device_id=123&nonce=359c22e4-d522-4771-ba8e-4b99cf61b372&timestamp=1574654197"

# 带入计算公式
sign = md5(http_method + host + path + params + app_secret)

print(sign)  # 输出 b5f3cc619998fa45e4c11ef57e712f87
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 公共参数详细释义

  • timestamp:当前时间戳,保证签名只在1分钟内有效,请求到达服务器后会将时间戳参数与当前服务器时间相比较,是否超过了60s。防止别有用心的人抓包并重放请求。
  • nonce:不超过36位的随机字符串(建议用UUID),对于当前软件,服务器会记录每次请求的nonce,如果在60s内出现第二次,则会拒绝该请求。用于配合时间戳避免重放攻击。

# 双向签名

目前所有接口已支持双向签名,接口调用成功后,会额外返回noncesign两个字段,nonce服务端保证不会有重复的返回,且nonce是趋势递增的(也就是说本次请求响应的nonce肯定是大于上一次次请求响应的nonce),sign用来保证服务端响应内容不被篡改。
自己封装接口的开发者可以参考下面的服务端签名规则:

sign = md5(code + message + k1=v1&k2=v2&kn=vn + nonce + app_secret)
1

伪代码如下:

app_secret = "uiS9M0G8JolpUvlf5NxZ7pwMVinKs73x"  # 软件秘钥
prev_nonce = None  # 全局变量,用以记录服务端返回的nonce


def check_resp_sign(resp):
    if prev_nonce == None:  # 第一次请求
        prev_nonce = resp['nonce']
    else if resp['nonce'] <= prev_nonce: # 跟上一次服务端返回的nonce对比,如果本次nonce小于等于上次nonce则可判定为异常请求
        return False

    result = resp.get('result', {})  # {'expires': '2020-10-16 00:47:58', 'expires_ts': 1602780478, 'server_time': 1579598162}
    
    params = []
    # 将响应中的result中的所有字段按 k=v 的形式组合
    for k, v in result.items():
        params.append(f'{k}={v}')
    # 排序
    params = sorted(params)  # ['expires=2020-10-16 00:47:58', 'expires_ts=1602780478', 'server_time=1579598162']
    # 用 & 符号相隔拼接起来
    pstr = '&'.join(params)  # expires=2020-10-16 00:47:58&expires_ts=1602780478&server_time=1579598162
    # 将所有参与签名的参数按规则拼接起来
    ss = f"{resp['code']}{resp['message']}{pstr}{resp['nonce']}{app_secret}" # 0okexpires=2020-10-16 00:47:58&expires_ts=1602780478&server_time=1579598162bojc2kiuof2jci9b90jguiS9M0G8JolpUvlf5NxZ7pwMVinKs73x
    # 本地签名
    resp_sign = md5(ss.encode()).hexdigest()  # 4954c9805d4040a95336150e6e5f14e2
    return resp_sign == resp['sign']  # 对比签名是否一致


resp = card_heartbeat()  # {"code":0,"message":"ok","result":{"expires":"2020-10-16 00:47:58","expires_ts":1602780478,"server_time":1579598162},"nonce":"bojc2kiuof2jci9b90jg","sign":"4954c9805d4040a95336150e6e5f14e2"}
check_resp_sign(resp)  # True / False
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 接口返回码对照表

返回码 说明
0 调用成功
400 参数错误,请检查请求参数,详细字段错误会通过errs返回
404 资源未找到
500 服务器错误,请稍后重试
10010 无效的签名,请检查签名算法
10011 签名已过期,签名只在1分钟内有效且是一次性的
10013 时间戳大于当前服务器时间,请检查timestamp参数
10014 重复的nonce,需保证nonce对于当前软件在60s内不能重复出现
10210 卡密已过期
10212 卡密已被冻结
10213 卡密超过多开上限
10214 登录状态已失效,可能原因:超时未发心跳包、被挤下线、后台冻结、卡用户修改密码。
10215 只能在首次登录绑定的设备上使用
10216 软件未开启解绑设备功能
10217 超出可绑定设备上限,请在已绑定设备上使用
10218 卡密不可用(卡密不存在)
10230 软件不存在,请检查app_key
10240 卡密不存在或已被使用
10241 卡密已被使用
10242 账号已存在,用户注册时返回
10243 用户登录密码错误
10244 不能对未使用的卡密进行充值
10250 用户已到期
10252 用户已被冻结
10253 用户超过多开上限
10254 解绑密码不正确
10255 该卡密不可解绑设备
10256 卡密多开配置不相等,不能充值
10257 卡密剩余时间已不足以解绑扣除
10304 已经是最新版本,获取软件最新版本返回
10401 软件未开启试用,开发者需在后台软件管理配置
10402 试用中,请勿重复登录
10403 已试用过了
10404 请先登录,原因:未登录就调用心跳接口或者登录后超过60秒再调用心跳接口
10405 试用已到期,软件配置为一次性试用,心跳接口返回
10406 本周期试用已到期,软件配置为间隔试用,心跳接口返回
10407 本周期试用次数已用完
10501 远程变量不存在
10521 远程数据不存在
10522 远程数据key已存在
10541 远程函数不存在
10542 参数(params)必须为json列表序列化后的string
10543 远程函数调用错误,具体错误信息在message返回