先说流程
一次核验分两步:拉起拿到一个核验单号,查询拿结论。之所以不合成一次调用, 是因为采集要用户在自己手机上做,几十秒到几分钟都有可能 —— 一次调用挂在那儿等, 连接断了这一单就成了「钱扣了没结论」。分开之后,查询问多少遍都不花钱。
1. POST https://face.yunsapi.com/api/face_start 带上 name / idcard(有公网图就再带 file_url)
← { "code": 1001, "data": { "check_id": "K...", "live_url": "https://…" } }
2. 把 live_url 交给用户(手机打开,或者生成二维码让他扫),按提示做一遍活体采集
3. POST https://face.yunsapi.com/api/face_query 带上 check_id,隔几秒问一次,直到拿到 2xxx / 3xxx / 4xxx 里的一个
← { "code": 2001, "data": { "check_id": "K...", "state": 2001, "passed": true, "score": 92.4 } }
用户做完采集会落在我们这一页(live.php)—— 那一页不用登录,只把这一单的结论告诉做采集的本人(通过 / 没通过),并且顺手把这一单收尾。 页面的地址里带着这一单的随机单号,没单号打不开。 你那一侧还是要自己查:以 face_query.php 的返回为准,别拿用户截图当结论。
live_url 是要给人用手机打开的,电脑上展示可以现场把它画成二维码让他扫。 验签、订单这类业务判断必须放在你自己的服务端,别放在这一页的返回值上。
密钥怎么带
密钥是两样:密钥 ID(AK 开头,公开的)和密钥(只在控制台显示一次)。两样都要带,少一样就当没带。
POST https://face.yunsapi.com/api/face_start Content-Type: application/x-www-form-urlencoded X-Api-Id: AK3F9C1D2E4B5A60 X-Api-Key: frs_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
| 放哪儿 | 密钥 ID | 密钥 |
|---|---|---|
| 请求头(推荐) | X-Api-Id | X-Api-Key |
| 表单参数 | api_id | key |
两样都在请求头时以请求头为准。浏览器里直接发请求会先来一次 OPTIONS 预检, 两个头都已经放行,不用另外配。
密钥 ID 可以随便露(日志里、报错里、找人问事时说出口都行):它单独调不动接口, 但能拿来对账。密钥本体泄漏了就在控制台点「换密钥」—— ID 不变,你代码里那一行不用改。
一、拉起:api/face_start
| 参数 | 必填 | 说明 |
|---|---|---|
| name | 是 | 姓名,2 到 6 个汉字 |
| idcard | 是 | 证件号,18 位 |
| file_url | 否 | 人脸照的公网地址,必须能被外网直接打开。给了就不用做采集,直接进第二步查询即可 |
{
"code": 1001,
"msg": "让用户去做人脸采集,做完之后拿 check_id 来查。",
"data": {
"check_id": "K20261007A1B2C3",
"state": 1001,
"live_url": "https://活体服务给的那条地址"
}
}
这一步扣一次费。拉不起来的话这一笔当场退回,返回 code 3002(没做完活体)。
二、查询:api/face_query
| 参数 | 必填 | 说明 |
|---|---|---|
| check_id | 是 | 拉起那一步给的单号 |
查询不扣费,隔几秒问一次都行。HTTP 一律 200 —— 下面这些值是业务结论,看 body 里的 code,别看 HTTP。
返回码:四位数,第一位就是阶段
拿到码就知道该做什么:1 等着、2 是结论、3 让本人重做一次采集、4 重新发起一单。
| code | 阶段 | 含义 | 你要做什么 |
|---|---|---|---|
| 1001 | 还在路上 | 单子建好了,等结果。 | 正常计费 |
| 1002 | 还没做完活体采集,做完再查。 | 正常计费 | |
| 1008 | 结果还在路上,过几秒再查。 | 正常计费 | |
| 2001 | 有结论 | 核验通过。 | 正常计费 |
| 2002 | 核验不通过:不是同一个人。 | 正常计费 | |
| 2003 | 核验不通过:分值没到设定的线。 | 正常计费 | |
| 2004 | 核验不通过:姓名和证件号对不上。 | 正常计费 | |
| 2005 | 基础库里没有这个人的照片,这一次比不了。 | 这一笔会退回 | |
| 2006 | 这张人脸图不合格(太糊、太暗、有遮挡),换一张清楚的再来。 | 这一笔会退回 | |
| 2007 | 同一组信息试得太频,等几分钟再来。 | 这一笔会退回 | |
| 3001 | 活体没过 | 活体检测没通过:采集到的不是本人。 | 正常计费 |
| 3002 | 没做完活体采集就超时了,这一次不算数。 | 这一笔会退回 | |
| 4001 | 没拿到结论 | 这一次没拿到结论,重新发起一单就行。 | 这一笔会退回 |
| 4002 | 等太久没有结果,这一次不算数。 | 这一笔会退回 | |
| 4003 | 对面没受理这次请求,多半是参数不对或者凭证过期了,看一下 msg 里那句。 | 正常计费 | |
| 4004 | 这一单被取消了,费用已经退回。 | 这一笔会退回 |
data.passed 只在有明确结论时才出现:2001 是 true, 2002 / 2003 / 2004 / 3001 是 false。 其余那些码(还没出结果、这一次没拿到结论)不给这个字段 —— 给了等于替你下了「不是同一人」的结论,照着它做业务判断会出事。 data.score 同理,有分值时才有。
从拉起算起超过 300 秒还没有结论就按超时收尾:人没做完活体是 3002,卡在比对那一步是 4002,两种都已经退回。
{
"code": 2001,
"msg": "核验通过。",
"data": { "check_id": "K20261007A1B2C3", "state": 2001, "passed": true, "score": 92.4 }
}
判定线是 60.00 分,达到就算同一人。
想一步出结论:api/face
如果你手上已经有能公网打开的人脸图,用这一个就够:把三个参数一次给全,它当场返回结论(只会有 2xxx 与 4xxx,不会有 1xxx)。它和上面两个接口走的是同一套判断,只是省掉了你去查询那一步。
| 参数 | 必填 | 说明 |
|---|---|---|
| name | 是 | 姓名,2 到 6 个汉字 |
| idcard | 是 | 证件号,18 位 |
| file_url | 是 | 人脸照的公网地址。这个接口必须给,要用户现场采集就用 face_start.php |
HTTP 错误码
| HTTP | 含义 | 怎么办 |
|---|---|---|
| 400 | 参数不合规 | 看 msg 里点名的那一项 |
| 401 | 密钥无效或已停用 | 换一把密钥 |
| 402 | 次数和余额都不够 | 去控制台充值或买次数包 |
| 403 | 账号被停用,或还没过实名 | 控制台里看状态 |
| 404 | 没有这一单(check_id 不是你的,或写错了) | 拿拉起那一步给的那个单号 |
| 405 | 用了 GET | 只收 POST |
| 429 | 调得太快 | 每个密钥每分钟最多 60 次,放慢一点重试 |
| 500 | 这一单没建起来 | 稍后再试,没扣费 |
扣费只发生在拉起那一步(单价 0.40 元,或者扣一个次数)。查询不扣。标着「这一笔会退回」的那几种(2005/2006/2007/3002/4001/4002)不计费。
几个提醒
图片地址要公网可达
内网地址、带鉴权的地址、点开是网页的地址都不行。地址打不开这一单会落成超时,那一笔已经退回,重新发起即可。
同一个人的信息别连着猛调
上游有撞库拦截。同一组信息短时间内反复请求会被拦下来,等几分钟再来。
轮询别太密
隔 3 到 5 秒问一次就够。查询不花钱,但也别把连接占满。
密钥不要放在前端
密钥等同于密码,只放在你自己的服务端,挂到公网页面里就等于公开。要在自己机器上写个网页版工具调试可以 —— 接口带了跨域头,浏览器那次 OPTIONS 预检直接放行,但那个页面只能自己用。
