asign/openapi-sdk-php 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

asign/openapi-sdk-php

Composer 安装命令:

composer require asign/openapi-sdk-php

包简介

爱签(AiSign)电子签约开放平台 PHP SDK - 模块化、结构清晰的 API 封装类库

README 文档

README

PHP Version PHPUnit PSR-12 License: MIT

一个结构清晰、功能模块化、PSR-12 规范的爱签(AiSign)开放平台 PHP SDK。封装了实名认证、用户管理、印章管理、文件上传、合同签署、模板管理、存证服务、活体认证等全部 API 接口,提供统一的配置入口和异常处理机制。

目录

项目简介

本 SDK 是对爱签开放平台 V3.0 OpenAPI 的完整 PHP 封装。参考官方接口文档和已有 ACsign.class.php 示例代码,按照功能领域拆分为多个独立的服务类,通过 门面模式(Facade) 提供统一入口,具有以下设计目标:

  • 模块化:按功能领域拆分,每个服务类职责单一
  • 易维护:完整的 PHPDoc 注释、严格的类型声明、统一的错误处理
  • 可测试:所有核心逻辑均可通过 Mock 进行单元测试
  • 零依赖:除 PHP 内置扩展外不依赖任何第三方 HTTP 客户端库

功能特性

功能模块服务类主要能力
实名认证AuthService个人网页认证、三要素认证、人脸活体认证、企业法人认证、验证码管理
用户管理UserService个人/企业用户增删查改、手机号修改、短信验证码、OCR 识别、人脸比对
印章管理SealService印章创建/修改/删除/查询、法人章管理、默认章设置(完整 CRUD)
文件服务FileService获取上传 URL、PUT 直传云存储、一键上传
合同签署ContractService合同创建、签署方添加、状态查询、预览、密码签署、合同下载(多格式)
模板管理TemplateService合同模板上传、下载、通用模板接口
存证服务EvidenceService合同区块链存证、下载存证证书、下载区块链存证报告
活体认证VerifyService人脸照片 + 活体视频多文件上传比对
签名工具BaseClientRSA 签名/验签、Content-MD5 计算、回调验签

系统要求

依赖最低版本说明
PHP≥ 7.4需要类型声明和 nullable 类型支持
ext-curl*HTTP 请求
ext-openssl*RSA 签名/验签
ext-json*JSON 编解码

注意:项目使用 declare(strict_types=1),所有文件遵循严格类型模式。

安装步骤

通过 Composer 安装(推荐)

composer require asign/openapi-sdk-php

安装后,在入口文件中引入 autoload:

require_once __DIR__ . '/vendor/autoload.php';

手动安装

如果无法使用 Composer,可手动注册 PSR-4 自动加载器:

spl_autoload_register(function (string $class) {
    $prefix = 'Asign\\';
    $baseDir = __DIR__ . '/src/';
    $len = strlen($prefix);
    if (strncmp($prefix, $class, $len) !== 0) {
        return;
    }
    $relativeClass = substr($class, $len);
    $file = $baseDir . str_replace('\\', '/', $relativeClass) . '.php';
    if (file_exists($file)) {
        require $file;
    }
});

快速开始

以下是最简示例——初始化客户端并查询用户信息:

<?php

require_once __DIR__ . '/vendor/autoload.php';

use Asign\SignClient;
use Asign\SignConfig;
use Asign\SignException;

// 1. 创建配置
$config = new SignConfig([
    'appId'     => 'your_app_id',
    'apiSecret' => 'your_rsa_private_key',
    // 使用测试环境(正式环境使用 SignConfig::DOMAIN_PROD)
    'domain'    => SignConfig::DOMAIN_PREV,
]);

// 2. 创建客户端(Facade 入口)
$client = new SignClient($config);

