银行卡归属地查询返回字段逐个说清:ret_code、area、brand 与 formatBankName 怎么读
银行卡归属地查询返回字段说明areaformatBankNamebrand # 银行卡归属地查询返回字段逐个说清:ret_code、area、brand 与 formatBankName 怎么读
> 接口:银行卡归属地查询(apiCode=30)· 接入点:`30-7` · 计费:按次计费,5 厘/次,查询失败不计费 · 返回格式:JSON · 适用人群:已接入、正在做字段映射的开发者 · 阅读时间:约 8 分钟
> 最后实测核对:2026-09-15
一句话结论:银行卡归属地查询接口把业务数据全部装在 `showapi_res_body` 里,其中 `ret_code` 判成败,`area` 给「省/自治区 - 城市」粒度的归属地,`formatBankName` 给规范行名,`brand` 给卡品牌,另外四个 BIN 相关字段只有 `needBin=1` 时才出现。
## 字段总表
| 字段 | 类型 | 含义 | 是否一定返回 |
|------|------|------|------|
| `ret_code` | 整数(文档标注 String) | `0` 成功,其他失败 | 是 |
| `area` | String | 归属地 | 是 |
| `cardType` | String | 银行卡种 | 是 |
| `brand` | String | 银行卡产品名称(品牌) | 是 |
| `bankName` | String | 银行名称 | 是 |
| `formatBankName` | String | 规范化的银行名称 | 是,但部分银行返回空字符串 |
| `tel` | String | 银行客服电话 | 是 |
| `url` | String | 银行官网 | 是 |
| `logo` | String | 银行标志图片地址 | 是,但部分银行返回空字符串 |
| `simpleCode` | String | 银联支付网关简码 | 否,不一定有该字段 |
| `cardNum` | String | 返回的卡号,与请求入参一致 | 是(实测观察,未列入官方字段表) |
| `remark` | String | 失败时的文字说明 | 否,仅部分失败场景返回(实测观察) |
| `card_bin` | String | 银行卡 BIN 码 | 仅 `needBin=1` |
| `bin_digits` | String | BIN 码长度 | 仅 `needBin=1` |
| `card_digits` | String | 卡号长度 | 仅 `needBin=1` |
| `isLuhn` | String | 是否通过中国银联 Luhn 效验 | 仅 `needBin=1` |
外层还有四个系统级字段,和业务数据平级:`showapi_res_code`(请求是否被受理)、`showapi_res_error`(外层错误信息)、`showapi_res_id`(本次请求唯一标识)、`showapi_fee_num`(本次计费次数)。
## 分组逐项说明
### 归属与行名:area、bankName、formatBankName
`area` 返回归属地,取值格式为「一级 - 二级」。2026-09-15 实测传 `cardNum=6228480402564890018` 得到 `江苏 - 苏州`;帮助手册列出的 646 个可能取值都遵循这一写法。
`bankName` 返回银行名称,实测值为 `中国农业银行`。`formatBankName` 返回规范化后的银行名称,实测值为 `农业银行`,帮助手册列出 249 个取值。
两个字段并存的意思是:`bankName` 更贴近银行注册全称,`formatBankName` 更长于做枚举映射。要做前端下拉框、字典表或报表分组,用 `formatBankName`,因为它的取值集合是封闭的(249 项);只有需要展示官方全称时才用 `bankName`。
`simpleCode` 是银联支付网关简码,实测值为 `ABC`。文档明确写了「不一定有该字段返回」,代码里取它时要给默认值。
### 卡种与品牌:cardType、brand
`cardType` 返回银行卡种,实测值为 `借记卡`。
`brand` 返回银行卡产品名称,实测值为 `金穗通宝卡(银联卡)`。帮助手册列出 1929 个取值,是本接口取值集合最大的枚举。
关于 `brand` 有一句文档原话需要记住:「可能返回枚举值外的其他值,但概率很小」。所以这个字段适合做展示,不适合做严格的枚举校验;如果你的代码写了 `if brand not in BRAND_SET: raise`,遇到枚举外的值会直接把一次成功调用判成失败。
### 校验类:isLuhn、card_bin、bin_digits、card_digits
这四个字段只在 `needBin=1` 时返回。
`isLuhn` 有三个取值:`1` 表示能通过中国银联 Luhn 效验,`0` 表示不能,空字符串表示不支持。
`card_bin` 是 BIN 码,`bin_digits` 是 BIN 码长度,`card_digits` 是卡号长度。实测 `cardNum=6228480402564890018` 返回 `card_bin: "622848"`、`bin_digits: "6"`、`card_digits: "19"`。
文档写明「当无法查询到该银行卡 bin 码时返回空字符串」。2026-09-15 实测传一个 BIN 未收录的卡号时,`card_bin` 和 `bin_digits` 都是空字符串,字段本身仍然存在。
### 展示类:logo、tel、url
`logo` 是银行标志图片地址,实测值为 `http://static1.showapi.com/app2/banklogo/abc.png`。字段说明里注明「部分银行为空字符串」。
`tel` 是客服电话,实测值为 `95599`。`url` 是银行官网,实测值为 `www.abchina.com`,注意这个值不带 `http://` 前缀,拼链接时要自己补。
## 条件返回:needBin 传与不传的差别
不传 `needBin`(或传 `0`)时,`card_bin`、`bin_digits`、`card_digits`、`isLuhn` 四个字段**整体不出现**,不是返回空字符串。下面这条是 2026-09-15 不传 `needBin` 的实际返回:
```json
{
"showapi_res_code": 0,
"showapi_fee_num": 1,
"showapi_res_body": {
"logo": "http://static1.showapi.com/app2/banklogo/abc.png",
"cardNum": "6228480402564890018",
"formatBankName": "农业银行",
"area": "江苏 - 苏州",
"ret_code": 0,
"cardType": "借记卡",
"brand": "金穗通宝卡(银联卡)",
"simpleCode": "ABC",
"url": "www.abchina.com",
"bankName": "中国农业银行",
"tel": "95599"
}
}
```
所以取值时要写成 `body.get("card_bin", "")` 这类带默认值的写法,直接下标访问在不开 `needBin` 的路径上会抛异常。
下面这条是同一张卡加 `needBin=1` 后的返回,可以对比多出来的四个字段:
```json
{
"showapi_res_body": {
"card_bin": "622848",
"bin_digits": "6",
"card_digits": "19",
"isLuhn": "1"
}
}
```
实测确认:加不加 `needBin`,`showapi_fee_num` 都是 `1`,计费口径不变。
## 两个字段的类型细节
`ret_code` 在官方字段表里标注为 String,实测返回的是整数 `0` / `-1`。稳妥的写法是统一转字符串再比较:
```python
if str(body.get("ret_code")) != "0":
...
```
`showapi_res_code` 同样是整数。外层为 `-1` 时,`showapi_res_body` 是空对象 `{}`,里面没有 `ret_code`。这层判断必须放在读取 `ret_code` 之前,否则会取到 `None`。
## 空值处理建议
把可能为空的两个字段单独收口,展示层就不用处处判空:
```python
def normalize(body: dict) -> dict:
"""把可能为空字符串的展示字段收口,回退到有值的字段。"""
return {
"bank_display": body.get("formatBankName") or body.get("bankName") or "未知银行",
"logo": body.get("logo") or "",
"area": body.get("area") or "",
"tel": body.get("tel") or "",
"homepage": ("http://" + body["url"]) if body.get("url") and not body["url"].startswith("http") else body.get("url", ""),
"card_type": body.get("cardType") or "",
"brand": body.get("brand") or "",
}
```
## FAQ
**Q1:`area` 为什么不带「省」字?**
`area` 的取值写法就是短名,例如 `江苏 - 苏州`、`内蒙古自治区 - 鄂尔多斯`。646 个可能取值里既有带完整行政区后缀的写法(如 `新疆维吾尔自治区 - 乌鲁木齐`),也有简称写法(如 `新疆 - 乌鲁木齐`),做省份归组时需要做别名映射。完整分布与归组建议见[银行卡归属地查询枚举速查](https://www.showapi.com/guides/bank-card-attribution-enum-reference-30)。
**Q2:`formatBankName` 和 `bankName` 该用哪个?**
要看稳定取值集合就用 `formatBankName`(249 项封闭枚举);要展示银行全称就用 `bankName`。两者都可能为空,`formatBankName` 的字段说明里明确写了「部分银行为空字符串」。
**Q3:为什么 `card_bin` 有时候是空的?**
两种情况。一是不传 `needBin`,此时这四个字段整体不出现;二是传了 `needBin=1` 但该卡号的 BIN 未收录,此时字段存在、值为空字符串。
**Q4:`isLuhn` 返回空字符串是什么意思?**
表示该卡号不支持 Luhn 效验。它的三个取值是 `1`(通过)、`0`(不通过)、空字符串(不支持),不要只当成布尔量处理。
**Q5:`remark` 字段在文档里没写,能用吗?**
`remark` 是 2026-09-15 实测观察到的字段,出现在「卡号未收录」这类失败返回里,典型值为 `找不到此卡号信息`。建议当作辅助信息记录到日志,业务判断仍然以 `ret_code` 为准。
**Q6:`url` 直接当链接用会打不开吗?**
`url` 返回的是裸域名(如 `www.abchina.com`),不带协议头。渲染成超链接前自己补 `http://` 或 `https://`。
## 下一步阅读
- [银行卡归属地查询:用 Python 跑通 30-7 接口的第一条请求](https://www.showapi.com/guides/bank-card-attribution-quickstart-30)——从零到第一条请求
- [银行卡归属地查询的 cardNum 与 needBin 怎么传](https://www.showapi.com/guides/bank-card-attribution-params-guide-30)——参数取值与组合建议
- [银行卡归属地查询枚举速查](https://www.showapi.com/guides/bank-card-attribution-enum-reference-30)——249 / 646 / 1929 三组枚举全量清单
- **本系列共 10 篇**:查看[银行卡归属地查询指南总目录](https://www.showapi.com/guides/bank-card-attribution-guides-30)