Payment Kit 身份核验实战:API 24 实名验证与人脸核身 PC 端适配
在 HarmonyOS NEXT 6.1.1(API 24)中,Payment Kit 的 realNameService 服务迎来重要升级:人脸核身能力正式支持 PC 及 2in1 设备,同时元服务(Atomic Service)也可免安装调用身份核验接口。这对于金融、政务、医疗等强合规场景的身份真实性验证具有重要意义。本文介绍实名验证、实名授权、人脸核身三大防护矩阵,并给出完整的 ETS 代码实现。一、身份验证三大防护矩阵
realNameService 提供三层递进的安全验证机制:
实名信息验证(startRealNameVerification):校验用户输入的姓名+身份证号是否与支付底座预留信息一致,适用于游戏防沉迷、政务预约等无需留存证件原件的场景。
实名信息授权(startRealNameAuth):直接从系统支付底座提取已认证的姓名、证件号,适用于金融开户、医疗建档等需要快速录入实名数据的场景。
人脸核身实人验证(startFaceVerification):通过摄像头采集面部数据,结合 TEE 活体检测算法验证操作者为本人,适用于大额转账、修改密保等高风险操作。
二、PC/2in1 设备的新特性打通
API 24 通过多端一致性硬件抽象层(HAL),将 PC 及 2in1 设备的安全摄像头与底层 TEE 安全微内核打通,防止注入虚假相机流、翻拍、AI 换脸等攻击。人脸核身组件在 PC 端同样运行在系统级安全渲染层,商户应用无法截图、录屏或窃听输入。同时,身份核验接口全面支持元服务免安装拉起,降低了轻量级政务或缴费元服务的集成门槛。
三、直连商户核身接入流程
商户服务端先向华为支付网关发起预验证请求,获取具有时效性的 preVerifyId;客户端通过 UIAbilityContext 调用 realNameService 接口(如 startFaceVerification);用户完成核身同意后,Promise 返回 verifyResultId 或 realNameAuthId;商户服务端再向支付网关核对该凭证,最终确认身份。整套流程确保一过性安全。
四、ETS 完整代码实现
以下代码实现了一个身份核验控制舱页面,包含三个核心按钮:实名验证、实名授权、人脸核身。注意需要在正式工程中替换预验证 ID 并适配 UIAbilityContext。错误码处理统一封装在 handlePaymentErrorCode 方法中。
import { router } from '@kit.ArkUI';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { realNameService } from '@kit.PaymentKit';
import { common } from '@kit.AbilityKit';
const TAG = 'PaymentKitRealNameDetail';
const DOMAIN_NUMBER = 0xFF00;
class PaymentLogEntry {
timestamp: string = '';
message: string = '';
type: string = '';
constructor(timestamp: string, message: string, type: string) {
this.timestamp = timestamp;
this.message = message;
this.type = type;
}
}
@Entry
@Component
struct PaymentKitRealNameDetail {
@State preVerifyIdInput: string = 'your_pre_verify_id_2026_demo';
@State faceVerifyIdInput: string = 'your_face_pre_verify_id_2026_demo';
@State isVerifyingName: boolean = false;
@State isAuthorizingName: boolean = false;
@State isVerifyingFace: boolean = false;
@State scanningLineOffsetY: number = 0;
@State isScanning: boolean = false;
@State consoleLogs: PaymentLogEntry[] = [];
aboutToAppear() {
this.pushLog('🎬 Payment Kit 身份核验控制舱已就位', 'i');
this.pushLog('🛡️ 系统能力监测:SystemCapability.Payment.RealNameService ', 'i');
this.pushLog('💻 新增特性:人脸核身实人验证能力已全面覆盖 PC/2in1 设备', 'i');
}
private pushLog(msg: string, type: string = 'i') {
const time = new Date().toLocaleTimeString();
const entry = new PaymentLogEntry(time, msg, type);
this.consoleLogs.unshift(entry);
hilog.i(DOMAIN_NUMBER, TAG, `[${type.toUpperCase()}] ${msg}`);
}
private clearLogs() {
this.consoleLogs = [];
this.pushLog('🧹 极客终端日志已清理。', 'i');
}
private async executeRealNameVerification() {
if (this.isVerifyingName) return;
this.isVerifyingName = true;
this.pushLog(`🚀 准备拉起实名信息验证组件,PreVerifyId: ${this.preVerifyIdInput}`, 'i');
try {
const context = this.getUIContext().getHostContext() as common.UIAbilityContext;
if (!context) {
throw new Error('无法获取 Stage 模型的 UIAbilityContext 上下文信息');
}
this.pushLog('⏳ 正在拉起实名验证授权收银台...', 'i');
const verifyResultId = await realNameService.startRealNameVerification(context, this.preVerifyIdInput);
this.isVerifyingName = false;
this.pushLog(`🎉 实名信息验证成功!结果凭证 VerifyResultId: ${verifyResultId}`, 'success');
} catch (error) {
this.isVerifyingName = false;
const err = error as BusinessError;
this.handlePaymentErrorCode('实名信息验证', err);
}
}
private async executeRealNameAuthorization() {
if (this.isAuthorizingName) return;
this.isAuthorizingName = true;
this.pushLog('🚀 准备拉起实名信息授权组件...', 'i');
try {
const context = this.getUIContext().getHostContext() as common.UIAbilityContext;
if (!context) {
throw new Error('无法获取 Stage 模型的 UIAbilityContext 上下文信息');
}
this.pushLog('⏳ 正在拉起实名授权组件...', 'i');
const realNameAuthId = await realNameService.startRealNameAuth(context);
this.isAuthorizingName = false;
this.pushLog(`🎉 实名信息授权成功!结果凭证 RealNameAuthId: ${realNameAuthId}`, 'success');
} catch (error) {
this.isAuthorizingName = false;
const err = error as BusinessError;
this.handlePaymentErrorCode('实名信息授权', err);
}
}
private async executeFaceVerification() {
if (this.isVerifyingFace) return;
this.isVerifyingFace = true;
this.isScanning = true;
this.triggerFaceScanningAnimation();
this.pushLog(`🚀 准备拉起人脸核身实人验证,PreVerifyId: ${this.faceVerifyIdInput}`, 'i');
try {
const context = this.getUIContext().getHostContext() as common.UIAbilityContext;
if (!context) {
throw new Error('无法获取 Stage 模型的 UIAbilityContext 上下文信息');
}
this.pushLog('⏳ 正在唤醒本地安全摄像头并加载 TEE 人脸核身组件...', 'i');
const verifyResultId = await realNameService.startFaceVerification(context, this.faceVerifyIdInput);
this.isVerifyingFace = false;
this.isScanning = false;
this.pushLog(`🎉 人脸核身实人验证成功!结果凭证 VerifyResultId: ${verifyResultId}`, 'success');
} catch (error) {
this.isVerifyingFace = false;
this.isScanning = false;
const err = error as BusinessError;
this.handlePaymentErrorCode('人脸核身验证', err);
}
}
private triggerFaceScanningAnimation() {
if (!this.isScanning) return;
animateTo({
duration: 1200,
curve: Curve.EaseInOut,
onFinish: () => {
if (this.isScanning) {
animateTo({
duration: 1200,
curve: Curve.EaseInOut,
onFinish: () => {
this.triggerFaceScanningAnimation();
}
}, () => {
this.scanningLineOffsetY = 0;
})
}
}
}, () => {
this.scanningLineOffsetY = 110;
})
}
private handlePaymentErrorCode(action: string, error: BusinessError) {
const errCode = error.code;
const errMsg = error.message;
if (errCode === 1020100000) {
this.pushLog(`❌ ${action}失败: 当前设备不支持此安全Kit能力(如模拟器或部分无前置安全相机PC)!`, 'error');
} else if (errCode === 1020100001) {
this.pushLog(`❌ ${action}失败: 用户未接受合规认证或人脸核身协议!`, 'warning');
} else if (errCode === 1020100002) {
this.pushLog(`❌ ${action}中断: 用户手动取消了核身或授权流程。`, 'warning');
} else if (errCode === 1020100003) {
this.pushLog(`❌ ${action}拦截: PreVerifyId 预验证 ID 格式非法或已过期!`, 'error');
} else if (errCode === 1020100004) {
this.pushLog(`❌ ${action}熔断: 当前网络断开,无法拉起核身校验通道。`, 'error');
} else if (errCode === 1020100005) {
this.pushLog(`❌ ${action}故障: 系统服务内部致命错误。`, 'error');
} else if (errCode === 1020100006) {
this.pushLog(`❌ ${action}被拒: 核心相机组件权限(ohos.permission.CAMERA)未被授予!`, 'error');
} else if (errCode === 1020100007) {
this.pushLog(`❌ ${action}失败: 活体检测未通过(翻拍拦截)!`, 'error');
} else if (errCode === 1020100008 || errCode === 1020100009) {
this.pushLog(`❌ ${action}失败[${errCode}]: AppID/UserID 与预验证服务不一致!`, 'error');
} else {
this.pushLog(`❌ ${action}发生异常[${errCode}]: ${errMsg}`, 'error');
}
}
build() {
// UI 布局(略,详见原文)
}
}
五、错误码速查表
实际调试中,可通过 handlePaymentErrorCode 方法快速定位问题。关键错误码:
1020100000:设备不支持安全 Kit能力(常见于模拟器或无安全相机的 PC)
1020100001:用户未同意协议
1020100002:用户取消
1020100003:preVerifyId 过期或格式错误
1020100006:缺少摄像头权限
1020100007:活体检测未通过(翻拍拦截)
本文内容基于 HarmonyOS NEXT 6.1.1 (API 24) 官方能力编写,开发者可直接复制代码到 DevEco Studio 中运行测试。注意替换预验证 ID 并确保在真机(非模拟器)上运行人脸核身功能。
Re: Payment Kit 身份核验实战:API 24 实名验证与人脸核身 PC 端适配
感谢分享!这篇实战内容太及时了,我们团队正好在适配PC端的金融核身场景。API 24把人脸核身能力扩展到PC/2in1设备这点很关键,底层TEE防注入也解决了我们之前担心的安全问题。想问一下,startFaceVerification在PC上调用时,摄像头选择和权限处理跟移动端有啥区别吗?还有那个preVerifyId的失效时间一般是多久?盼复!Re: Payment Kit 身份核验实战:API 24 实名验证与人脸核身 PC 端适配
感谢楼主分享这么详细的技术实战!Payment Kit 在 API 24 上的升级确实很实用,特别是人脸核身能力覆盖 PC 和 2in1 设备后,金融政务类应用的跨端适配应该能省不少功夫。你提到的三层防护矩阵逻辑清晰,从信息校验到活体检测的递进设计很合理。 代码部分我仔细看了,用 `PaymentLogEntry` 和扫描线动画来展示状态,对调试和用户体验都挺友好。想请教一下:在 PC 端调用 `startFaceVerification` 时,摄像头权限和隐私提示的流程跟手机端有差异吗?另外,元服务免安装调用时,`preVerifyId` 的获取是否也需要在服务端先向支付网关发请求,还是元服务本身可以内置一个临时 ID?期待后续更多实践经验分享。Re: Payment Kit 身份核验实战:API 24 实名验证与人脸核身 PC 端适配
感谢楼主分享!PC和2in1设备支持人脸核身确实是个重要更新,对金融和政务场景的覆盖很有价值。代码部分很清晰,不过我看到错误码处理封装在handlePaymentErrorCode里,但示例中没贴出具体实现,能否补充一下常用错误码的对应处理逻辑?另外,预验证ID的获取流程在服务端对接时有没有什么注意事项?
页:
[1]