技术博客
银行卡归属地查询排错:外层 -1、body ret_code -1、remark 提示分别代表什么

银行卡归属地查询排错:外层 -1、body ret_code -1、remark 提示分别代表什么

作者: 万维易源
2026-09-15
银行卡归属地查询排错ret_code失败处理remark
# 银行卡归属地查询排错:外层 -1、body ret_code -1、remark 提示分别代表什么 > 接口:银行卡归属地查询(apiCode=30)· 接入点:`30-7` · 计费:按次计费,5 厘/次,查询失败不计费 · 返回格式:JSON · 适用人群:已接入、正在处理异常分支的开发者 · 阅读时间:约 7 分钟 > 最后实测核对:2026-09-15 一句话结论:银行卡归属地查询接口的「调用不成功」有三种不同的响应形态——参数缺失时外层 `showapi_res_code` 直接是 `-1` 且 `showapi_res_body` 为空对象,卡号未收录时外层为 `0`、内层 `ret_code` 为 `-1` 并带 `remark`,归属地未收录但 BIN 命中时内层 `ret_code` 为 `-1`、`area` 是占位串而银行其余字段照常返回。 ## 三种形态对照表 | 形态 | 触发条件 | `showapi_res_code` | `showapi_res_error` | `showapi_res_body` | `showapi_fee_num` | |------|---------|-------------------|--------------------|--------------------|-------------------| | A 参数缺失 | 不传 `cardNum` | `-1` | `must input cardNum field` | 空对象 `{}`,**没有 `ret_code`** | `0` | | B 卡号未收录 | 卡号无法识别到 BIN | `0` | `""` | `{"remark":"找不到此卡号信息","ret_code":-1}` | `0` | | C 归属地未收录、BIN 命中 | BIN 可识别但归属地库无记录 | `0` | `""` | `ret_code:-1`,`area` 为 `该卡归属地信息暂未收录 - `,银行其余字段照常返回 | `0` | 三种形态的 `showapi_fee_num` 都是 `0`。也就是说这三次调用都不计费,与接口文档「若归属地查询失败,则不收取费用」的表述一致。 ## 判定顺序:先外层,再内层 形态 A 下 `showapi_res_body` 是空对象,里面没有 `ret_code`。如果代码里直接写 `body["ret_code"]`,这一路会抛 `KeyError`。判定必须从外层开始。 一段可以直接放进项目的判定逻辑: ```python import requests APPKEY = "YOUR_APPKEY" API_URL = "https://route.showapi.com/30-7" def query_bank_card(card_num: str) -> dict: """返回统一结构:{"status": ..., "data": {...}}""" resp = requests.get(API_URL, params={"appKey": APPKEY, "cardNum": card_num}, timeout=30) resp.raise_for_status() data = resp.json() # 第一层:请求是否被受理。形态 A 在这里拦下。 if data.get("showapi_res_code") != 0: return { "status": "request_rejected", "detail": data.get("showapi_res_error", ""), "fee_num": data.get("showapi_fee_num", 0), } body = data.get("showapi_res_body") or {} # 第二层:本次查询是否拿到结果。形态 B、C 在这里拦下。 if str(body.get("ret_code")) != "0": # 有 remark 的是形态 B;没有 remark 但 area 是占位串的是形态 C。 if body.get("remark"): status = "card_not_found" elif body.get("area", "").startswith("该卡归属地信息暂未收录"): status = "area_not_found" else: status = "query_failed" return {"status": status, "detail": body.get("remark", ""), "fee_num": data.get("showapi_fee_num", 0), "raw": body} return {"status": "ok", "data": body, "fee_num": data.get("showapi_fee_num", 0)} ``` ## 形态 A:参数缺失 不传 `cardNum` 时的完整响应(2026-09-15 实测): ```json { "showapi_res_error": "must input cardNum field", "showapi_res_id": "6aa8e455fb638c2f695203f2", "showapi_res_code": -1, "showapi_fee_num": 0, "showapi_res_body": {} } ``` 识别特征有三个:外层 `showapi_res_code` 为 `-1`、`showapi_res_error` 是英文提示 `must input cardNum field`、`showapi_res_body` 是空对象。这条路径不必读 `ret_code`,因为根本没有这个键。 `cardNum` 是本接口唯一的必填业务参数,传之前判一次空就能避免这一类返回。 ## 形态 B:卡号未收录 传一个 BIN 完全无法识别的卡号(实测用 `cardNum=1234567890`): ```json { "showapi_res_error": "", "showapi_res_id": "6aa8e454fb638c2f6951c5fa", "showapi_res_code": 0, "showapi_fee_num": 0, "showapi_res_body": { "remark": "找不到此卡号信息", "ret_code": -1 } } ``` 外层是 `0`,说明请求已经被正常受理;内层 `ret_code` 为 `-1`,说明这次没查到。`remark` 给出了文字说明。 这一形态下 `showapi_res_body` 里只有 `remark` 和 `ret_code` 两个键,`area`、`bankName` 等字段都不出现。取值时统一带默认值,不要假设它们一定存在。 ## 形态 C:归属地未收录、BIN 命中 传一个 BIN 能命中、但归属地库没有记录的卡号(实测用 `cardNum=9999999999999999999&needBin=1`): ```json { "showapi_res_error": "", "showapi_res_id": "6aa8e445fb638c2f694c6fc8", "showapi_res_code": 0, "showapi_fee_num": 0, "showapi_res_body": { "logo": "http://static1.showapi.com/app2/banklogo/hxb.png", "cardNum": "9999999999999999999", "area": "该卡归属地信息暂未收录 - ", "cardType": "借记卡", "bankName": "华夏银行", "formatBankName": "华夏银行", "isLuhn": "0", "card_digits": "19", "brand": "华夏卡(银联卡)", "simpleCode": "HXB", "bin_digits": "", "url": "www.hxb.com.cn", "card_bin": "", "tel": "95528", "ret_code": -1 } } ``` 这一形态值得单独讲,因为它同时具备「失败」和「有数据」两个特征: - `ret_code` 是 `-1`,按 `ret_code` 判定属于失败; - 但 `bankName`、`formatBankName`、`brand`、`tel`、`url`、`logo`、`cardType`、`card_digits` 都有正常取值; - `area` 不是空字符串,而是占位串 `该卡归属地信息暂未收录 - `(帮助手册的 646 个取值里没有这一项); - 开了 `needBin=1`,但 `card_bin` 和 `bin_digits` 是空字符串,`isLuhn` 为 `0`。 如果你的业务只需要银行名和客服电话,这一形态的数据是可以用的;如果业务必须要归属地,那就按失败处理。判定时可以按 `area` 是否等于占位串来分流。 占位串的边界建议用前缀匹配,不要写全等:`body["area"].startswith("该卡归属地信息暂未收录")`。理由是占位串末尾带了一个分隔符,直接全等容易因为不可见字符对不上。 ## 计费与重试 三种失败形态的 `showapi_fee_num` 都是 `0`。按次计费的口径是「调用成功才计费」,所以失败返回不会消耗额度。 重试策略上有一点要区分:形态 A 属于请求构造问题,重试同样会失败,改完参数再发;形态 B、C 属于数据层面没收录,同参数重试结果不变,可以把卡号记进「待人工复核」队列,等数据更新后再说。 数据更新频率是每年不定期更新,没有固定的刷新时间点。 ## FAQ **Q1:为什么 `ret_code` 有时候读不到?** 因为参数缺失时返回的 `showapi_res_body` 是空对象 `{}`,里面没有 `ret_code`。先判外层 `showapi_res_code` 就能避开这种情况。 **Q2:`showapi_res_code` 和 `ret_code` 有什么区别?** `showapi_res_code` 在返回体最外层,判的是这次请求有没有被接口受理(比如参数是否齐全);`ret_code` 在 `showapi_res_body` 里,判的是这次业务查询有没有结果。两者要分开看。 **Q3:失败会不会扣费?** 不会。2026-09-15 实测三种失败形态的 `showapi_fee_num` 都是 `0`。接口文档的相应表述是「若归属地查询失败,则不收取费用」。 **Q4:`area` 返回 `该卡归属地信息暂未收录 - ` 算成功还是失败?** `ret_code` 为 `-1`,按接口定义属于失败;但同一响应里的银行名、客服电话、官网等字段是正常值,可以按业务需要取用。 **Q5:接口有具体的错误码列表吗?** 接口文档对 `ret_code` 的说明是「`0` 为成功,其他失败」,没有给出以外的编号枚举。实际排错靠的是三样东西:外层 `showapi_res_code`、内层 `ret_code`、以及 `remark` 与 `area` 的文字内容。帮助手册里的错误码/枚举文档覆盖的是 `formatBankName`、`area`、`brand` 三组业务取值,不是错误码。 ## 下一步阅读 - [银行卡归属地查询返回字段逐个说清](https://www.showapi.com/guides/bank-card-attribution-response-fields-30)——字段出现条件与空值处理 - [银行卡归属地查询的 cardNum 与 needBin 怎么传](https://www.showapi.com/guides/bank-card-attribution-params-guide-30)——避免形态 A 的参数写法 - [银行卡归属地查询按次计费下怎么省调用](https://www.showapi.com/guides/bank-card-attribution-cache-cost-30)——失败结果怎么缓存才不浪费额度 - **本系列共 10 篇**:查看[银行卡归属地查询指南总目录](https://www.showapi.com/guides/bank-card-attribution-guides-30)