Skip to content

feat(miniapp): 新增微信小程序人脸核身服务后台接口#3902

Open
Copilot wants to merge 6 commits intodevelopfrom
copilot/add-wechat-face-recognition-api
Open

feat(miniapp): 新增微信小程序人脸核身服务后台接口#3902
Copilot wants to merge 6 commits intodevelopfrom
copilot/add-wechat-face-recognition-api

Conversation

Copy link
Contributor

Copilot AI commented Mar 5, 2026

按照官方文档实现微信小程序人脸核身服务的两个后台接口,接口路径以 /cityservice/face/identify/ 为前缀。

新增接口

  • getVerifyId — 根据用户实名信息(姓名+身份证)获取核身会话标识 verifyId,供前端调用 wx.requestFacialVerify 使用
  • queryVerifyInfo — 根据 verifyId 查询人脸核身真实结果;核身通过条件:errcode=0verify_ret=10000

新增文件

类型 文件
接口 WxMaFaceService
实现 WxMaFaceServiceImpl
Bean WxMaFaceGetVerifyIdRequest/ResponseWxMaFaceQueryVerifyInfoRequest/Response
常量 WxMaApiUrlConstants.Face

变更文件

  • WxMaService — 新增 getFaceService() 声明
  • BaseWxMaServiceImpl — 注册服务实例并实现 getter

cert_hash 计算

queryVerifyInfo 需要传入 cert_hashWxMaFaceQueryVerifyInfoRequest 内置了静态工具方法:

// 对 cert_info 中各字段 Base64 编码后拼接,再 SHA-256
String certHash = WxMaFaceQueryVerifyInfoRequest.calcCertHash("IDENTITY_CARD", "张三", "310101199801011234");
// → "3c241f7ff324977aeb91f173bb2a7b06569e6fd784d5573db34a636d8671108b"

计算结果与官方文档示例值完全一致。

使用示例

WxMaFaceGetVerifyIdRequest req = WxMaFaceGetVerifyIdRequest.builder()
    .outSeqNo("BIZ20240101001")
    .certInfo(WxMaFaceGetVerifyIdRequest.CertInfo.builder()
        .certType("IDENTITY_CARD")
        .certName("张三")
        .certNo("310101199801011234")
        .build())
    .openid("oXXXXXXXXXXX")
    .build();

WxMaFaceGetVerifyIdResponse resp = wxMaService.getFaceService().getVerifyId(req);
String verifyId = resp.getVerifyId();

// 查询核身结果时计算 cert_hash
String certHash = WxMaFaceQueryVerifyInfoRequest.calcCertHash("IDENTITY_CARD", "张三", "310101199801011234");
Original prompt

This section details on the original issue you should resolve

<issue_title>新增接口实现:微信小程序人脸识别服务</issue_title>
<issue_description># 简要描述

实现微信小程序人脸识别服务后台相关接口,详情请参考微信官方需求文档。建议将官方文档内容直接贴入,以最大程度保证需求明确。

官方文档地址

https://developers.weixin.qq.com/miniprogram/dev/server/API/face/


以下为官方文档需求摘要(请补充完整或参考原文档):

微信小程序人脸识别服务 API 说明:

  • 支持创建在线人脸识别任务
  • 查询识别任务状态
  • 获取识别结果等

接口列表请参见文档原文。

注意不要自己瞎编接口(之前很多次PR都存在捏造根源文档不符的接口地址),完全按照文档里的要求来编写代码。

</issue_description>

Comments on the Issue (you are @copilot in this section)

Custom agent used: 全部用中文
需要用中文,包括PR标题和分析总结过程


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

- 添加 WxMaFaceService 接口,包含 getVerifyId 和 queryVerifyInfo 方法
- 添加 WxMaFaceServiceImpl 实现,包含 cert_hash 计算工具方法
- 添加请求/响应 bean:WxMaFaceGetVerifyIdRequest/Response、WxMaFaceQueryVerifyInfoRequest/Response
- 在 WxMaApiUrlConstants 中新增 Face 接口 URL 常量
- 在 WxMaService 和 BaseWxMaServiceImpl 中注册人脸核身服务
- 新增测试类 WxMaFaceServiceImplTest(含 cert_hash 单元测试)

