feat(kyc): 升级实名认证为三要素验证(姓名+身份证号+手机号)
- 后端 aliyun-kyc.provider.ts: 改用 ID_CARD_THREE 类型,添加 PhoneNumber 参数 - 后端 kyc-application.service.ts: 从用户账户获取手机号传递给 KYC provider - 前端 kyc_id_page.dart: 更新文案为"三要素验证" 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
e4f27b3134
commit
181d11d656
|
|
@ -211,15 +211,16 @@ export class KycApplicationService {
|
|||
|
||||
/**
|
||||
* ========================================
|
||||
* 层级1: 实名认证 - 提交二要素验证
|
||||
* 层级1: 实名认证 - 提交三要素验证
|
||||
* ========================================
|
||||
* 验证姓名+身份证号+手机号(手机号从用户账户获取)
|
||||
*/
|
||||
async submitRealNameVerification(
|
||||
userId: string,
|
||||
realName: string,
|
||||
idCardNumber: string,
|
||||
) {
|
||||
this.logger.log(`[KYC] [Level1] Submitting real name verification for user: ${userId}`);
|
||||
this.logger.log(`[KYC] [Level1] Submitting real name verification (3-factor) for user: ${userId}`);
|
||||
|
||||
const config = await this.getKycConfig();
|
||||
if (!config.level1Enabled) {
|
||||
|
|
@ -231,6 +232,7 @@ export class KycApplicationService {
|
|||
select: {
|
||||
realNameVerified: true,
|
||||
kycStatus: true,
|
||||
phoneNumber: true,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -242,6 +244,10 @@ export class KycApplicationService {
|
|||
throw new ApplicationError('实名认证已完成,无需重复提交');
|
||||
}
|
||||
|
||||
if (!user.phoneNumber) {
|
||||
throw new ApplicationError('未绑定手机号,无法进行实名认证');
|
||||
}
|
||||
|
||||
// 生成请求 ID
|
||||
const requestId = `REAL_NAME_${userId}_${Date.now()}`;
|
||||
|
||||
|
|
@ -249,22 +255,24 @@ export class KycApplicationService {
|
|||
const attempt = await this.prisma.kycVerificationAttempt.create({
|
||||
data: {
|
||||
userId: BigInt(userId),
|
||||
verificationType: 'ID_CARD',
|
||||
verificationType: 'ID_CARD_THREE',
|
||||
provider: 'ALIYUN',
|
||||
requestId,
|
||||
inputData: {
|
||||
realName: this.maskName(realName),
|
||||
idCardNumber: this.maskIdCard(idCardNumber),
|
||||
phoneNumber: this.maskPhoneNumber(user.phoneNumber),
|
||||
},
|
||||
status: 'PENDING',
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
// 调用阿里云二要素验证
|
||||
// 调用阿里云三要素验证
|
||||
const result = await this.aliyunKycProvider.verifyIdCard(
|
||||
realName,
|
||||
idCardNumber,
|
||||
user.phoneNumber,
|
||||
requestId,
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ export interface IdCardOcrResult {
|
|||
/**
|
||||
* 阿里云实人认证服务 - 支持三层认证
|
||||
*
|
||||
* 层级1: 实名认证 - 二要素验证(姓名+身份证号)
|
||||
* 层级1: 实名认证 - 三要素验证(姓名+身份证号+手机号)
|
||||
* 层级2: 实人认证 - 人脸活体检测
|
||||
* 层级3: KYC - 证件照OCR识别验证
|
||||
*
|
||||
|
|
@ -83,32 +83,34 @@ export class AliyunKycProvider {
|
|||
|
||||
/**
|
||||
* ========================================
|
||||
* 层级1: 实名认证 - 二要素验证
|
||||
* 层级1: 实名认证 - 三要素验证
|
||||
* ========================================
|
||||
* 验证姓名和身份证号是否匹配
|
||||
* 验证姓名、身份证号和手机号是否匹配
|
||||
*/
|
||||
async verifyIdCard(
|
||||
realName: string,
|
||||
idCardNumber: string,
|
||||
phoneNumber: string,
|
||||
requestId: string,
|
||||
): Promise<IdCardVerificationResult> {
|
||||
this.logger.log(`[AliyunKYC] [Level1] Starting ID card verification, requestId: ${requestId}`);
|
||||
this.logger.log(`[AliyunKYC] [Level1] Starting ID card verification (3-factor), requestId: ${requestId}`);
|
||||
|
||||
// 开发/测试环境:模拟验证
|
||||
if (!this.enabled) {
|
||||
this.logger.warn('[AliyunKYC] KYC is disabled, using mock verification');
|
||||
return this.mockIdCardVerification(realName, idCardNumber);
|
||||
return this.mockIdCardVerification(realName, idCardNumber, phoneNumber);
|
||||
}
|
||||
|
||||
try {
|
||||
// 调用阿里云身份二要素核验 API
|
||||
// 调用阿里云身份三要素核验 API
|
||||
const params = {
|
||||
Action: 'VerifyMaterial',
|
||||
Version: '2019-03-07',
|
||||
Format: 'JSON',
|
||||
BizType: 'ID_CARD_TWO',
|
||||
BizType: 'ID_CARD_THREE',
|
||||
Name: realName,
|
||||
IdCardNumber: idCardNumber,
|
||||
PhoneNumber: phoneNumber,
|
||||
};
|
||||
|
||||
const response = await this.callAliyunApi(params);
|
||||
|
|
@ -410,8 +412,8 @@ export class AliyunKycProvider {
|
|||
|
||||
// ============ Mock 方法 (开发/测试环境) ============
|
||||
|
||||
private mockIdCardVerification(realName: string, idCardNumber: string): IdCardVerificationResult {
|
||||
this.logger.log('[AliyunKYC] Using mock ID card verification');
|
||||
private mockIdCardVerification(realName: string, idCardNumber: string, phoneNumber: string): IdCardVerificationResult {
|
||||
this.logger.log('[AliyunKYC] Using mock ID card verification (3-factor)');
|
||||
|
||||
// 基本格式验证
|
||||
if (!realName || realName.length < 2) {
|
||||
|
|
@ -441,9 +443,19 @@ export class AliyunKycProvider {
|
|||
};
|
||||
}
|
||||
|
||||
// 手机号格式验证
|
||||
const phoneRegex = /^1[3-9]\d{9}$/;
|
||||
if (!phoneNumber || !phoneRegex.test(phoneNumber)) {
|
||||
return {
|
||||
success: false,
|
||||
errorMessage: '手机号格式不正确',
|
||||
rawResponse: { mock: true, reason: 'invalid_phone' },
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
rawResponse: { mock: true, verifyTime: new Date().toISOString() },
|
||||
rawResponse: { mock: true, verifyTime: new Date().toISOString(), verifyType: '3-factor' },
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import 'package:flutter_screenutil/flutter_screenutil.dart';
|
|||
import 'package:go_router/go_router.dart';
|
||||
import 'kyc_entry_page.dart';
|
||||
|
||||
/// KYC 层级1: 实名认证页面 (二要素验证)
|
||||
/// KYC 层级1: 实名认证页面 (三要素验证: 姓名+身份证号+手机号)
|
||||
class KycIdPage extends ConsumerStatefulWidget {
|
||||
const KycIdPage({super.key});
|
||||
|
||||
|
|
@ -143,7 +143,7 @@ class _KycIdPageState extends ConsumerState<KycIdPage> {
|
|||
SizedBox(height: 24.h),
|
||||
|
||||
Text(
|
||||
'二要素验证',
|
||||
'三要素验证',
|
||||
style: TextStyle(
|
||||
fontSize: 24.sp,
|
||||
fontWeight: FontWeight.w700,
|
||||
|
|
@ -152,7 +152,7 @@ class _KycIdPageState extends ConsumerState<KycIdPage> {
|
|||
),
|
||||
SizedBox(height: 8.h),
|
||||
Text(
|
||||
'请填写与身份证一致的信息',
|
||||
'请填写与身份证一致的信息,系统将自动使用您的注册手机号进行验证',
|
||||
style: TextStyle(
|
||||
fontSize: 14.sp,
|
||||
color: const Color(0xFF999999),
|
||||
|
|
@ -224,7 +224,7 @@ class _KycIdPageState extends ConsumerState<KycIdPage> {
|
|||
SizedBox(width: 8.w),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'您的身份信息将通过权威数据源进行验证,信息将被加密存储,不会泄露给任何第三方。',
|
||||
'您的身份信息(姓名+身份证号+手机号)将通过权威数据源进行三要素验证,信息将被加密存储,不会泄露给任何第三方。',
|
||||
style: TextStyle(
|
||||
fontSize: 12.sp,
|
||||
color: const Color(0xFFE65100),
|
||||
|
|
|
|||
Loading…
Reference in New Issue