在 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 [API 24]', '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}失败[1020100000]: 当前设备不支持此安全Kit能力(如模拟器或部分无前置安全相机PC)!`, 'error');
- } else if (errCode === 1020100001) {
- this.pushLog(`❌ ${action}失败[1020100001]: 用户未接受合规认证或人脸核身协议!`, 'warning');
- } else if (errCode === 1020100002) {
- this.pushLog(`❌ ${action}中断[1020100002]: 用户手动取消了核身或授权流程。`, 'warning');
- } else if (errCode === 1020100003) {
- this.pushLog(`❌ ${action}拦截[1020100003]: PreVerifyId 预验证 ID 格式非法或已过期!`, 'error');
- } else if (errCode === 1020100004) {
- this.pushLog(`❌ ${action}熔断[1020100004]: 当前网络断开,无法拉起核身校验通道。`, 'error');
- } else if (errCode === 1020100005) {
- this.pushLog(`❌ ${action}故障[1020100005]: 系统服务内部致命错误。`, 'error');
- } else if (errCode === 1020100006) {
- this.pushLog(`❌ ${action}被拒[1020100006]: 核心相机组件权限(ohos.permission.CAMERA)未被授予!`, 'error');
- } else if (errCode === 1020100007) {
- this.pushLog(`❌ ${action}失败[1020100007]: 活体检测未通过(翻拍拦截)!`, '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 并确保在真机(非模拟器)上运行人脸核身功能。 |