// 3. 调用业务接口
try {
    // 查询用户信息
    $userInfo = $client->user->getUser('13900001111');
    echo '用户姓名: ' . $userInfo['name'] . "\n";

    // 查询印章列表
    $seals = $client->seal->getUserSeals('13900001111');
    echo '印章数量: ' . count($seals['seals'] ?? []) . "\n";

} catch (SignException $e) {
    echo sprintf("API 错误 [%s]: %s\n", $e->getErrorCode(), $e->getMessage());
    // 可通过 getResponseData() 获取原始响应用于排查
    var_dump($e->getResponseData());
} catch (\Exception $e) {
    echo "系统异常: " . $e->getMessage() . "\n";
}

配置说明

SignConfig 参数详解

SignConfig 是全局配置类,支持三种初始化方式:

参数类型必填默认值说明
appIdstring''爱签开放平台分配的应用 ID
apiSecretstring''RSA 私钥(不含 -----BEGIN/END PRIVATE KEY----- 头尾标记)
domainstringhttps://oapi.asign.cnAPI 域名,正式环境默认值
notifyUrlstring''回调通知地址
publicKeystring''RSA 公钥(用于验证回调签名,不含头尾标记)
timeoutint30HTTP 请求超时秒数(自动转为 int)
signExpireMsint600000签名有效期毫秒数(默认 10 分钟)

三种初始化方式

方式 A:数组配置(推荐)

$config = new SignConfig([
    'appId'       => 'your_app_id',
    'apiSecret'   => 'your_private_key',
    'domain'      => SignConfig::DOMAIN_PREV,
    'notifyUrl'   => 'https://your-site.com/callback',
    'timeout'     => 30,
]);

方式 B:从环境变量

设置环境变量 ASIGN_APP_IDASIGN_API_SECRETASIGN_DOMAINASIGN_NOTIFY_URLASIGN_PUBLIC_KEY,然后:

$config = SignConfig::fromEnv();

方式 C:从 $GLOBALS 数组(兼容旧版 ACsign)

$GLOBALS['asign'] = [
    'appId'     => 'your_app_id',
    'apiSecret' => 'your_private_key',
    'domian'    => 'https://prev.asign.cn', // 注意:兼容旧版拼写
];
$config = SignConfig::fromGlobals($GLOBALS);

环境切换

// 切换到测试环境
$config->usePrevDomain();

// 切换回正式环境
$config->useProdDomain();

环境域名常量

常量说明
SignConfig::DOMAIN_PRODhttps://oapi.asign.cn正式环境
SignConfig::DOMAIN_PREVhttps://prev.asign.cn测试环境

API 调用示例

以下示例假定已创建 $client = new SignClient($config)

实名认证服务(AuthService)

// 1. 获取个人实名认证网页 URL
$result = $client->auth->personIdentifyUrl([
    'account' => '13900001111',
    'name'    => '张三',
    'idcard'  => '330100199001011234',
    'mobile'  => '13900001111',
]);
echo '认证链接: ' . $result['authUrl'];

// 2. 个人运营商三要素认证
$result = $client->auth->personMobile3([
    'name'   => '张三',
    'idcard' => '330100199001011234',
    'mobile' => '13900001111',
]);

// 3. 企业法人三要素认证
$result = $client->auth->companyMobile3([
    'account'     => 'company001',
    'name'        => '李四',
    'idcard'      => '330100198001015678',
    'mobile'      => '13900002222',
    'creditCode'  => '91330100MA2XXXXXXX',
    'companyName' => '测试科技有限公司',
]);

// 4. 重发验证码
$result = $client->auth->captchaResend('SN001');

// 5. 校验验证码
$result = $client->auth->captchaVerify('SN001', '123456');

用户管理服务(UserService)

// 新增个人用户
$result = $client->user->addPersonalUserV2([
    'account' => '13900001111',
    'name'    => '张三',
    'idcard'  => '330100199001011234',
    'mobile'  => '13900001111',
]);

// 新增企业用户
$result = $client->user->addEnterpriseUserV2([
    'account'     => 'company001',
    'companyName' => '测试科技有限公司',
    'creditCode'  => '91330100MA2XXXXXXX',
    'legalName'   => '王五',
    'legalIdcard' => '330100198501011234',
    'legalMobile' => '13900003333',
]);

// 查询用户(account 必填,idcard/creditCode 可选)
$user = $client->user->getUser('13900001111');
$user = $client->user->getUser('13900001111', '330100199001011234');

