#!/usr/bin/env python3 """ 阿里云实人认证 (CloudAuth) 管理工具 功能: 认证场景管理、发起认证、查询结果、套餐余额查询 依赖: pip install alibabacloud-cloudauth20190307 alibabacloud-bssopenapi20171214 """ import argparse import json import sys import os import uuid def get_client(): from alibabacloud_cloudauth20190307.client import Client from alibabacloud_tea_openapi.models import Config ak_id = os.environ.get('ALIBABA_CLOUD_ACCESS_KEY_ID', '') ak_secret = os.environ.get('ALIBABA_CLOUD_ACCESS_KEY_SECRET', '') endpoint = os.environ.get('ALIYUN_KYC_ENDPOINT', 'cloudauth.aliyuncs.com') if not ak_id or not ak_secret: print('ERROR: Set ALIBABA_CLOUD_ACCESS_KEY_ID and ALIBABA_CLOUD_ACCESS_KEY_SECRET') sys.exit(1) config = Config( access_key_id=ak_id, access_key_secret=ak_secret, endpoint=endpoint, ) return Client(config) # ──────────────── 认证场景 ──────────────── def describe_verify_setting(args): """查询认证场景配置 (通过探测 API 检查服务状态)""" client = get_client() from alibabacloud_cloudauth20190307.models import InitFaceVerifyRequest scene_id = args.scene_id or os.environ.get('ALIYUN_KYC_SCENE_ID', '') try: # Probe service via InitFaceVerify with dummy data req = InitFaceVerifyRequest( scene_id=int(scene_id) if scene_id else 0, outer_order_no=str(uuid.uuid4()), product_code='ID_PRO', cert_type='IDENTITY_CARD', cert_name='probe', cert_no='000000000000000000', ) resp = client.init_face_verify(req) body = resp.body print(f'\n📋 实人认证服务状态\n') print(f' 场景ID: {scene_id or "(未设置)"}') print(f' 产品码: ID_PRO (人脸活体检测)') print(f' 请求ID: {body.request_id}') print(f' 状态: ✅ 服务可用 (权限正常)') if body.code and body.code != '200': print(f' 探测响应: {body.code} - {body.message or ""}') except Exception as e: err = str(e) if 'NoPermission' in err or '403' in err: print(f'\n❌ 权限不足: RAM 子账号缺少 CloudAuth 权限') print(f' 请添加 AliyunYundunCloudAuthFullAccess 策略') else: print(f'ERROR: {e}') # ──────────────── 发起认证 ──────────────── def init_face_verify(args): """发起人脸活体检测认证""" client = get_client() from alibabacloud_cloudauth20190307.models import InitFaceVerifyRequest scene_id = args.scene_id or os.environ.get('ALIYUN_KYC_SCENE_ID', '') outer_order_no = args.order_no or str(uuid.uuid4()) req = InitFaceVerifyRequest( scene_id=int(scene_id), outer_order_no=outer_order_no, product_code='ID_PRO', cert_type='IDENTITY_CARD', cert_name=args.name, cert_no=args.id_number, ) try: resp = client.init_face_verify(req) body = resp.body if body.code == '200' or body.code == 200: result = body.result_object print(f'✅ 认证已发起') print(f' CertifyId: {result.certify_id}') print(f' CertifyUrl: {result.certify_url}') print(f' OrderNo: {outer_order_no}') else: print(f'ERROR: {body.code} - {body.message}') except Exception as e: print(f'ERROR: {e}') def describe_face_verify(args): """查询认证结果""" client = get_client() from alibabacloud_cloudauth20190307.models import DescribeFaceVerifyRequest req = DescribeFaceVerifyRequest( scene_id=int(args.scene_id or os.environ.get('ALIYUN_KYC_SCENE_ID', '')), certify_id=args.certify_id, ) try: resp = client.describe_face_verify(req) body = resp.body if body.code == '200' or body.code == 200: result = body.result_object passed = result.passed == 'T' print(f'\n📋 认证结果') print(f' CertifyId: {args.certify_id}') print(f' 通过: {"✅ 是" if passed else "❌ 否"}') print(f' SubCode: {result.sub_code}') if result.material_info: print(f' 材料信息: {result.material_info}') else: print(f'ERROR: {body.code} - {body.message}') except Exception as e: print(f'ERROR: {e}') # ──────────────── 身份二要素核验 ──────────────── def verify_identity(args): """身份证二要素核验 (姓名+身份证号)""" client = get_client() from alibabacloud_cloudauth20190307.models import Id2MetaVerifyRequest req = Id2MetaVerifyRequest( param_type='normal', user_name=args.name, identity_card_number=args.id_number, ) try: resp = client.id_2meta_verify(req) body = resp.body if body.code == '200' or body.code == 200: result = body.result_object bizCode = result.biz_code if result else 'UNKNOWN' passed = bizCode == '1' print(f'\n📋 身份二要素核验') print(f' 姓名: {args.name}') print(f' 身份证: {args.id_number[:6]}****{args.id_number[-4:]}') print(f' 结果: {"✅ 一致" if passed else "❌ 不一致"}') print(f' BizCode: {bizCode}') else: print(f'ERROR: {body.code} - {body.message}') except Exception as e: print(f'ERROR: {e}') # ──────────────── 套餐余额查询 ──────────────── def query_quota(args): """查询实人认证套餐余额 (通过 BSS API)""" from alibabacloud_bssopenapi20171214.client import Client as BssClient from alibabacloud_bssopenapi20171214.models import QueryResourcePackageInstancesRequest from alibabacloud_tea_openapi.models import Config ak_id = os.environ.get('ALIBABA_CLOUD_ACCESS_KEY_ID', '') ak_secret = os.environ.get('ALIBABA_CLOUD_ACCESS_KEY_SECRET', '') config = Config( access_key_id=ak_id, access_key_secret=ak_secret, endpoint='business.aliyuncs.com', ) client = BssClient(config) try: req = QueryResourcePackageInstancesRequest(page_num=1, page_size=100) resp = client.query_resource_package_instances(req) body = resp.body instances = body.data.instances.instance if body.data and body.data.instances else [] # Filter cloudauth packages auth_pkgs = [i for i in instances if 'cloudauth' in (i.to_map().get('CommodityCode', '') or '').lower()] if not auth_pkgs: print('\n📋 未找到实人认证相关套餐') return print(f'\n📋 实人认证套餐余额 (共 {len(auth_pkgs)} 个)\n') print(f'{"套餐名称":<30} {"总量":<10} {"剩余":<10} {"到期日":<22} {"状态"}') print('-' * 90) for pkg in auth_pkgs: m = pkg.to_map() name = m.get('Remark', '') or m.get('PackageType', '') total = m.get('TotalAmount', '0') remain = m.get('RemainingAmount', '0') unit = m.get('TotalAmountUnit', '次') expiry = (m.get('ExpiryTime', '') or '')[:10] status = m.get('Status', '') status_str = '✅ 可用' if status == 'Available' else f'❌ {status}' print(f'{name:<30} {total}{unit:<8} {remain}{unit:<8} {expiry:<22} {status_str}') except Exception as e: err = str(e) if 'NotAuthorized' in err: print(f'\n❌ 权限不足: 需要 AliyunBSSReadOnlyAccess 策略') else: print(f'ERROR: {e}') # ──────────────── CLI ──────────────── def main(): parser = argparse.ArgumentParser(description='阿里云实人认证管理工具') sub = parser.add_subparsers(dest='command') # 场景查询 p = sub.add_parser('list-scenes', help='查询认证场景配置') p.add_argument('--scene-id', help='场景ID (留空查全部)') # 发起人脸认证 p = sub.add_parser('init-face', help='发起人脸活体检测') p.add_argument('--name', required=True, help='真实姓名') p.add_argument('--id-number', required=True, help='身份证号') p.add_argument('--scene-id', help='场景ID (默认取环境变量)') p.add_argument('--order-no', help='业务订单号 (默认自动生成)') # 查询认证结果 p = sub.add_parser('query-face', help='查询人脸认证结果') p.add_argument('--certify-id', required=True, help='认证ID') p.add_argument('--scene-id', help='场景ID') # 套餐余额 p = sub.add_parser('quota', help='查询套餐余额') # 身份二要素核验 p = sub.add_parser('verify-id', help='身份证二要素核验') p.add_argument('--name', required=True, help='真实姓名') p.add_argument('--id-number', required=True, help='身份证号') args = parser.parse_args() if not args.command: parser.print_help() return cmds = { 'list-scenes': describe_verify_setting, 'init-face': init_face_verify, 'query-face': describe_face_verify, 'verify-id': verify_identity, 'quota': query_quota, } cmds[args.command](args) if __name__ == '__main__': main()