接口返回怎么解析:核验类与短信类两套返回结构的差异与统一封装
核验类返回 data.code(字符串)+ status 两层结构,短信类返回扁平的 code(数字)且无 status。同一个 1000/1002 在两套体系里含义完全不同。附统一归一化封装代码与 7 项上线检查单。
一句话结论
诺正通的接口分两套返回体系,报文结构与码表都不通用:核验类(身份证/手机号/银行卡)返回 {"data":{"code":"1000","message":"一致"},"status":"2001"}——code 是字符串、外面套 data、多一层 status;短信类(/sms.php)返回 {"code":1000,"message":"发送成功"}——code 是数字、没有 data / status 包装。用一套统一封装去接这两类接口,最常见的后果是短信侧永远判不出成功。
更反直觉的是核验类内部:status 与 data.code 正交。status=2002(第三方服务器异常)时 data.code 仍可能是 1000——只判 code 会把一次上游异常当成核验通过。
两套返回结构的字段对照
| 维度 | 核验类(身份证/手机号/银行卡) | 短信类(/sms.php) |
|---|
| 报文顶层 | data + status 两个字段 | 只有 code + message,扁平 |
|---|---|---|
| 码字段路径 | data.code | code |
| code 类型 | 字符串 "1000" | 数字 1000 |
| message 字段路径 | data.message | message |
| status 字段 | 有(2001/2002/2003/2004/2005) | 没有 |
| 码表 | 21 个码(1000-1005 + 1101-1110 + 2001-2005) | 10 个码(1000-1009),与核验类不通用 |
| 签名规则 | 含 tm,与签名时用同一个值 | 不含 tm |
官方接口文档里两个体系的原始返回示例(核验类取 /v2/id-server,短信取 /sms.php):
核验类
{ "data": { "code": "1000", "message": "一致" }, "status": "2001" }
短信类 { "code": 1000, "message": "发送成功" }
一套封装接两类接口,会在哪一步静默出错?
这是最容易埋雷的地方:接口能调通、能返回 200,但判定逻辑是错的。同一组示例值本地实测(判据来自官方文档原文):
| 写法 | 短信接口 | 核验接口 | 后果 |
|---|
res.code == 1000 | True | False | 核验侧永远判不成功,被误当成调用失败 |
|---|---|---|---|
res.code == "1000" | False | True | 短信侧永远判不成功 |
res.status == "2001" | False | True | 短信侧 status 恒为 None,等于没做服务态校验 |
res.data.code | 抛异常(无 data) | True | 短信侧直接取不到值 |
注意前两行是互斥的:一个写法只对一套体系成立。核验侧的 data.code 是字符串 "1000",短信侧的 code 是数字 1000,在 JavaScript 这类弱类型语言里 == 会让二者意外相等,换到 Python 则是 True/False 反过来——同一种写法换语言就换行为,这类问题在联调时最容易被误判成"接口不稳定"。
实测输出:
核验 data.code 值='1000' 类型=str
短信 code 值=1000 类型=int 写法A 'res.code == 1000' 短信接口 -> True | 核验接口 -> False 写法B 'res.code == "1000"' 短信接口 -> False | 核验接口 -> True 短信侧 res.get("status") 恒为 None -> True
status 和 data.code 正交:只判 code 会漏掉上游异常
核验类有两层状态,它们互不蕴含:
data.code = 业务结果(核验本身成没成、结果一不一致)status = 服务状态(平台到上游这一段顺不顺利)官方口径是二者正交:status=2002(第三方服务器异常)时,即使 code=1000 也可能需要复核。常见于上游超时、第三方服务异常这类场景。
status 的 5 个取值:
| status | 含义 | 处置 |
|---|
| 2001 | 正常服务 | 正常读 data.code |
|---|---|---|
| 2002 | 第三方服务器异常 | 稍后重试;即使 code=1000 也需复核 |
| 2003 | 服务器维护 | 按维护公告处理 |
| 2004 | 帐号余额不足 | 充值后重试 |
| 2005 | 参数异常 | 核对参数 |
这组组合本地复算(用官方文档给出的正交示例):
data.code==1000(判定'核验已完成')-> True
status!=2001(判定'上游异常') -> True => 只判 code 会漏掉上游异常,需要两层一起看
正确的判定顺序是先看 status,再看 data.code:只有 status == 2001 时,data.code 的结果才可直接采信;status != 2001 时无论 code 是什么,都按服务侧异常处理。这一点写进客户端封装里,比事后从错误日志里反推省事得多。
同一个 1002,在两套体系里是两回事
号码重叠最容易踩坑。核验类 1002 是"库中无此号 / 系统无记录 / 无法认证"(不计费);短信类 1002 是签名错误——要去核对 mall_id + phone + code + appkey 的拼接顺序,而不是去查号码是否存在。
同样的重叠还有 1000:核验类 1000 是"姓名与证件号一致",短信类 1000 是"发送成功"。1003/1004/1005 在核验类里是手机号三要素详版的"哪一项不一致"(均计费),在短信类里分别是"可发送条数为 0 / 短信内容为空 / 短信内容存在敏感词"。
结论:两套码表不能共用一张映射表。 哪怕表结构看着一样,同一个数字在两套体系里的业务含义与处置动作完全不同。落到工程上,建议按接口类型分两张表,不要试图合并。
哪些码计费,哪些不计费
计费口径按官方码表,核验类与短信类分开看:
| 码 | 核验类 | 短信类 |
|---|
| 1000 | 一致,计费 | 发送成功,计费 |
|---|---|---|
| 1001 | 不一致,计费 | 发送失败,不计费 |
| 1002 | 库中无此号,不计费 | 签名错误,不计费 |
| 1003 | 身份证/银行卡:第三方异常不计费;手机三要素详版:计费 | 可发送条数为 0,不计费 |
| 1004 | 仅手机三要素详版,计费 | 短信内容为空,不计费 |
| 1005 | 仅手机三要素详版,计费 | 敏感词,不计费 |
| 1101-1110 | 一律不计费(请求级失败) | — |
| 1006-1009 | — | 一律不计费 |
关键点:1001(不一致)也是计费的——核验已经完成、只是结果为否,钱照扣。所以"1001 不要重试"不是省钱建议,是避免重复计费的动作。
统一封装的正确写法
把两套结构归一化到一个内部模型,让业务代码只面对一种形态:
def normalize(resp):
"""把两套返回结构归一化成 (code:int, message:str, status:str|None)""" if "data" in resp and isinstance(resp.get("data"), dict): d = resp["data"] return int(d.get("code")), str(d.get("message", "")), str(resp.get("status", "")) if "code" in resp: return int(resp.get("code")), str(resp.get("message", "")), None raise ValueError("无法识别的返回结构")
def judge(resp): c, m, s = normalize(resp) if s is not None and s != "2001": return "需复核:status=%s 表示服务侧异常,即使 code=%s 也不能直接采信" % (s, c) if c == 1000: return "成功" if c == 1001: return "业务不一致(已计费,不要重试)" return "请求级失败(不计费):%s" % m
本地跑通三组输入的输出:
核验类一致 -> 成功
短信类成功 -> 成功 正交异常 -> 需复核:status=2002 表示服务侧异常,即使 code=1000 也不能直接采信
三个要点:
None 要和 2002 区分开。上线检查单
data.code(字符串),短信类读 code(数字),比较前统一转型status == 2001 再读 data.code,不要只看 codestatus 缺失(短信侧)与 status != 2001 走不同分支tm;核验类的 tm 要与签名时用同一个值相关阅读
关于诺正通
身份证、手机号、银行卡核验接口与短信验证码接口按量计费、面议,含 100 次免费试用用于联调,具体价格向商务询价。