概述
实名认证系统采用插件化架构,每个认证通道(如阿里云二要素、三要素、人脸识别等)作为一个独立的插件,放置在 public/plugins/certification/ 目录下。插件通过实现统一的接口与系统交互,支持多通道并行、同步/异步模式、第三方回调等特性。
目录结构
public/plugins/certification/{plugin_name}/
├── {plugin_name}.php # 插件主文件(类定义)
└── manifest.json # 插件自描述文件
示例
public/plugins/certification/
├── manual/
│ ├── manual.php
│ └── manifest.json
├── alitwo/
│ ├── alitwo.php
│ └── manifest.json
├── edgeone/
│ ├── edgeone.php
│ └── manifest.json
└── threehc/
├── threehc.php
└── manifest.json
插件接口
所有插件必须实现 app\common\library\certification\CertificationPluginInterface 接口。
接口定义
interface CertificationPluginInterface
{
/** 插件标识(目录名) */
public static function getName(): string;
/** 提交认证 */
public function submit(SubmitParams $params): SubmitResult;
/** 查询认证结果 */
public function query(QueryParams $params): QueryResult;
/** 处理第三方回调 */
public function handleCallback(array $request): CallbackResult;
/** 获取插件自描述信息 */
public static function getManifest(): array;
}
参数类说明
SubmitParams(提交入参)
| 属性 | 类型 | 说明 |
|---|
| realName | string | 真实姓名 |
| idCard | string | 身份证号 |
| mobile | string | 用户手机号 |
| userId | int | 用户 ID |
| extra | array | 插件自定义字段(银行卡号、视频等) |
| config | array | 插件自定义配置(custom_config 解码后) |
| timeout | int | 超时时间,秒 |
SubmitResult(提交出参)
| 属性 | 类型 | 说明 |
|---|
| success | bool | 是否成功 |
| message | string | 提示信息 |
| mode | string | 'sync' \ | 'async' — 决定写入状态的方式 |
| status | string | 'passed' \ | 'rejected' \ | 'pending' |
| token | string | 业务防重号 |
| type | string | 'none' \ | 'qrcode' \ | 'redirect' \ | 'manual' |
| data | mixed | 前端需要的数据(URL、二维码等) |
| extra | array | 需要存入 DB extra 字段的自定义数据 |
| code | int | ErrorCode |
QueryParams(查询入参)
| 属性 | 类型 | 说明 |
|---|
| verifySn | string | 业务防重号 |
| status | string | 当前数据库状态 |
| name | string | 真实姓名 |
| idCardNo | string | 身份证号 |
| extra | array | 提交时存的 extra 数据 |
| config | array | 插件自定义配置 |
QueryResult(查询出参)
| 属性 | 类型 | 说明 |
|---|
| success | bool | 是否成功 |
| message | string | 提示信息 |
| status | string | 'passed' \ | 'rejected' \ | 'pending' |
| rejectReason | string | 驳回原因 |
| extra | array | 需要更新的 extra 数据 |
| code | int | ErrorCode |
CallbackResult(回调出参)
| 属性 | 类型 | 说明 |
|---|
| success | bool | 是否成功 |
| message | string | 提示信息 |
| status | string | 'passed' \ | 'rejected' |
| rejectReason | string | 驳回原因 |
| token | string | 用于匹配 Idv 记录 |
| code | int | ErrorCode |
错误码
class ErrorCode
{
const SUCCESS = 1; // 成功
const VALIDATE = 0; // 参数校验失败(前端可重新提交)
const REJECTED = -1; // 认证被拒绝
const UPSTREAM = -2; // 上游接口错误(可重试)
const SYSTEM = -3; // 系统异常
}
mode 字段说明
mode 字段决定控制器如何写入认证状态:
| mode | 含义 | 控制器行为 |
|---|
| sync | 同步验证 | 直接写入最终状态(passed/rejected) |
| async | 异步验证 | 先写入 pending,等待回调或轮询更新 |
典型场景:
- 同步:三要素核验、四要素核验、人脸比对、活体检测 — 调用上游 API 立即返回结果
- 异步:扫码认证、H5 跳转认证、人工审核 — 需要用户完成交互后才有结果
type 字段说明
type 字段控制前端行为:
| type | 前端行为 |
|---|
| none | 直接刷新页面 |
| qrcode | 显示二维码弹窗,用户扫码后轮询 |
| redirect | 跳转到第三方 URL |
| manual | 提示等待人工审核 |
插件开发步骤
1. 创建目录和文件
public/plugins/certification/myalitwo/
├── myalitwo.php
└── manifest.json
2. 编写插件类
<?php
namespace plugins\certification\myalitwo;
use app\common\library\certification\CertificationPluginInterface;
use app\common\library\certification\SubmitParams;
use app\common\library\certification\SubmitResult;
use app\common\library\certification\QueryParams;
use app\common\library\certification\QueryResult;
use app\common\library\certification\CallbackResult;
use app\common\library\certification\ErrorCode;
class MyalitwoPlugin implements CertificationPluginInterface
{
private array $config;
public function __construct(array $config = [])
{
$this->config = $config;
}
public static function getName(): string
{
return 'myalitwo';
}
public static function getManifest(): array
{
$file = __DIR__ . '/manifest.json';
return is_file($file) ? json_decode(file_get_contents($file), true) : [];
}
public function submit(SubmitParams $params): SubmitResult
{
$appCode = trim($this->config['app_code'] ?? '');
if (!$appCode) {
return SubmitResult::error('请先配置 AppCode', ErrorCode::SYSTEM);
}
$realName = $params->realName;
$idCard = $params->idCard;
if (!$realName) {
return SubmitResult::error('请输入真实姓名');
}
if (!$idCard) {
return SubmitResult::error('请输入身份证号码');
}
$result = $this->apiRequest($idCard, $realName, $appCode);
$passed = ($result['status'] === '01');
return SubmitResult::success($passed ? '实名认证通过' : '信息核验不匹配', [
'mode' => 'sync',
'status' => $passed ? 'passed' : 'rejected',
'token' => $result['traceId'] ?? uniqid('ALI_'),
'type' => 'none',
'extra' => ['trace_id' => $result['traceId'] ?? ''],
]);
}
public function query(QueryParams $params): QueryResult
{
if ($params->status === 'passed') {
return QueryResult::success('实名认证已通过', 'passed');
}
if ($params->status === 'rejected') {
return QueryResult::error('实名认证未通过', ErrorCode::REJECTED, ['status' => 'rejected']);
}
return QueryResult::error('认证处理中', ErrorCode::VALIDATE, ['status' => 'pending']);
}
public function handleCallback(array $request): CallbackResult
{
return CallbackResult::error('不支持回调', ErrorCode::SYSTEM);
}
private function apiRequest(string $idCard, string $name, string $appCode): array
{
// 调用上游 API 的 HTTP 请求
return [];
}
}
3. 编写 manifest.json
{
"name": "myalitwo",
"display_name": "我的阿里云二要素",
"version": "1.0.0",
"mode": "sync",
"callback": {
"required": false
},
"required_fields": ["real_name", "id_card"],
"optional_fields": [],
"extra_fields": [],
"config": {
"app_code": {
"title": "AppCode",
"type": "text",
"value": "",
"tip": "阿里云云市场购买的 AppCode"
}
}
}
4. 后台添加通道
在后台「实名认证 → 插件管理」中添加新通道,选择 myalitwo 插件,填入配置即可启用。
manifest.json 字段说明
{
"name": "插件标识(目录名,与插件类 getName() 一致)",
"display_name": "前台显示名称",
"version": "版本号",
"mode": "sync | async(默认模式)",
"callback": {
"required": true | false,
"method": "POST | GET",
"content_type": "application/json",
"params": ["token", "channel"]
},
"required_fields": ["real_name", "id_card"],
"optional_fields": ["front_image", "back_image", "handheld_image"],
"extra_fields": {
"mobile": {
"title": "手机号",
"type": "text",
"required": true,
"show_when": {"verify_type": ["mobile_three", "bankcard_four"]},
"tip": "用于三要素/四要素验证"
}
},
"config": {
"secret_id": {
"title": "SecretId",
"type": "text",
"value": "",
"tip": "API 密钥 ID"
}
}
}
extra_fields.show_when 说明
show_when 用于条件显示额外字段,根据 config 中其他配置项的值来决定是否显示该字段。当 config.verify_type 的值为 mobile_three 或 bankcard_four 时,显示手机号字段。
日志调试
插件日志写入 public/plugins/certification/{plugin_name}/logs/ 目录下,按日期分文件。开启 app_debug=true 时自动写入。
use app\common\service\PluginLogger;
// 写入调试日志(仅在 app_debug=true 时输出)
PluginLogger::write('myalitwo', '请求URL: ' . $url, 'debug');
// 强制写入(忽略调试模式开关)
PluginLogger::force('myalitwo', 'HTTP状态码: ' . $httpCode);
完整插件示例
参考现有插件:
| 插件 | 目录 | 特点 |
|---|
| 人工审核 | manual | 异步、人工审核、支持图片上传 |
| 阿里云二要素 | alitwo | 同步、姓名+身份证号核验 |
| 三要素/四要素 | threehc | 同步、银行卡核验、支持多要素 |
| 易付云 | edgeone | 混合模式、支持多种认证方式 |
数据库表结构
dd_idv_plugins(认证通道配置表)
| 字段 | 类型 | 说明 |
|---|
| id | int | 通道编号 |
| plugin | varchar(50) | 插件目录名 |
| vendor | varchar(50) | 厂商名称 |
| priority | tinyint | 优先级,越小越优先 |
| weight | tinyint | 负载权重 0-100 |
| timeout | smallint | 超时时间(秒) |
| free_times_total | int | 免费次数 |
| fee_after_free | int | 超出后每次费用(分) |
| daily_limit | int | 每日最大调用次数,0 不限 |
| status | enum | disabled/enabled |
| custom_config | text | 插件自定义配置(JSON) |
dd_user_idv(用户认证快照)
| 字段 | 类型 | 说明 |
|---|
| id | bigint | 主键 |
| user_id | bigint | 用户 ID |
| channel | varchar(20) | 认证通道(插件 ID) |
| status | enum | pending/passed/rejected/expired |
| name | varchar(64) | 真实姓名 |
| id_card_no | varchar(20) | 身份证号 |
| verify_sn | varchar(64) | 业务防重号 |
| reject_reason | varchar(255) | 驳回原因 |
| extra | text | 扩展字段(JSON) |
dd_user_idv_logs(认证流水)
| 字段 | 类型 | 说明 |
|---|
| id | bigint | 主键 |
| user_id | bigint | 用户 ID |
| channel | varchar(20) | 认证通道 |
| status | enum | 审核状态 |
| name | varchar(64) | 真实姓名 |
| id_card_no | varchar(20) | 身份证号 |
| reject_reason | varchar(255) | 驳回原因 |
| ip | varchar(45) | 提交 IP |
| ua | text | 提交 UA |
| created_at | datetime | 提交时间 |
请求流程
用户提交认证信息
│
▼
Realname::submit()
│
├─ ChannelSelector::selectChannel() → 选择可用通道
├─ 检查日限额、免费次数、扣费
├─ $plugin->submit($params) → 调用插件
│
├─ mode=sync → 写入最终状态(passed/rejected)
└─ mode=async → 写入 pending,等待回调/轮询
用户查询认证结果
│
▼
Realname::query()
│
├─ 已 passed → 直接返回
├─ 已 rejected → 直接返回(含驳回原因)
└─ 其他 → 调用插件 query() 查询上游
第三方服务完成认证
│
▼
Realname::callback()
│
├─ $plugin->handleCallback() → 解析结果
├─ 更新 Idv 状态
└─ 更新 user.verification.idv
chat_bubble_outline 评论