// 发送短信验证码(修改手机号前)
$result = $client->user->sendCode('13900001111');
$codeToken = $result['codeToken'];

// 通过验证码修改手机号
$result = $client->user->modifyMobileByCode(
    '13900001111',   // 原账号
    '13900002222',   // 新手机号
    '654321',        // 短信验证码
    $codeToken       // 验证码 token
);

// OCR 身份证识别(上传图片文件)
$result = $client->user->ocrIdentify('/path/to/idcard.jpg', 'front');

// 人脸照片比对
$result = $client->user->faceCompare(
    '张三',
    '330100199001011234',
    'https://example.com/photo.jpg'  // URL 和 image 二选一
);

印章管理服务(SealService)

// 创建印章(上传印章图片)
$result = $client->seal->createSeal('/path/to/seal.png', [
    'account'   => '13900001111',
    'sealName'  => '合同专用章',
    'sealNo'    => 'SEAL_001',
    'isDefault' => '1',     // '1'=默认章 '0'=非默认
    'scaling'   => 0.85,    // 缩放比例 0~1
]);

// 创建法人章
$result = $client->seal->createLegalPersonSeal('legal001', 'company001');

// 查询印章列表
$seals = $client->seal->getUserSeals('13900001111');
foreach ($seals['seals'] ?? [] as $seal) {
    echo "印章: {$seal['sealName']} ({$seal['sealNo']})\n";
}

// 修改印章
$result = $client->seal->modifySeal(
    '13900001111',    // account
    '',               // image(空字符串表示不更换图片)
    '1',              // isDefault
    '更新后的印章名',  // sealName
    'SEAL_001',       // sealNo
    0.9               // scaling
);

// 设置默认印章
$result = $client->seal->setDefaultSeal('13900001111', 'SEAL_001');
// 传空字符串恢复系统默认章
$result = $client->seal->setDefaultSeal('13900001111', '');

// 删除印章
$result = $client->seal->removeSeal('13900001111', 'SEAL_002');

文件服务(FileService)

// 方式 1:分步上传(适用于自定义上传流程)
// 第一步:获取预签名上传 URL
$result = $client->file->getUploadUrl(
    '/path/to/document.pdf',
    '我的文档',       // 显示名称
    null              // 扩展名(默认从文件路径提取)
);
$uploadUrl = $result['uploadUrl'];

// 第二步:PUT 直传文件到云存储
$success = $client->file->uploadToUrl($uploadUrl, '/path/to/document.pdf');

// 方式 2:一键上传(推荐)
$result = $client->file->upload('/path/to/document.pdf', '我的文档');

合同签署服务(ContractService)

// 1. 创建合同(上传 PDF 合同文件)
$contract = $client->contract->createContract([
    'contractNo'   => 'CONTRACT_' . time(),
    'contractName' => '销售合同',
    'contractType' => 'SALES',
    'bizType'      => 'B2B',
], '/path/to/contract.pdf');

// 2. 添加签署方(指定签章位置)
$result = $client->contract->addSigner([
    'contractNo' => 'CONTRACT_001',
    'account'    => '13900001111',
    'signStrategyList' => [[
        'attachNo'     => 1,
        'locationMode' => '2',   // 2=坐标签章
        'signPage'     => 1,     // 签约页码
        'signX'        => 0.15,  // X 坐标比例 0~1
        'signY'        => 0.1,   // Y 坐标比例 0~1
    ]],
]);

// 3. 查询合同状态
$status = $client->contract->getContractStatus('CONTRACT_001');
echo '合同状态: ' . $status['status'];   // DRAFT / SIGNING / COMPLETED

// 4. 查询合同详细信息
$detail = $client->contract->getContract('CONTRACT_001');

// 5. 预览合同(获取签署预览 URL)
$preview = $client->contract->previewContract('CONTRACT_001', '13900001111');
echo '预览链接: ' . $preview['previewUrl'];

// 6. 密码签署
$result = $client->contract->passSign('CONTRACT_001', '13900001111', 'sign_password');

