接口概述
一句话说明
bankcard.ocr 把「绑卡录卡号」拆成一次 HTTP 请求。 相比传统固定模板 OCR 只能认标准摆拍、遇到凸字反光就串行, 本接口不依赖固定模板与定位框,能理解卡面版式与上下文语义, 对轻微模糊、倾斜、反光、非标准拍摄角度的卡片同样有效; 卡号会逐位核对,拿不准就返回空字符串而不是猜测值, 并给出置信度与质量提示,方便业务侧决定「直接采信 / 转人工复核」。
| 请求地址 | https://api.xujian.tech/openapi/bankcard/ocr |
|---|---|
| 请求方式 | POST;支持 multipart/form-data(file 字段)、
application/json(image base64)、远程图片地址(imageUrl)三种传法 |
| 鉴权方式 | 请求头 X-API-Key(不做签名与加密) |
| 接口编码 | bankcard.ocr |
| 收费类型 | PER_CALL(按次计费,0.0500 元/次) |
| 计费特殊规则 | 调用即计费:图片通过校验进入识别流程后,无论识别成功还是失败 (模糊无法识别 / 不是银行卡 / 上游超时)都计一次费用; 只有未传图片、超 10MB、格式不支持这类参数级错误返回失败且不扣费 |
| 响应格式 | JSON,Content-Type: application/json;charset=UTF-8 |
| 是否需要授权 | 否(无需授权,API Key 有效且余额充足即可调用) |
| 图片限制 | 单张 ≤ 10MB,支持 JPG / PNG / WEBP / BMP / GIF |
| 典型耗时 | 通常 2 ~ 8 秒(图片越大越慢) |
| 典型用途 | 支付绑卡、钱包开户、商户进件与结算账户录入、报销打款账户采集、 代发工资卡号采集、企业付款与供应商账户维护 |
快速开始
curl -s -X POST "https://api.xujian.tech/openapi/bankcard/ocr" \
-H "X-API-Key: 你的APIKey" \
-F "file=@/path/to/bankcard.jpg"
curl -s -X POST "https://api.xujian.tech/openapi/bankcard/ocr" \
-H "Content-Type: application/json" \
-H "X-API-Key: 你的APIKey" \
-d '{"image": "图片的 base64 内容(可带 data:image/jpeg;base64, 前缀)"}'
curl -s -X POST "https://api.xujian.tech/openapi/bankcard/ocr" \
-H "Content-Type: application/json" \
-H "X-API-Key: 你的APIKey" \
-d '{"imageUrl": "https://example.com/bankcard.jpg"}'
拍得更准的三条建议
- 正拍 + 铺满画面:卡片占画面 70% 以上,四角完整入镜;
- 避免反光与手指遮挡:凸字卡号最怕斜向反光,手指不要压在卡号上;
- 只拍卡号面:只上传背面时
cardNo通常为空, 建议前端引导用户拍有卡号的一面。
请求参数
| 参数名 | 是否必填 | 说明 |
|---|---|---|
X-API-Key | 是 | 开发者 API Key |
Content-Type | 视传法而定 | 文件用 multipart/form-data;base64 / URL 用 application/json |
业务参数(三选一,优先级:file > image > imageUrl)
| 字段 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
file | File | 三选一 | multipart/form-data 上传的图片文件,≤ 10MB |
image | String | 三选一 | 图片 base64 内容,可带 data:image/jpeg;base64, 前缀;
JSON 请求体或表单字段均可 |
imageUrl | String | 三选一 | 图片远程地址(http / https),由服务端下载,≤ 10MB、下载超时 15 秒 |
响应参数
| 字段 | 类型 | 示例 | 说明 |
|---|---|---|---|
side | String | front | 版面:front 卡号面 / back 背面 /
unknown 无法判断 |
sideText | String | 卡号面 | 版面中文名 |
cardNo | String | 6222021234567890123 | 银行卡号,只保留数字(去掉卡面印刷的空格与横线) |
binNo | String | 622202 | 卡号前 6 位 BIN,由服务端按卡号推导,不依赖模型输出; 卡号不足 6 位时为空字符串 |
bankName | String | 中国工商银行 | 发卡行名称 |
cardType | String | 借记卡 | 卡类型:借记卡 / 信用卡 / 准贷记卡;看不出时为空字符串 |
cardScheme | String | 银联 | 卡组织:银联 / VISA / MasterCard / JCB / American Express |
cardLevel | String | 金卡 | 卡等级:普卡 / 金卡 / 白金卡 / 钻石卡;看不出时为空字符串 |
cardName | String | 牡丹灵通卡 | 卡名称 |
holderName | String | ZHANG SAN | 持卡人姓名(卡面印有中文姓名或拼音时返回,拼音保持大写) |
expiryDate | String | 08/29 | 有效期,格式 MM/YY(信用卡卡面;借记卡没有时为空字符串) |
confidence | Number | 97.00 | 整体识别置信度,0 ~ 100,保留 2 位小数; 未能评估时为 null,接入方请先判空 |
qualityNote | String | 图片清晰 | 图片质量提示(如「卡号被手指遮挡,建议重新拍摄」) |
apiCode | String | bankcard.ocr | 接口编码 |
apiName | String | 银行卡识别 | 接口名称 |
chargeType | String | PER_CALL | 计费类型 |
balance | BigDecimal | 调用完成后(已扣费)的账户余额(元) | |
costMs | Long | 3100 | 本次调用耗时(毫秒) |
字段口径约定
- 识别不到一律返回空字符串
""(不是 null),可直接落库; 唯一例外是confidence,评估不出时为null; cardNo只保留数字,不带空格;binNo由服务端推导, 可用于判定发卡行与卡种;- 接口不做卡号校验位(Luhn)推断:识别出的卡号是卡面原文, 业务侧如需强校验请自行加 Luhn 或走支付通道鉴权;
- 只上传背面时,卡号相关字段通常为空字符串。
响应示例
成功(code = 0)
{
"code": 0,
"msg": "success",
"data": {
"side": "front",
"sideText": "卡号面",
"cardNo": "6222021234567890123",
"binNo": "622202",
"bankName": "中国工商银行",
"cardType": "借记卡",
"cardScheme": "银联",
"cardLevel": "",
"cardName": "牡丹灵通卡",
"holderName": "",
"expiryDate": "",
"confidence": 97.00,
"qualityNote": "图片清晰",
"apiCode": "bankcard.ocr",
"apiName": "银行卡识别",
"chargeType": "PER_CALL",
"balance": 99.9500,
"costMs": 3100
}
}
失败(不是银行卡 / 卡号认不出)—— 已扣费
{
"code": 500,
"msg": "未能识别出银行卡上的卡号,请上传更清晰、无遮挡的银行卡照片(本次调用已计费)",
"data": null
}
失败(参数级错误)—— 不收费
{
"code": 500,
"msg": "请上传图片:multipart 的 file 字段、JSON 的 image(base64)、imageUrl(图片地址)三选一",
"data": null
}
错误码与常见失败原因
| code | msg(示例) | 是否扣费 | 处理建议 |
|---|---|---|---|
| 0 | success | 扣费 | 识别成功 |
| 500 | 缺少请求头 X-API-Key | 否 | 补充 X-API-Key |
| 500 | API Key 无效 / API Key 已停用 | 否 | 检查 Key 或重新启用 |
| 500 | 客户不存在或已停用 | 否 | 联系平台(可联系微信xujian_cq) |
| 500 | 接口不存在或已停用 | 否 | 接口下线维护中,稍后再试 |
| 500 | 余额不足,请先充值。可联系微信xujian_cq | 否 | 充值后重试 |
| 500 | 请上传图片:multipart 的 file 字段、JSON 的 image(base64)、imageUrl(图片地址)三选一 | 否 | 补传图片 |
| 500 | 图片大小不能超过 10MB | 否 | 压缩后再传(建议长边 2000px 以内) |
| 500 | 不支持的图片格式,仅支持 JPG / PNG / WEBP / BMP / GIF | 否 | 转换格式后重试 |
| 500 | image 不是合法的 base64 内容 | 否 | 检查 base64 编码(不要传 URL 编码后的内容) |
| 500 | 图片地址下载失败或超时 | 否 | 换用可公网直连的地址,或改传文件 / base64 |
| 500 | 未能识别出银行卡信息……(本次调用已计费) | 是 | 确认上传的是银行卡而非身份证 / 会员卡 / 名片;重新正拍后重试 |
| 500 | 未能识别出银行卡上的卡号……(本次调用已计费) | 是 | 卡号被反光 / 遮挡,重新拍摄后重试 |
| 500 | 银行卡识别失败(请求视觉模型超时或网络异常)(本次调用已计费) | 是 | 上游抖动,稍后重试;连续失败请联系平台 |
| 500 | 银行卡识别服务未启用或未配置,本次调用不计费 | 否 | 平台侧配置问题,联系平台处理 |
多语言代码示例
Java(Hutool)
// 方式一:multipart 上传文件
String body = HttpRequest.post("https://api.xujian.tech/openapi/bankcard/ocr")
.header("X-API-Key", apiKey)
.form("file", new File("/path/to/bankcard.jpg"))
.timeout(60000)
.execute()
.body();
// 方式二:JSON + base64
JSONObject req = JSONUtil.createObj().set("image", Base64.encode(new File("/path/to/bankcard.jpg")));
String body2 = HttpRequest.post("https://api.xujian.tech/openapi/bankcard/ocr")
.header("Content-Type", "application/json")
.header("X-API-Key", apiKey)
.timeout(60000)
.body(req.toString())
.execute()
.body();
JSONObject json = JSONUtil.parseObj(body);
if (json.getInt("code") != 0) {
// 注意:识别失败同样已扣费,不要无脑重试
System.out.println("识别失败:" + json.getStr("msg"));
return;
}
JSONObject data = json.getJSONObject("data");
System.out.printf("%s|BIN %s|%s|%s|置信度 %s%n",
data.getStr("cardNo"), data.getStr("binNo"), data.getStr("bankName"),
data.getStr("cardType"), data.getBigDecimal("confidence"));
Python
import base64
import requests
def recognize(api_key: str, path: str):
"""识别银行卡;识别失败也会扣费,不要直接重试用同一张图"""
with open(path, "rb") as f:
b64 = base64.b64encode(f.read()).decode()
resp = requests.post(
"https://api.xujian.tech/openapi/bankcard/ocr",
json={"image": b64},
headers={"Content-Type": "application/json", "X-API-Key": api_key},
timeout=60,
)
result = resp.json()
if result.get("code") != 0:
print("识别失败(已扣费):", result.get("msg"))
return None
return result["data"]
def luhn_ok(card_no: str) -> bool:
"""卡号校验位自检(可选,接口不做 Luhn 推断)"""
digits = [int(c) for c in card_no][::-1]
total = sum(digits[0::2]) + sum(sum(divmod(d * 2, 10)) for d in digits[1::2])
return total % 10 == 0
if __name__ == "__main__":
data = recognize("你的APIKey", "bankcard.jpg")
if data:
print(data["cardNo"], data["bankName"], luhn_ok(data["cardNo"]))
JavaScript(浏览器 / Node 18+)
// 浏览器端:直接用 FormData 上传用户选择的图片
const form = new FormData();
form.append("file", fileInput.files[0]);
const resp = await fetch("https://api.xujian.tech/openapi/bankcard/ocr", {
method: "POST",
headers: { "X-API-Key": API_KEY }, // 不要手动设置 Content-Type,浏览器会自动带上 boundary
body: form,
});
const { code, msg, data } = await resp.json();
if (code === 0) {
console.log(`${data.cardNo}|${data.bankName}|${data.cardType}|置信度 ${data.confidence}`);
} else {
console.warn("识别失败(可能已扣费):", msg);
}
常见问题(FAQ)
bankcard.ocr 接口怎么收费?识别失败扣费吗?
bankcard.ocr 的收费类型为 PER_CALL(按次计费),当前单价 0.0500 元/次,调用前校验余额,余额不足返回「余额不足,请先充值。可联系微信xujian_cq」且不扣费;充值可可联系微信xujian_cq。扣费金额、交易前后余额均记录于交易流水中。
本接口为调用即计费:图片通过校验进入识别流程后, 无论最终识别成功还是失败(模糊、不是银行卡、上游超时)都计一次费用, 因为识别算力已经发生。只有未传图片、超 10MB、格式不支持、base64 非法、 远程地址下载失败这类参数级错误才不扣费。
卡号会认错吗?有没有校验?
卡号是核心字段,接口会逐位与卡面核对,不确定时宁可留空也不猜测。 但接口不做 Luhn 校验位推断——返回的是卡面原文, 业务侧如需强校验请自行加 Luhn(Python 示例里给了一份实现), 涉及真实绑卡与打款的场景请走支付通道的鉴权或打款验证。
为什么有的字段返回空字符串?
cardLevel、cardName、holderName
这些字段很多卡面本来就不印(尤其是借记卡),此时返回空字符串属于正常;
若是卡号区域模糊 / 反光 / 被手指遮挡,cardNo 也会留空,
并结合 qualityNote 给出提示。
binNo 是怎么来的?能用于判定发卡行吗?
binNo 由服务端按识别出的卡号前 6 位推导,不依赖模型输出,
因此只要卡号识别正确,BIN 就一定正确,可直接用于发卡行与卡种判定。
图片怎么传最方便?
三种方式任选:multipart/form-data 的 file 字段(最省事,浏览器 / App 直传)、
JSON 的 image(base64,可带 data URI 前缀)、
imageUrl(服务端去下载,适合图片已在对象存储上的场景)。
三者同时传时优先级为 file > image > imageUrl。
支持信用卡背面、身份证、行驶证吗?
本接口仅识别银行卡。 上传身份证、行驶证、名片、会员卡时,接口会返回「未能识别出银行卡信息」并按计费规则扣费, 请务必在上传前做卡片类型校验或给用户明确提示。 身份证请用 idcard.ocr, 行驶证请用 vehicle.license。
图片会不会被保存?
不会。平台不落库图片与识别结果: 图片仅在本次请求的内存中参与识别,请求结束后即被丢弃, 响应中只返回结构化字段,接入方无需为此做额外的合规申报。
调用需要签名或加密吗?
不需要。XAPI 的接口仅校验请求头 X-API-Key,不做签名、时间戳或加密。