asign/openapi-sdk-php
Composer 安装命令:
composer require asign/openapi-sdk-php
包简介
爱签(AiSign)电子签约开放平台 PHP SDK - 模块化、结构清晰的 API 封装类库
README 文档
README
一个结构清晰、功能模块化、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 | 人脸照片 + 活体视频多文件上传比对 |
| 签名工具 | BaseClient | RSA 签名/验签、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 是全局配置类,支持三种初始化方式:
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
appId | string | 是 | '' | 爱签开放平台分配的应用 ID |
apiSecret | string | 是 | '' | RSA 私钥(不含 -----BEGIN/END PRIVATE KEY----- 头尾标记) |
domain | string | 否 | https://oapi.asign.cn | API 域名,正式环境默认值 |
notifyUrl | string | 否 | '' | 回调通知地址 |
publicKey | string | 否 | '' | RSA 公钥(用于验证回调签名,不含头尾标记) |
timeout | int | 否 | 30 | HTTP 请求超时秒数(自动转为 int) |
signExpireMs | int | 否 | 600000 | 签名有效期毫秒数(默认 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_ID、ASIGN_API_SECRET、ASIGN_DOMAIN、ASIGN_NOTIFY_URL、ASIGN_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_PROD | https://oapi.asign.cn | 正式环境 |
SignConfig::DOMAIN_PREV | https://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_ERR | cURL 请求失败 |
| 响应异常 | EMPTY_RESPONSE | API 返回空响应 |
| 解析错误 | JSON_PARSE_ERR | JSON 反序列化失败 |
| 文件错误 | FILE_NOT_FOUND, MD5_ERR, FILE_READ_ERR | 文件操作相关 |
| 签名错误 | RSA_KEY_ERR, RSA_SIGN_ERR | RSA 签名/验签 |
| 业务错误 | 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() | string | API 返回的错误码 |
getMessage() | string | 格式为 [ERROR_CODE] 错误描述 |
getCode() | int | HTTP 状态码 |
getResponseData() | ?array | API 原始响应数组(用于深入排查) |
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),关键配置项:
| 配置项 | 值 | 说明 |
|---|---|---|
bootstrap | tests/bootstrap.php | 测试引导文件(自动处理 PSR-4 加载) |
colors | true | 彩色输出 |
stopOnFailure | false | 遇错不终止,完整运行 |
beStrictAboutTestsThatDoNotTestAnything | true | 严格检查无断言的测试方法 |
测试策略
所有测试用例不发起真实 HTTP 请求,采用 Mock 策略:
- 通过
PHPUnit\MockObject模拟BaseClient的post、postWithFile、putRaw、contentMd5等方法 - 每个服务类的测试覆盖成功场景和异常场景(如参数错误、用户不存在、密码错误等)
- 使用
@dataProvider数据供给器覆盖多组错误码和参数组合 - 文件上传测试使用
createTempFile()创建临时文件并在tearDown()中自动清理 - 通过
ReflectionMethod测试protected方法(如rsaSign、filterEmpty)
测试覆盖统计
| 测试文件 | 测试方法数 | 覆盖范围 |
|---|---|---|
SignConfigTest | 9 | 配置创建(数组/全局/环境)、域名常量、环境切换、超时类型转换 |
SignExceptionTest | 8 | 异常构造(完整/最简)、fromApiResponse(message/msg/缺省)、ResponseData |
BaseClientTest | 11 | 时间戳、Content-MD5、签名确定性/唯一性、RSA 验签、filterEmpty 递归 |
AuthServiceTest | 8 | 个人认证(URL/三要素)、企业认证、验证码重发/校验 |
UserServiceTest | 12 | 个人/企业新增、查询(仅account/带idcard)、修改手机、验证码、OCR、人脸比对 |
SealServiceTest | 8 | 创建印章/法人章、查询列表/空列表、修改、设置默认、删除/不存在 |
FileServiceTest | 8 | 获取上传URL、自定义文件名、PUT直传、一键上传、uploadUrl缺失 |
ContractServiceTest | 12 | 创建合同、添加签署方、状态/详情查询、预览、密码签署/错误、下载、saveBase64 |
TemplateServiceTest | 5 | 模板上传(两种接口)、下载、模板不存在 |
EvidenceServiceTest | 7 | 存证、按contractNo/preserveNo下载证书、下载链报告、各类不存在 |
VerifyServiceTest | 4 | 活体比对成功、认证失败、多组质量错误 |
SignClientTest | 5 | 全服务初始化验证、共享BaseClient、环境配置、getBaseClient/getConfig |
代码审查报告
基于对 src/ 和 tests/ 全部源代码的全面审查,以下是主要发现:
架构设计 ✅
- 门面模式:
SignClient统一入口,所有服务共享单一BaseClient实例,避免重复初始化 - 关注点分离:配置(
SignConfig)→ 通信(BaseClient)→ 业务(Service 类)三层清晰 - 命名空间规范:PSR-4 映射
Asign\→src/,可与任何 Composer 项目集成
PHPDoc 注释完整性 ✅
- 所有公开方法均标注
@param、@return、@throws - 数组参数使用
array<string, mixed>形状标注 - 类级别注释说明了模块职责和核心设计决策
错误处理一致性 ✅
- 统一使用
SignException(继承\RuntimeException),包含errorCode和responseData fromApiResponse()兼容message和msg两种字段名- cURL 错误、JSON 解析错误、文件操作错误均被捕获并转换为异常
- 所有 HTTP 请求均检查返回值和错误信息
PSR-12 规范遵循度 ✅
- 所有文件使用
declare(strict_types=1) - 4 空格缩进,左大括号同行
- 类型声明完整(属性类型、参数类型、返回类型)
- 运算符两侧空格,方法间空行分隔
改进建议 ⚠️
| 类别 | 建议 | 优先级 |
|---|---|---|
| 安全性 | BaseClient 中 CURLOPT_SSL_VERIFYPEER 硬编码为 false,建议在 SignConfig 中增加 sslVerify 配置项 | 中 |
| 可维护性 | BaseClient 的 request、requestWithFile、requestWithFiles 三个方法中存在签名头部构建的代码重复,可提取为 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
其他信息
- 授权协议: MIT
- 更新时间: 2026-07-13