// 7. 下载合同(支持多种格式)
// downloadFileType: 1=PDF, 2=多个PNG+PDF, 3=分页PNG压缩包+PDF
//                   4=单张图片(无PDF), 5=所有分页图片(无PDF)
$download = $client->contract->downloadContract('CONTRACT_001', '/save/contract.pdf', 1);

// 8. 按附件名下载
$download = $client->contract->downloadByAttachName('CONTRACT_001', ['附件1', '附件2'], '/save/', 1);

// 9. 将 Base64 内容保存到本地文件
$result = $client->contract->saveBase64ToFile($download['content'], '/save/contract.pdf');

模板服务(TemplateService)

// 上传合同模板
$result = $client->template->uploadTemplate(
    '销售合同模板',           // 模板名称
    'TPL_001',               // 模板编号(自定义唯一标识)
    '/path/to/template.pdf'  // 模板文件路径(PDF 或 Word)
);

// 通用模板上传接口
$result = $client->template->templateUpload('/path/to/file.docx', 'TPL_IDENT');

// 下载模板
$result = $client->template->downloadTemplate('TPL_001', '/save/template.pdf');
// $result['content'] 包含 base64 编码的模板文件内容

存证服务(EvidenceService)

// 1. 合同存证(固化到区块链)
$result = $client->evidence->saveEvidence('CONTRACT_001');
echo '存证编号: ' . $result['preserveNo'];
echo '交易哈希: ' . $result['txHash'];

// 2. 下载存证证书(通过合同编号)
$result = $client->evidence->downloadWithCert('CONTRACT_001', '', '/save/cert.pdf');

// 或通过存证编号下载
$result = $client->evidence->downloadWithCert('', 'PRE_001', '/save/cert.pdf');

// 3. 下载区块链存证报告
$result = $client->evidence->downloadChainReport('CONTRACT_001');
// $result['content'] 包含 base64 编码的报告内容

活体认证服务(VerifyService)

// 个人活体人脸比对(上传照片 + 活体视频)
$result = $client->verify->verifyPersonFace3(
    '/path/to/face.jpg',    // 人脸照片(jpg/png,≤3M)
    '/path/to/video.mp4'    // 活体视频(mp4/avi/flv,≤8M)
);

echo '比对结果: ' . $result['result'];        // pass / fail
echo '相似度: ' . $result['similarity'] . '%';
echo '活体分数: ' . $result['liveScore'];

异常处理指南

异常体系

所有 SDK 错误均通过 SignException 抛出(继承自 \RuntimeException):

