商协会审核入会申请,有一件事绕不开:确认来申请的人确实是他声称的那家企业的人。
过去的做法是让申请人把营业执照和身份证照片发过来,秘书处的人肉眼比对。这个办法有三个问题:一是慢,一份材料来回确认要一两天;二是容易错,照片拍糊了、名字改过、证照过期,肉眼都不一定看得出来;三是没有留痕,过几年回头查当时是谁审的、依据是什么,说不清。
后来我们把这一步搬到了线上,用腾讯云慧眼人脸核身(产品代号 faceid)做核验。这篇把接入过程记录一下。
先说整体流程。
第一步,申请人在小程序或者 H5 上填完企业信息和个人信息,点"开始核验"。
第二步,前端请求我们自己的服务端,服务端调用慧眼的接口申请一个核身令牌(BizToken),把这个令牌返回给前端。
第三步,前端拿着令牌拉起核身流程:用户做活体检测(眨眼、摇头或者读数),系统拿检测到的人脸跟权威库的证件照比对。
第四步,核身完成后,服务端再调一次接口拉取结果,把结果写进申请记录。
这里有个设计要点:核身结果不能只信前端回调。前端回调适合用来触发"拉一下结果"这个动作,真正作为判断依据的,必须是服务端主动去查的那一次。因为前端的返回可以被伪造,服务端的查询走的是签名请求,改不了。
服务端代码大概是这样。
```js
// 申请核身令牌 + 查询结果(Node.js,跑在云函数 SCF 上)
const tencentcloud = require('tencentcloud-sdk-nodejs-faceid')
const { Client } = tencentcloud.faceid.v20200303
const { Credential } = tencentcloud.common
const client = new Client({
credential: new Credential(process.env.TENCENTCLOUD_SECRET_ID, process.env.TENCENTCLOUD_SECRET_KEY),
region: 'ap-guangzhou',
profile: { httpProfile: { endpoint: 'faceid.tencentcloudapi.com' } }
})
// 1. 申请 BizToken
async function createToken(orderNo, idCard, name) {
const res = await client.GetFaceIdToken({
CompareLib: 'AUTH', // 与权威库比对
IdCard: idCard,
Name: name,
RuleId: process.env.FACEID_RULE_ID, // 控制台配置的核身流程规则
RedirectUrl: 'https://example.com/verify/done',
Extra: orderNo // 透传业务单号,查结果时要用
})
return res.BizToken
}
// 2. 核身完成后查结果
async function queryResult(bizToken, orderNo) {
const res = await client.GetFaceIdResult({
BizToken: bizToken,
RuleId: process.env.FACEID_RULE_ID,
Extra: orderNo
})
// Result: 0 通过;非 0 见错误码表
return {
passed: res.Result === '0',
description: res.Description,
similarity: res.Similarity // 人脸相似度,可做二次判断
}
}
```
接下来是几个实际接入时踩过的点。
第一个是 BizToken 的有效期。令牌不是无限期有效的,过期了前端拉起会失败。我们的处理是:申请人点"开始核验"时才去申请令牌,不在进页面时就申请。同时前端做了失败重试——如果拉起失败,重新走一次申请令牌的流程,而不是让用户退出重进。
第二个是核身结果的留存时限。慧眼的结果不会永久保存,超过一定时间就查不到了。所以核身完成的回调一进来,服务端要立刻去查一次结果并落库,不能等审核人员几天后再去看。我们当时的做法是回调触发即查询,查到就写进申请记录的核验字段,附相似度分值。
第三个是幂等。同一个人可能核验多次——第一次光线不好没通过,第二次重来。如果每次都新建一条记录,审核人员看到一堆重复项会混乱。我们的处理是以申请单号做幂等键,同一个单号多次核验只保留最后一次的结果,历史记录单独存一份备查。
第四个是 RuleId 的配置。核身流程的细节(要不要做活体、要不要读数字、要不要比对权威库)都是在控制台的核身规则里配的,代码里只传一个 RuleId。这个好处是调整流程不用改代码发版,坏处是环境容易搞混——测试环境和生产环境的 RuleId 不一样,出现过测试环境配的规则被用到生产的情况。建议把 RuleId 放进环境变量,部署时按环境注入,并在启动日志里打出来。
第五个是失败原因的展示。核身失败的原因有很多:光线不足、戴了口罩、人脸比对相似度不够、证件信息有误。直接把接口返回的原始描述给用户看,体验不好,也容易引发误解。我们做了一层映射,把常见错误码转成用户能懂的提示,比如"光线偏暗,请在明亮处重试""信息不匹配,请确认姓名和证件号"。
再说合规这块。
人脸属于敏感个人信息,处理之前要做的几件事:一是隐私政策里明确写清楚采集目的、使用范围、存储期限;二是在采集前弹出单独授权,不能用一揽子同意糊弄过去;三是数据够用即可,核身结果我们只存"是否通过"和"相似度分值",不存人脸图像;四是留存期限到了要做删除,我们在数据库里给了核验记录一个过期时间,定时任务清理。
这几条不是形式主义。社会组织处理会员信息,一旦出问题,声誉损失比企业还大。
最后是成本。人脸核身是按次计费的,商协会的场景量不大——一个新会员核验一次,一年可能就几百次,费用可控。值得注意的反而是无效调用:申请人点进去又退出、反复重试,这些都会计费。我们的做法是在前端加了明确的提示,让用户知道这一步要做什么,减少无谓的重复。
我们这边在用的商会管理系统叫未来漫城·商会互联平台,入会申请里的法人身份核验就是走这套链路,核验结果直接挂在申请记录上,审核人员在后台能一眼看到是否通过以及核验时间。
做完之后直观的变化是:入会审核从原来的一两天缩短到几个小时,而且每一条都有据可查。对秘书处来说,省的不只是时间,还有"这个人到底是不是本人"的心理负担。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。