library_books 思博业务

实名认证插件开发文档

概述

实名认证系统采用插件化架构,每个认证通道(如阿里云二要素、三要素、人脸识别等)作为一个独立的插件,放置在 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(提交入参)

属性类型说明
realNamestring真实姓名
idCardstring身份证号
mobilestring用户手机号
userIdint用户 ID
extraarray插件自定义字段(银行卡号、视频等)
configarray插件自定义配置(custom_config 解码后)
timeoutint超时时间,秒

SubmitResult(提交出参)

属性类型说明
successbool是否成功
messagestring提示信息
modestring'sync' \'async' — 决定写入状态的方式
statusstring'passed' \'rejected' \'pending'
tokenstring业务防重号
typestring'none' \'qrcode' \'redirect' \'manual'
datamixed前端需要的数据(URL、二维码等)
extraarray需要存入 DB extra 字段的自定义数据
codeintErrorCode

QueryParams(查询入参)

属性类型说明
verifySnstring业务防重号
statusstring当前数据库状态
namestring真实姓名
idCardNostring身份证号
extraarray提交时存的 extra 数据
configarray插件自定义配置

QueryResult(查询出参)

属性类型说明
successbool是否成功
messagestring提示信息
statusstring'passed' \'rejected' \'pending'
rejectReasonstring驳回原因
extraarray需要更新的 extra 数据
codeintErrorCode

CallbackResult(回调出参)

属性类型说明
successbool是否成功
messagestring提示信息
statusstring'passed' \'rejected'
rejectReasonstring驳回原因
tokenstring用于匹配 Idv 记录
codeintErrorCode

错误码

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_threebankcard_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(认证通道配置表)

字段类型说明
idint通道编号
pluginvarchar(50)插件目录名
vendorvarchar(50)厂商名称
prioritytinyint优先级,越小越优先
weighttinyint负载权重 0-100
timeoutsmallint超时时间(秒)
free_times_totalint免费次数
fee_after_freeint超出后每次费用(分)
daily_limitint每日最大调用次数,0 不限
statusenumdisabled/enabled
custom_configtext插件自定义配置(JSON)

dd_user_idv(用户认证快照)

字段类型说明
idbigint主键
user_idbigint用户 ID
channelvarchar(20)认证通道(插件 ID)
statusenumpending/passed/rejected/expired
namevarchar(64)真实姓名
id_card_novarchar(20)身份证号
verify_snvarchar(64)业务防重号
reject_reasonvarchar(255)驳回原因
extratext扩展字段(JSON)

dd_user_idv_logs(认证流水)

字段类型说明
idbigint主键
user_idbigint用户 ID
channelvarchar(20)认证通道
statusenum审核状态
namevarchar(64)真实姓名
id_card_novarchar(20)身份证号
reject_reasonvarchar(255)驳回原因
ipvarchar(45)提交 IP
uatext提交 UA
created_atdatetime提交时间

请求流程

用户提交认证信息
      │
      ▼
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 评论 0

edit 发表评论

目录