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:
hailin 2025-12-25 09:46:17 -08:00
parent 7a1e789f4d
commit 4ec4df14b5
1 changed files with 146 additions and 5 deletions

View File

@ -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)');