Co-authored-by: binarywang <1343140+binarywang@users.noreply.github.com>
Copilot AI changed the title [WIP] Add WeChat mini-program face recognition service API feat(miniapp): 新增微信小程序人脸核身服务后台接口 Mar 5, 2026
@binarywang binarywang marked this pull request as ready for review March 5, 2026 04:06
Copilot AI review requested due to automatic review settings March 5, 2026 04:06
@augmentcode
Copy link

augmentcode bot commented Mar 5, 2026

🤖 Augment PR Summary

总结:本 PR 按照微信官方文档新增「小程序人脸核身」后台能力,补齐获取核身会话与查询核身结果两类接口。

主要变更:

  • 新增 WxMaFaceService 接口,提供 getVerifyIdqueryVerifyInfo 两个方法,覆盖创建核身会话与查询核身结果流程。
  • 新增实现类 WxMaFaceServiceImpl,通过 WxMaService.post 调用对应 API,并提供 calcCertHash 工具方法用于生成 cert_hash
  • 新增人脸核身相关请求/响应 Bean:WxMaFaceGetVerifyIdRequest/ResponseWxMaFaceQueryVerifyInfoRequest/Response
  • WxMaApiUrlConstants 中新增 Face 常量区,补充 /cityservice/face/identify/ 前缀下的两条接口 URL。
  • 扩展 WxMaService:新增 getFaceService(),并在 BaseWxMaServiceImpl 中完成服务实例注册与 getter 实现。
  • 新增 WxMaFaceServiceImplTest,包含接口调用示例以及对官方文档给出的 cert_hash 样例校验。

技术说明:接口调用由底层执行器统一追加 access_tokenqueryVerifyInfo 的通过条件(errcode=0verify_ret=10000)由业务侧基于响应字段自行判断。

🤖 Was this summary useful? React with 👍 or 👎

Copy link

@augmentcode augmentcode bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review completed. 2 suggestions posted.

Fix All in Augment

Comment augment review to trigger a new review at any time.

byte[] hashBytes = digest.digest(raw.getBytes(StandardCharsets.UTF_8));
StringBuilder hex = new StringBuilder();
for (byte b : hashBytes) {
hex.append(String.format("%02x", b));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里用 String.format("%02x", b) 直接格式化 byte 时,遇到负值字节可能输出 ffffffff...(不会截断),导致 cert_hash 长度/内容异常;建议确认按无符号字节生成 2 位 hex。

Severity: high

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

.certName("张三")
.certNo("310101199801011234")
.build())
.openid("test_openid_001")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

testGetVerifyId/testQueryVerifyInfo 里使用了明显的占位 openid/verifyId(如 test_openid_001test_verify_id_001),在真实调用微信接口时大概率直接报错抛 WxErrorException,会让用例不可用/不稳定。

Severity: medium

Other Locations
  • weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaFaceServiceImplTest.java:53
  • weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaFaceServiceImplTest.java:56

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

该 PR 在 weixin-java-miniapp 模块中新增“小程序人脸核身服务”后台接口封装,补齐官方文档中的 getVerifyIdqueryVerifyInfo 两个能力,并将其纳入 WxMaService 的子服务体系,方便 SDK 使用方以统一方式调用。

Changes:

  • 新增人脸核身服务接口 WxMaFaceService 及实现 WxMaFaceServiceImpl,封装 getVerifyId/queryVerifyInfo 请求。
  • 新增对应的请求/响应 Bean,并补充 WxMaApiUrlConstants.Face 接口地址常量。
  • WxMaService/BaseWxMaServiceImpl 中注册并暴露 getFaceService();新增相关测试类。

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaFaceServiceImplTest.java 新增人脸核身服务测试用例(目前包含直连真实接口的用例)。
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/WxMaApiUrlConstants.java 增加 Face 接口 URL 常量定义。
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/face/WxMaFaceQueryVerifyInfoResponse.java 新增 queryVerifyInfo 响应模型与反序列化方法。
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/face/WxMaFaceQueryVerifyInfoRequest.java 新增 queryVerifyInfo 请求模型与序列化方法。
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/face/WxMaFaceGetVerifyIdResponse.java 新增 getVerifyId 响应模型与反序列化方法。
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/face/WxMaFaceGetVerifyIdRequest.java 新增 getVerifyId 请求模型(含 cert_info)与序列化方法。
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaFaceServiceImpl.java 新增服务实现与 cert_hash 计算工具方法。
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/BaseWxMaServiceImpl.java 注册 WxMaFaceService 实例并提供 getter。
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaService.java 新增 getFaceService() 对外声明。
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaFaceService.java 新增人脸核身服务接口定义与方法注释。

Comment on lines +43 to +71
/**
* 计算证件信息摘要(cert_hash)
* <p>
* 计算规则:
* 1. 对 cert_type、cert_name、cert_no 字段内容进行标准 base64 编码(若含中文等Unicode字符,先进行UTF-8编码)
* 2. 按顺序拼接各个字段:cert_type=xxx&amp;cert_name=xxx&amp;cert_no=xxx
* 3. 对拼接串进行 SHA256 并输出十六进制小写结果
* </p>
*
* @param certType 证件类型
* @param certName 证件姓名
* @param certNo 证件号码
* @return cert_hash 十六进制小写字符串
*/
public static String calcCertHash(String certType, String certName, String certNo) {
String encodedType = Base64.getEncoder().encodeToString(certType.getBytes(StandardCharsets.UTF_8));
String encodedName = Base64.getEncoder().encodeToString(certName.getBytes(StandardCharsets.UTF_8));
String encodedNo = Base64.getEncoder().encodeToString(certNo.getBytes(StandardCharsets.UTF_8));
String raw = "cert_type=" + encodedType + "&cert_name=" + encodedName + "&cert_no=" + encodedNo;
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hashBytes = digest.digest(raw.getBytes(StandardCharsets.UTF_8));
StringBuilder hex = new StringBuilder();
for (byte b : hashBytes) {
hex.append(String.format("%02x", b));
}
return hex.toString();
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("SHA-256 algorithm not available", e);
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

calcCertHash 放在 api.impl 包下并作为 public static 暴露,会引导 SDK 使用方依赖实现类(示例/测试里也直接调用了 WxMaFaceServiceImpl.calcCertHash),不利于对外 API 的稳定性。建议将该工具方法移动到对外可见且语义更合适的位置(例如 WxMaFaceQueryVerifyInfoRequest/WxMaFaceGetVerifyIdRequest 的静态工具方法,或新增 cn.binarywang.wx.miniapp.util 下的工具类),并在实现类中改为调用新位置。

Copilot uses AI. Check for mistakes.
* 文档地址:<a href="https://developers.weixin.qq.com/miniprogram/dev/server/API/face/api_getverifyid">获取用户人脸核身会话唯一标识</a>
* </p>
*
* @author <a href="https://github.com/github-copilot">Github Copilot</a>
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Javadoc 作者信息里品牌拼写应为 “GitHub”,这里写成了 “Github”。建议统一更正为 “GitHub Copilot”。

Suggested change
* @author <a href="https://github.com/github-copilot">Github Copilot</a>
* @author <a href="https://github.com/github-copilot">GitHub Copilot</a>

Copilot uses AI. Check for mistakes.
Comment on lines +3 to +63
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.face.WxMaFaceGetVerifyIdRequest;
import cn.binarywang.wx.miniapp.bean.face.WxMaFaceGetVerifyIdResponse;
import cn.binarywang.wx.miniapp.bean.face.WxMaFaceQueryVerifyInfoRequest;
import cn.binarywang.wx.miniapp.bean.face.WxMaFaceQueryVerifyInfoResponse;
import cn.binarywang.wx.miniapp.test.ApiTestModule;
import com.google.inject.Inject;
import me.chanjar.weixin.common.error.WxErrorException;
import org.testng.annotations.Guice;
import org.testng.annotations.Test;

import static org.testng.Assert.assertEquals;
import static org.testng.AssertJUnit.assertNotNull;

/**
* 微信小程序人脸核身服务测试类
*
* @author <a href="https://github.com/github-copilot">Github Copilot</a>
*/
@Test
@Guice(modules = ApiTestModule.class)
public class WxMaFaceServiceImplTest {

@Inject
private WxMaService wxService;

@Test
public void testGetVerifyId() throws WxErrorException {
WxMaFaceGetVerifyIdRequest request = WxMaFaceGetVerifyIdRequest.builder()
.outSeqNo("TEST20240101001")
.certInfo(WxMaFaceGetVerifyIdRequest.CertInfo.builder()
.certType("IDENTITY_CARD")
.certName("张三")
.certNo("310101199801011234")
.build())
.openid("test_openid_001")
.build();

WxMaFaceGetVerifyIdResponse response = this.wxService.getFaceService().getVerifyId(request);
assertNotNull(response);
}

@Test
public void testQueryVerifyInfo() throws WxErrorException {
String certType = "IDENTITY_CARD";
String certName = "张三";
String certNo = "310101199801011234";
String certHash = WxMaFaceServiceImpl.calcCertHash(certType, certName, certNo);

WxMaFaceQueryVerifyInfoRequest request = WxMaFaceQueryVerifyInfoRequest.builder()
.verifyId("test_verify_id_001")
.outSeqNo("TEST20240101001")
.certHash(certHash)
.openid("test_openid_001")
.build();

WxMaFaceQueryVerifyInfoResponse response = this.wxService.getFaceService().queryVerifyInfo(request);
assertNotNull(response);
}

@Test
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

该测试类里的 testGetVerifyId/testQueryVerifyInfo 直接调用真实微信接口,但使用了 test_openid_001test_verify_id_001 等占位值。由于 BaseWxMaServiceImpl.executeInternalerrcode != 0 时会抛 WxErrorException,这些用例在绝大多数环境下会稳定失败。建议改为用 Mockito mock WxMaService.post(...) 返回示例 JSON,只验证请求序列化/响应反序列化与方法路由;或仅保留纯本地的 calcCertHash 计算测试。

Suggested change
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.face.WxMaFaceGetVerifyIdRequest;
import cn.binarywang.wx.miniapp.bean.face.WxMaFaceGetVerifyIdResponse;
import cn.binarywang.wx.miniapp.bean.face.WxMaFaceQueryVerifyInfoRequest;
import cn.binarywang.wx.miniapp.bean.face.WxMaFaceQueryVerifyInfoResponse;
import cn.binarywang.wx.miniapp.test.ApiTestModule;
import com.google.inject.Inject;
import me.chanjar.weixin.common.error.WxErrorException;
import org.testng.annotations.Guice;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
import static org.testng.AssertJUnit.assertNotNull;
/**
* 微信小程序人脸核身服务测试类
*
* @author <a href="https://github.com/github-copilot">Github Copilot</a>
*/
@Test
@Guice(modules = ApiTestModule.class)
public class WxMaFaceServiceImplTest {
@Inject
private WxMaService wxService;
@Test
public void testGetVerifyId() throws WxErrorException {
WxMaFaceGetVerifyIdRequest request = WxMaFaceGetVerifyIdRequest.builder()
.outSeqNo("TEST20240101001")
.certInfo(WxMaFaceGetVerifyIdRequest.CertInfo.builder()
.certType("IDENTITY_CARD")
.certName("张三")
.certNo("310101199801011234")
.build())
.openid("test_openid_001")
.build();
WxMaFaceGetVerifyIdResponse response = this.wxService.getFaceService().getVerifyId(request);
assertNotNull(response);
}
@Test
public void testQueryVerifyInfo() throws WxErrorException {
String certType = "IDENTITY_CARD";
String certName = "张三";
String certNo = "310101199801011234";
String certHash = WxMaFaceServiceImpl.calcCertHash(certType, certName, certNo);
WxMaFaceQueryVerifyInfoRequest request = WxMaFaceQueryVerifyInfoRequest.builder()
.verifyId("test_verify_id_001")
.outSeqNo("TEST20240101001")
.certHash(certHash)
.openid("test_openid_001")
.build();
WxMaFaceQueryVerifyInfoResponse response = this.wxService.getFaceService().queryVerifyInfo(request);
assertNotNull(response);
}
@Test
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
/**
* 微信小程序人脸核身服务本地计算测试类
*
* 仅验证证件哈希计算逻辑避免依赖真实微信接口
*/
@Test
public class WxMaFaceServiceImplTest {
@Test

Copilot uses AI. Check for mistakes.
import org.testng.annotations.Test;

import static org.testng.Assert.assertEquals;
import static org.testng.AssertJUnit.assertNotNull;
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里同时静态导入了 org.testng.Assert.assertEqualsorg.testng.AssertJUnit.assertNotNull,断言库混用会降低可读性并可能引入行为差异。建议统一使用 TestNG 的 org.testng.Assert(例如静态导入 assertNotNull),与本模块其他测试类保持一致。

Suggested change
import static org.testng.AssertJUnit.assertNotNull;
import static org.testng.Assert.assertNotNull;

Copilot uses AI. Check for mistakes.
/**
* 微信小程序人脸核身相关接口实现
*
* @author <a href="https://github.com/github-copilot">Github Copilot</a>
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Javadoc 作者信息里品牌拼写应为 “GitHub”,这里写成了 “Github”。建议统一更正为 “GitHub Copilot”。

Suggested change
* @author <a href="https://github.com/github-copilot">Github Copilot</a>
* @author <a href="https://github.com/github-copilot">GitHub Copilot</a>

Copilot uses AI. Check for mistakes.
* 文档地址:<a href="https://developers.weixin.qq.com/miniprogram/dev/server/API/face/api_queryverifyinfo">查询用户人脸核身真实验证结果</a>
* </p>
*
* @author <a href="https://github.com/github-copilot">Github Copilot</a>
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Javadoc 作者信息里品牌拼写应为 “GitHub”,这里写成了 “Github”。建议统一更正为 “GitHub Copilot”。

Suggested change
* @author <a href="https://github.com/github-copilot">Github Copilot</a>
* @author <a href="https://github.com/github-copilot">GitHub Copilot</a>

Copilot uses AI. Check for mistakes.
* 文档地址:<a href="https://developers.weixin.qq.com/miniprogram/dev/server/API/face/api_queryverifyinfo">查询用户人脸核身真实验证结果</a>
* </p>
*
* @author <a href="https://github.com/github-copilot">Github Copilot</a>
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Javadoc 作者信息里品牌拼写应为 “GitHub”,这里写成了 “Github”。建议统一更正为 “GitHub Copilot”。

Suggested change
* @author <a href="https://github.com/github-copilot">Github Copilot</a>
* @author <a href="https://github.com/github-copilot">GitHub Copilot</a>

Copilot uses AI. Check for mistakes.
binarywang and others added 3 commits March 5, 2026 12:18
…/impl/WxMaFaceServiceImplTest.java

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…/WxMaFaceService.java

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…n/face/WxMaFaceGetVerifyIdResponse.java

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@binarywang
Copy link
Owner

@copilot apply changes based on the comments in this thread

- 将 calcCertHash 工具方法从 WxMaFaceServiceImpl 移到 WxMaFaceQueryVerifyInfoRequest 中
- 修复所有文件中 Github → GitHub 拼写错误
- 重构测试类:移除依赖真实接口的测试,只保留本地 calcCertHash 计算测试
- 修复测试类断言库混用问题,统一使用 TestNG 的 org.testng.Assert

Co-authored-by: binarywang <1343140+binarywang@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Mar 5, 2026

@copilot apply changes based on the comments in this thread

已根据审查意见完成全部修改(commit 6297422):

  1. calcCertHash 方法迁移 — 从 WxMaFaceServiceImpl 移到 WxMaFaceQueryVerifyInfoRequest 中作为 public static 方法,使用方直接调用 WxMaFaceQueryVerifyInfoRequest.calcCertHash(...) 即可,无需依赖实现类
  2. 拼写修复 — 全部 GithubGitHub
  3. 测试类重构 — 移除依赖真实微信接口的 testGetVerifyId/testQueryVerifyInfo,只保留本地计算的 testCalcCertHash;统一使用 org.testng.Assert,消除断言库混用

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

新增接口实现:微信小程序人脸识别服务

3 participants