③ 查散户状态
一次拿到某散户的全景:实名过没、500 万额度、收款人建档/审核/授权/冻结,并直接告诉你现在能不能给他开票。数据类接口。
属「工商银行 · 业务型接口」——底层编排 乐企直联原子接口,你只调这一个。
接口(数据类)
POST https://openapi.kailingteck.com/api/v1/rinv/seller/status
鉴权:X-App-Key + X-Timestamp + X-Sign(= sha256(appKey + 时间戳 + 报文 + appSecret))。
请求参数
| 字段 | 必填 | 大白话说明 |
|---|---|---|
userId | 必填 | 散户ID(与②一致),工行口径里这是「收方编号」 |
vendorId | 选填 | 企业子商户编号,工行口径里这是「付方编号」。不传则自动取后台为贵司配置的值 |
transNo | 选填 | ②发起扫脸时平台已自动记住流水号,一般不用传;显式传则以传的为准 |
taxNo | 选填 | 企业税号。不传时自动取后台为贵司配置的税号,一般不用传 |
bankCard | 选填 | 散户收款卡号。查「是否已入驻」工行必须有卡号——②之二建档成功时平台已自动记住,一般不用传;平台没有记录(如建档没走本平台)时才需显式传 |
请求示例
{
"userId": "U123",
"vendorId": "V1",
"transNo": "T1786630352940",
"taxNo": "91500114MAET09070Q"
}返回
所有接口都用同一层信封:code/msg/requestId/timestamp/data。业务字段都在 data 里,下表列的就是 data 的内容。code 为 CYT_00000 表示调用成功。
返回参数(data)
| 字段 | 大白话说明 |
|---|---|
canInvoice | ★直接看这个:true=现在可以给他开票。等于「已实名 且 已完成收方入驻 且 没触额度红线」。2026-09-15 起把「已入驻」也纳入判定 —— 此前只看实名,收方其实没在工行入驻时也会返 true,属误报 |
onboarded | ★是否已在工行完成收方入驻。注意:实名通过 ≠ 已入驻,这是两件事,刷脸只是其中一步 |
blockedBy | canInvoice=false 时卡在哪一步:realname 未实名 / onboard 未入驻 / quota 撞额度红线 |
blockedReason | 同上,中文说明,可直接展示给用户 |
receiverError | 查收方档案时工行返回的原始报错(正常时为 null)。出现 30601004「平台商户未开启智慧清分」属平台侧能力未开通,需联系工行处理 |
realNamed | 宽口径实名:刷脸通过 或 工行收方档案已存在(档案存在即推定实名,此时 realNamedBy=record)。★合规判断「散户本人是否做过实名」别用它,用 faceVerified |
faceVerified | ★真实名:散户本人刷脸实测通过(工行 authResult=02)。false=没刷过脸或没刷成——收方档案存在也不算。合规口径只认这个字段 |
faceTransNo | 本次实名查询用的认证流水号(显式传的或平台记住的);null=两边都没有,刷脸结果无从查起 |
receiverStatus | 收款人状态 |
auditStatus | 工行分行的审核状态 |
authorizeStatus | 授权状态 |
freezeStatus | 冻结状态 |
quota.over500W | 连续 12 个月累计开票是否超 500 万:0 否 / 1 是。★硬红线,超了不能开 |
quota.overYear210W | 当年累计是否超 210 万:0 否 / 1 是。★硬红线 |
quota.overYear200W | 当年累计是否超 200 万:0 否 / 1 是。接近提醒,不阻断 |
quota.overMonth10W | 当月累计是否超 10 万:0 否 / 1 是。接近提醒,不阻断 |
quota.backAmount | 该散户是否有退税余额:0 否 / 1 是 |
raw | 工行三个原始接口的全字段(实名结果 / 收款人 / 额度),需要细节时看这里 |
返回示例(成功)
{
"code": "CYT_00000",
"msg": "success",
"data": {
"userId": "U123",
"canInvoice": true,
"realNamed": true,
"receiverStatus": "0",
"auditStatus": "2",
"authorizeStatus": "1",
"freezeStatus": "1",
"quota": {
"over500W": "0",
"overYear200W": "0",
"overYear210W": "0",
"overMonth10W": "0",
"backAmount": "0"
},
"mock": false,
"raw": { }
}
}返回示例(失败 / 业务拦截)
// 还没实名 / 触了红线,canInvoice 就是 false
{
"code": "CYT_00000",
"data": {
"userId": "U123",
"canInvoice": false,
"realNamed": false,
"quota": { "over500W": "1" }
}
}异常与报错
| 情形 | 含义与处理 |
|---|---|
| canInvoice 一直 false | 先看 realNamed:false 说明散户没刷脸或没刷过;true 但仍 false,就是撞了 quota 里的硬红线 |
| quota 全为空 | 没传 taxNo,或该散户还没实名——额度查询要靠实名后绑定的关系 |
前端怎么用
// 轮询,直到 data.canInvoice === true 再给他开票
if (!res.data.canInvoice) return alert('该散户暂不可开票');★依赖铁律:查 500 万额度用 userId 当钥匙、不收身份证——所以必须先完成②扫脸实名,把 userId 和身份证绑上,才查得到额度。
所有接口通用的报错(平台层)
| HTTP / code | 含义与处理 |
|---|---|
400 · CYT_40001 | 必填参数没传,data.hint 里写了缺哪个 |
401 · CYT_40100 / 40102 | 缺鉴权头,或签名算错。签名 = sha256(appKey + 时间戳 + 报文原文 + appSecret),参与签名的报文必须与实际发送的逐字节一致 |
401 · CYT_40103 | 时间戳与服务器相差超过 300 秒,校准本机时间后重试 |
403 · CYT_40301 | 这把密钥的产品或渠道不对(工行接口需要「反向开票」产品 + 「工商银行」渠道) |
429 · CYT_42901 | 超过每分钟 120 次限流,稍后重试 |
502 · CYT_50200 | 工行侧暂时不可达。注意工行测试环境每晚 19:00 后关闭、次日恢复,联调请排在工作时间 |
200 · data.mock=true | 尚未配置工行凭据,返回的是演示数据、并未真调工行 |