错误类型errorCode 示例说明
网络错误CURL_ERRcURL 请求失败
响应异常EMPTY_RESPONSEAPI 返回空响应
解析错误JSON_PARSE_ERRJSON 反序列化失败
文件错误FILE_NOT_FOUND, MD5_ERR, FILE_READ_ERR文件操作相关
签名错误RSA_KEY_ERR, RSA_SIGN_ERRRSA 签名/验签
业务错误API 原始 code(如 200001爱签平台返回的错误

推荐处理模式

use Asign\SignException;

try {
    $result = $client->user->getUser('13900001111');
} catch (SignException $e) {
    // 获取错误码和消息
    $code = $e->getErrorCode();
    $msg  = $e->getMessage();

    // 获取原始 API 响应数据进行详细排查
    $rawResponse = $e->getResponseData();

    // 根据错误码分类处理
    switch ($code) {
        case '200020':
            // 用户不存在 - 引导注册
            break;
        case '200110':
            // 签署密码错误 - 提示重试
            break;
        case 'CURL_ERR':
            // 网络异常 - 重试或告警
            break;
        default:
            // 记录日志并通知
            error_log("Asign API Error [{$code}]: {$msg}");
    }
} catch (\Exception $e) {
    // 处理非 SDK 异常
    error_log("System Error: " . $e->getMessage());
}

SignException 常用方法

方法返回值说明
getErrorCode()stringAPI 返回的错误码
getMessage()string格式为 [ERROR_CODE] 错误描述
getCode()intHTTP 状态码
getResponseData()?arrayAPI 原始响应数组(用于深入排查)
getPrevious()?\Throwable上一个异常(链式追踪)

运行测试

安装测试依赖

composer install

执行测试

# 运行全部测试套件
./vendor/bin/phpunit

# 带详细输出
./vendor/bin/phpunit --verbose

# 生成代码覆盖率报告(HTML 格式)
./vendor/bin/phpunit --coverage-html coverage/

# 运行特定测试文件
./vendor/bin/phpunit tests/ContractServiceTest.php

# 运行特定测试方法
./vendor/bin/phpunit --filter testPassSignSuccess

PHPUnit 配置

项目使用 phpunit.xml 配置(PHPUnit 9.x),关键配置项:

配置项说明
bootstraptests/bootstrap.php测试引导文件(自动处理 PSR-4 加载)
colorstrue彩色输出
stopOnFailurefalse遇错不终止,完整运行
beStrictAboutTestsThatDoNotTestAnythingtrue严格检查无断言的测试方法

测试策略

所有测试用例不发起真实 HTTP 请求,采用 Mock 策略

  • 通过 PHPUnit\MockObject 模拟 BaseClientpostpostWithFileputRawcontentMd5 等方法
  • 每个服务类的测试覆盖成功场景异常场景(如参数错误、用户不存在、密码错误等)
  • 使用 @dataProvider 数据供给器覆盖多组错误码和参数组合
  • 文件上传测试使用 createTempFile() 创建临时文件并在 tearDown() 中自动清理
  • 通过 ReflectionMethod 测试 protected 方法(如 rsaSignfilterEmpty

测试覆盖统计

测试文件测试方法数覆盖范围
SignConfigTest9配置创建(数组/全局/环境)、域名常量、环境切换、超时类型转换
SignExceptionTest8异常构造(完整/最简)、fromApiResponse(message/msg/缺省)、ResponseData
BaseClientTest11时间戳、Content-MD5、签名确定性/唯一性、RSA 验签、filterEmpty 递归
AuthServiceTest8个人认证(URL/三要素)、企业认证、验证码重发/校验
UserServiceTest12个人/企业新增、查询(仅account/带idcard)、修改手机、验证码、OCR、人脸比对
SealServiceTest8创建印章/法人章、查询列表/空列表、修改、设置默认、删除/不存在
FileServiceTest8获取上传URL、自定义文件名、PUT直传、一键上传、uploadUrl缺失
ContractServiceTest12创建合同、添加签署方、状态/详情查询、预览、密码签署/错误、下载、saveBase64
TemplateServiceTest5模板上传(两种接口)、下载、模板不存在
EvidenceServiceTest7存证、按contractNo/preserveNo下载证书、下载链报告、各类不存在
VerifyServiceTest4活体比对成功、认证失败、多组质量错误
SignClientTest5全服务初始化验证、共享BaseClient、环境配置、getBaseClient/getConfig

代码审查报告

基于对 src/tests/ 全部源代码的全面审查,以下是主要发现:

架构设计 ✅

  • 门面模式SignClient 统一入口,所有服务共享单一 BaseClient 实例,避免重复初始化
  • 关注点分离:配置(SignConfig)→ 通信(BaseClient)→ 业务(Service 类)三层清晰
  • 命名空间规范:PSR-4 映射 Asign\src/,可与任何 Composer 项目集成

PHPDoc 注释完整性 ✅

  • 所有公开方法均标注 @param@return@throws
  • 数组参数使用 array<string, mixed> 形状标注
  • 类级别注释说明了模块职责和核心设计决策

错误处理一致性 ✅

  • 统一使用 SignException(继承 \RuntimeException),包含 errorCoderesponseData
  • fromApiResponse() 兼容 messagemsg 两种字段名
  • cURL 错误、JSON 解析错误、文件操作错误均被捕获并转换为异常
  • 所有 HTTP 请求均检查返回值和错误信息

PSR-12 规范遵循度 ✅

  • 所有文件使用 declare(strict_types=1)
  • 4 空格缩进,左大括号同行
  • 类型声明完整(属性类型、参数类型、返回类型)
  • 运算符两侧空格,方法间空行分隔

改进建议 ⚠️

类别建议优先级
安全性BaseClientCURLOPT_SSL_VERIFYPEER 硬编码为 false,建议在 SignConfig 中增加 sslVerify 配置项
可维护性BaseClientrequestrequestWithFilerequestWithFiles 三个方法中存在签名头部构建的代码重复,可提取为 buildSignedRequest() 统一方法
健壮性可考虑增加 HTTP 请求失败时的自动重试机制(指数退避),尤其对 CURL_ERR 类的瞬态网络错误
兼容性SignConfig::fromGlobals()domian 拼写(非 domain)是为了兼容旧版 ACsign.class.php 的遗留拼写错误,已通过注释标注已知

目录结构概览

project/
├── src/                          # 源代码目录
│   ├── SignConfig.php            # 全局配置类(域名、密钥、超时等)
│   ├── SignException.php         # 统一异常类(封装 API 错误码和响应数据)
│   ├── BaseClient.php            # 基础请求客户端(签名、HTTP、响应解析)
│   ├── AuthService.php           # 实名认证服务
│   ├── UserService.php           # 用户管理服务
│   ├── SealService.php           # 印章管理服务
│   ├── FileService.php           # 文件服务(上传 URL、PUT 直传)
│   ├── ContractService.php       # 合同签署服务
│   ├── TemplateService.php       # 模板服务
│   ├── EvidenceService.php       # 存证服务
│   ├── VerifyService.php         # 活体认证/验证服务
│   └── SignClient.php            # 统一入口类(Facade 门面)
├── tests/                        # 测试用例目录
│   ├── bootstrap.php             # PHPUnit 引导文件(PSR-4 自动加载)
│   ├── TestCase.php              # 测试基类(Mock 工具、临时文件管理)
│   ├── SignConfigTest.php        # 配置类测试
│   ├── SignExceptionTest.php     # 异常类测试
│   ├── BaseClientTest.php        # 基础客户端测试(签名、验签、filterEmpty)
│   ├── AuthServiceTest.php       # 实名认证服务测试
│   ├── UserServiceTest.php       # 用户管理服务测试
│   ├── SealServiceTest.php       # 印章管理服务测试
│   ├── FileServiceTest.php       # 文件服务测试(含边界条件)
│   ├── ContractServiceTest.php   # 合同签署服务测试
│   ├── TemplateServiceTest.php   # 模板服务测试
│   ├── EvidenceServiceTest.php   # 存证服务测试
│   ├── VerifyServiceTest.php     # 活体认证服务测试
│   └── SignClientTest.php        # 统一入口测试
├── composer.json                 # Composer 配置(命名空间映射、依赖声明)
├── phpunit.xml                   # PHPUnit 配置(测试套件、覆盖率)
├── demo.php                      # 完整使用示例
└── README.md                     # 项目文档(本文件)

贡献指南

欢迎提交 Issue 和 Pull Request!

开发环境搭建

# 克隆项目
git clone <repository-url>
cd asign-openapi-sdk-php

# 安装依赖
composer install

# 运行测试确保一切正常
./vendor/bin/phpunit

代码规范

  • 遵循 PSR-12 编码规范
  • 所有 PHP 文件以 declare(strict_types=1) 开头
  • 所有公开方法必须包含完整的 PHPDoc 注释(@param@return@throws
  • 新增服务类使用 Asign\ 命名空间并放置于 src/ 目录
  • 提交前运行 ./vendor/bin/phpunit 确保测试全部通过

提交规范

  • 测试文件命名:{ClassName}Test.php,测试类继承 Asign\Tests\TestCase
  • 每个新增接口至少覆盖成功场景和一个异常场景
  • 复杂接口使用 @dataProvider 提供多组测试数据
  • 文件相关测试使用 createTempFile() 方法创建临时文件

许可证

本项目基于 MIT License 开源。

相关链接

统计信息

  • 总下载量: 0
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 3
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-07-13

承接程序开发

PHP开发

VUE

Vue开发

前端开发

小程序开发

公众号开发

系统定制

数据库设计

云部署

网站建设

安全加固