feat(identity-service): KYC 改用二要素验证
- 默认使用阿里云 Id2MetaStandardVerify API(姓名+身份证号) - 保留三要素验证方法 verifyIdCardThreeFactor 作为备用 - 二要素验证始终调用真实 API,不使用 mock - 兼容 BizCode 数字和字符串类型 🤖 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
7a1e789f4d
commit
4ec4df14b5
|
|
@ -83,13 +83,117 @@ export class AliyunKycProvider {
|
|||
|
||||
/**
|
||||
* ========================================
|
||||
* 层级1: 实名认证 - 手机号三要素验证
|
||||
* 层级1: 实名认证 - 二要素验证(默认)
|
||||
* ========================================
|
||||
* 验证姓名和身份证号是否匹配
|
||||
* 使用阿里云 IdCardTwoMetaVerify API (身份证二要素核验)
|
||||
* 文档: https://help.aliyun.com/zh/id-verification/information-verification/developer-reference/api-cloudauth-2019-03-07-idcardtwometaverify
|
||||
*/
|
||||
async verifyIdCard(
|
||||
realName: string,
|
||||
idCardNumber: string,
|
||||
phoneNumber: string,
|
||||
requestId: string,
|
||||
): Promise<IdCardVerificationResult> {
|
||||
// 默认使用二要素验证(姓名+身份证号)
|
||||
return this.verifyIdCardTwoFactor(realName, idCardNumber, requestId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 二要素验证:姓名 + 身份证号
|
||||
* 使用阿里云 IdCardTwoMetaVerify API
|
||||
*/
|
||||
async verifyIdCardTwoFactor(
|
||||
realName: string,
|
||||
idCardNumber: string,
|
||||
requestId: string,
|
||||
): Promise<IdCardVerificationResult> {
|
||||
this.logger.log(`[AliyunKYC] [Level1] Starting 2-factor verification, requestId: ${requestId}`);
|
||||
|
||||
// 二要素验证始终使用真实 API,不使用 mock
|
||||
if (!this.accessKeyId || !this.accessKeySecret) {
|
||||
this.logger.error('[AliyunKYC] KYC credentials not configured');
|
||||
return {
|
||||
success: false,
|
||||
errorMessage: '实名认证服务未配置',
|
||||
rawResponse: { error: 'credentials_not_configured' },
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
// 调用阿里云身份二要素标准版核验 API (Id2MetaStandardVerify)
|
||||
// 文档: https://help.aliyun.com/zh/id-verification/api-cloudauth-2019-03-07-id2metastandardverify-infor-verify
|
||||
// 参数说明:
|
||||
// - ParamType: 加密方式 (normal=明文)
|
||||
// - UserName: 姓名
|
||||
// - IdentifyNum: 身份证号
|
||||
const params = {
|
||||
Action: 'Id2MetaStandardVerify',
|
||||
Version: '2019-03-07',
|
||||
Format: 'JSON',
|
||||
ParamType: 'normal',
|
||||
UserName: realName,
|
||||
IdentifyNum: idCardNumber,
|
||||
};
|
||||
|
||||
const response = await this.callAliyunApi(params);
|
||||
|
||||
this.logger.log(`[AliyunKYC] [Level1] 2-factor API Response: ${JSON.stringify(response, null, 2)}`);
|
||||
|
||||
// Id2MetaStandardVerify 返回结果:
|
||||
// - Code: 200 表示请求成功
|
||||
// - ResultObject.BizCode: 核验结果 (数字类型)
|
||||
// - 1: 核验一致 (成功)
|
||||
// - 2: 核验不一致 (失败)
|
||||
// - 3: 查无记录 (失败)
|
||||
if (response.Code === 'OK' || response.Code === '200' || response.Code === 200) {
|
||||
const bizCode = response.ResultObject?.BizCode;
|
||||
// BizCode 可能是数字或字符串,都要兼容
|
||||
const isMatch = bizCode === 1 || bizCode === '1';
|
||||
|
||||
this.logger.log(`[AliyunKYC] [Level1] 2-factor ResultObject: BizCode=${bizCode}`);
|
||||
|
||||
if (isMatch) {
|
||||
this.logger.log(`[AliyunKYC] [Level1] 2-factor Verification SUCCESS for requestId: ${requestId}`);
|
||||
return {
|
||||
success: true,
|
||||
rawResponse: response,
|
||||
};
|
||||
} else {
|
||||
this.logger.warn(`[AliyunKYC] [Level1] 2-factor Verification FAILED (BizCode: ${bizCode})`);
|
||||
return {
|
||||
success: false,
|
||||
errorMessage: '身份信息验证失败',
|
||||
rawResponse: response,
|
||||
};
|
||||
}
|
||||
} else {
|
||||
this.logger.warn(`[AliyunKYC] [Level1] 2-factor API call FAILED - Code: ${response.Code}, Message: ${response.Message}`);
|
||||
return {
|
||||
success: false,
|
||||
errorMessage: '身份信息验证失败',
|
||||
rawResponse: response,
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.error(`[AliyunKYC] [Level1] 2-factor API call failed: ${error.message}`, error.stack);
|
||||
return {
|
||||
success: false,
|
||||
errorMessage: '实名认证服务暂时不可用',
|
||||
rawResponse: { error: error.message },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ========================================
|
||||
* 层级1: 实名认证 - 手机号三要素验证(备用)
|
||||
* ========================================
|
||||
* 验证姓名、身份证号和手机号是否匹配
|
||||
* 使用阿里云 Mobile3MetaDetailVerify API (手机号三要素核验详版)
|
||||
* 文档: https://help.aliyun.com/zh/id-verification/information-verification/developer-reference/esf1ff158mxowkk6
|
||||
*/
|
||||
async verifyIdCard(
|
||||
async verifyIdCardThreeFactor(
|
||||
realName: string,
|
||||
idCardNumber: string,
|
||||
phoneNumber: string,
|
||||
|
|
@ -138,12 +242,12 @@ export class AliyunKycProvider {
|
|||
// - 203: 手机号与姓名不一致,但与证件号一致
|
||||
// - 204: 其他不一致
|
||||
// - 301: 查无记录
|
||||
if (response.Code === 'OK' || response.Code === '200') {
|
||||
if (response.Code === 'OK' || response.Code === '200' || response.Code === 200) {
|
||||
const bizCode = response.ResultObject?.BizCode;
|
||||
const subCode = response.ResultObject?.SubCode;
|
||||
const isConsistent = response.ResultObject?.IsConsistent;
|
||||
// BizCode === '1' 表示校验一致(成功)
|
||||
const isMatch = bizCode === '1';
|
||||
// BizCode 可能是数字或字符串,都要兼容
|
||||
const isMatch = bizCode === 1 || bizCode === '1';
|
||||
|
||||
this.logger.log(`[AliyunKYC] [Level1] ResultObject: BizCode=${bizCode}, SubCode=${subCode}, IsConsistent=${isConsistent}`);
|
||||
|
||||
|
|
@ -486,6 +590,43 @@ export class AliyunKycProvider {
|
|||
|
||||
// ============ Mock 方法 (开发/测试环境) ============
|
||||
|
||||
private mockIdCardTwoFactorVerification(realName: string, idCardNumber: string): IdCardVerificationResult {
|
||||
this.logger.log('[AliyunKYC] Using mock ID card verification (2-factor)');
|
||||
|
||||
// 基本格式验证
|
||||
if (!realName || realName.length < 2) {
|
||||
return {
|
||||
success: false,
|
||||
errorMessage: '身份信息验证失败',
|
||||
rawResponse: { mock: true, reason: 'invalid_name' },
|
||||
};
|
||||
}
|
||||
|
||||
// 身份证号格式验证
|
||||
const idCardRegex = /^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/;
|
||||
if (!idCardRegex.test(idCardNumber)) {
|
||||
return {
|
||||
success: false,
|
||||
errorMessage: '身份信息验证失败',
|
||||
rawResponse: { mock: true, reason: 'invalid_id_card_format' },
|
||||
};
|
||||
}
|
||||
|
||||
// 校验码验证
|
||||
if (!this.validateIdCardChecksum(idCardNumber)) {
|
||||
return {
|
||||
success: false,
|
||||
errorMessage: '身份信息验证失败',
|
||||
rawResponse: { mock: true, reason: 'invalid_checksum' },
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
rawResponse: { mock: true, verifyTime: new Date().toISOString(), verifyType: '2-factor' },
|
||||
};
|
||||
}
|
||||
|
||||
private mockIdCardVerification(realName: string, idCardNumber: string, phoneNumber: string): IdCardVerificationResult {
|
||||
this.logger.log('[AliyunKYC] Using mock ID card verification (3-factor)');
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue