ElectronicSignature 域把 8 家电子签章供应商——契约锁、e签宝、法大大、君子签、爱签、上上签、安证通、腾讯电子签——的合同签署能力收拢到一个 12 方法的接口背后。涵盖合同全生命周期、签署流程、印章管理、模板管理和回调验签五个区域。
接口本身走极简模式,复杂场景由厂商扩展方法承接;错误通过统一的 ElectronicSignatureResult<T> 包装;回调通知拆成验签与解析两个独立接口,调用顺序固定。完整 API 清单见 电子签章 API 参考。
域结构
Bitzsoft.Integrations.ElectronicSignature ← 抽象层(接口 + 模型 + 异常)├── Bitzsoft.Integrations.ElectronicSignature.Qiyuesuo ← 契约锁├── Bitzsoft.Integrations.ElectronicSignature.ESign ← e签宝├── Bitzsoft.Integrations.ElectronicSignature.Fadada ← 法大大├── Bitzsoft.Integrations.ElectronicSignature.Junziqian ← 君子签├── Bitzsoft.Integrations.ElectronicSignature.Asign ← 爱签├── Bitzsoft.Integrations.ElectronicSignature.BestSign ← 上上签├── Bitzsoft.Integrations.ElectronicSignature.Anzhengtong ← 安证通├── Bitzsoft.Integrations.ElectronicSignature.Tencent ← 腾讯电子签└── Bitzsoft.Integrations.ElectronicSignature.All ← 聚合包标准三层:抽象层定义接口与共享类型,每家厂商一个实现包,All 聚合包按配置节按需注册。
五个能力区域
IElectronicSignatureProvider 按 5 个业务区域组织,共 12 个方法。异步方法统一用 ElectronicSignatureResult<T> 包装,回调验签是同步方法直接返回 bool。
public interface IElectronicSignatureProvider{ string ProviderName { get; }
// ① 合同管理(5) Task<ElectronicSignatureResult<string>> CreateContractAsync(SimpleContractRequest request, CancellationToken ct = default); Task<ElectronicSignatureResult<ContractDetail>> GetContractDetailAsync(string contractId, CancellationToken ct = default); Task<ElectronicSignatureResult<byte[]>> DownloadContractAsync(string contractId, CancellationToken ct = default); Task<ElectronicSignatureResult<string>> GetContractViewUrlAsync(string contractId, CancellationToken ct = default); Task<ElectronicSignatureResult<bool>> CancelContractAsync(string contractId, string? reason = null, CancellationToken ct = default);
// ② 签署(2) Task<ElectronicSignatureResult<string>> GetSigningUrlAsync(string contractId, string signerId, CancellationToken ct = default); Task<ElectronicSignatureResult<SigningStatus>> GetSigningStatusAsync(string contractId, CancellationToken ct = default);
// ③ 印章管理(2) Task<ElectronicSignatureResult<IReadOnlyList<SealInfo>>> ListSealsAsync(string? organizationId = null, CancellationToken ct = default); Task<ElectronicSignatureResult<SealInfo>> GetSealDetailAsync(string sealId, CancellationToken ct = default);
// ④ 模板管理(2) Task<ElectronicSignatureResult<IReadOnlyList<TemplateInfo>>> ListTemplatesAsync(int pageIndex = 1, int pageSize = 20, CancellationToken ct = default); Task<ElectronicSignatureResult<TemplateDetail>> GetTemplateDetailAsync(string templateId, CancellationToken ct = default);
// ⑤ 回调验签(1,同步) bool VerifyCallback(string signature, string payload, IDictionary<string, string>? callbackHeaders = null, string? queryString = null);}Result 模式
ElectronicSignatureResult<T> 把供应商的成功与失败统一到一个对象里,避免调用方在 try-catch 里区分业务错误和系统异常:
public sealed class ElectronicSignatureResult<T>{ public bool IsSuccess { get; init; } public T? Data { get; init; } // 成功时的结果数据 public string? ErrorCode { get; init; } // 供应商原始错误码 public string? ErrorMessage { get; init; } // 错误消息
public static ElectronicSignatureResult<T> Success(T data) => ...; public static ElectronicSignatureResult<T> Fail(string errorCode, string errorMessage) => ...;}供应商实现内部用 ElectronicSignatureException 捕获底层错误,再转成 Fail:
public class ElectronicSignatureException : IntegrationException{ public string ProviderName => Provider ?? string.Empty; public new string? ErrorCode => base.ErrorCode; public new string? ProviderMessage => base.ProviderMessage;
public ElectronicSignatureException(string providerName, string? errorCode, string? providerMessage) : base(domain: "ElectronicSignature", message: $"[{providerName}] {errorCode}: {providerMessage}", provider: providerName, errorCode: errorCode, providerMessage: providerMessage) { }}也就是说:正常失败走 Fail(如签署方拒签、合同已过期等业务校验),系统级异常才会冒泡抛出。调用方通常只需检查 IsSuccess。
回调:验签与解析分离
电子签场景里,签署完成、状态变更等通知由厂商通过 Webhook 回调推送。每家厂商的签名算法和报文格式都不一样,抽象层把回调处理拆成两个独立关注点:
| 关注点 | 接口 | 返回 | 调用时机 |
|---|---|---|---|
| 验签(安全闸门) | IElectronicSignatureProvider.VerifyCallback | bool | 信任报文之前 |
| 解析(语义转换) | IElectronicSignatureCallbackParser.ParseCallback | CallbackEvent | 验签通过之后 |
public interface IElectronicSignatureCallbackParser{ CallbackEvent ParseCallback(string payload);}拆分是因为职责不同:验签是安全闸门,必须在信任报文之前完成;解析是语义转换,在信任报文之后提取业务字段。VerifyCallback 的签名之所以带 callbackHeaders 和 queryString,是因为部分厂商需要请求头里的时间戳或排序后的查询串参与验签计算。
统一事件模型 CallbackEvent:
public sealed class CallbackEvent{ public string ContractId { get; init; } = string.Empty; public ContractStatus Status { get; init; } // 签署/完成/拒签等 public string? SignerId { get; init; } public string? RejectReason { get; init; } public DateTime? EventTime { get; init; } public string OriginalPayload { get; init; } = string.Empty; // 保留原始报文备查}厂商清单
8 家厂商都完整实现了统一接口,每家都同时注册了 IElectronicSignatureProvider 和 IElectronicSignatureCallbackParser 两个能力。验签逻辑内嵌在 Provider 里,解析逻辑在独立的 XxxCallbackParser 类里。
| 厂商 | 中文 | Provider ID | DI 方法 |
|---|---|---|---|
| Qiyuesuo | 契约锁 | Qiyuesuo | AddBitzsoftQiyuesuoElectronicSignature() |
| ESign | e签宝 | ESign | AddBitzsoftESignElectronicSignature() |
| Fadada | 法大大 | Fadada | AddBitzsoftFadadaElectronicSignature() |
| Junziqian | 君子签 | Junziqian | AddBitzsoftJunziqianElectronicSignature() |
| Asign | 爱签 | Asign | AddBitzsoftAsignElectronicSignature() |
| BestSign | 上上签 | BestSign | AddBitzsoftBestSignElectronicSignature() |
| Anzhengtong | 安证通 | Anzhengtong | AddBitzsoftAnzhengtongElectronicSignature() |
| Tencent | 腾讯电子签 | Tencent | AddBitzsoftTencentElectronicSignature() |
注册
单厂商
// ① 契约锁builder.Services.AddBitzsoftQiyuesuoElectronicSignature(builder.Configuration.GetSection("ElectronicSignature:Qiyuesuo"));
// ② e签宝builder.Services.AddBitzsoftESignElectronicSignature(builder.Configuration.GetSection("ElectronicSignature:ESign"));多厂商聚合
builder.Services.AddBitzsoftElectronicSignatureAll(builder.Configuration, "ElectronicSignature");All 聚合包按配置节存在性按需注册,配置里写了哪几家就注册哪几家:
{ "ElectronicSignature": { "Qiyuesuo": { "AppId": "...", "AppSecret": "...", "BaseUrl": "..." }, "ESign": { "AppId": "...", "AppSecret": "...", "BaseUrl": "..." } }}消费
创建并发起签署
public class SigningService(IElectronicSignatureProvider esign){ public async Task<string> CreateAndStartAsync(byte[] fileData, string signerName, string signerPhone) { var result = await esign.CreateContractAsync(new SimpleContractRequest { FileData = fileData, FileName = "劳动合同.pdf", Title = "2026 年度劳动合同", SignerName = signerName, SignerPhone = signerPhone, });
if (!result.IsSuccess) throw new InvalidOperationException($"签署创建失败: {result.ErrorCode} - {result.ErrorMessage}");
return result.Data!; // 返回签署流程 ID }}SimpleContractRequest 支持两种发起来源:传 FileData + FileName 直接上传文件,或传 TemplateId 基于模板发起(二者二选一)。SignerPhone / SignerEmail 至少传一个,供厂商向签署方发送签署通知。供应商特有参数通过 ExtensionParams 字典透传。
回调处理:先验签,再解析
public class CallbackController( IElectronicSignatureProvider esign, IElectronicSignatureCallbackParser parser){ [HttpPost("webhook/esign")] public async Task<IActionResult> Handle([FromBody] string payload, [FromHeader] string xSignature) { // ① 必须先验签,确认报文可信 if (!esign.VerifyCallback(xSignature, payload)) return Unauthorized();
// ② 验签通过后解析为统一事件 var evt = parser.ParseCallback(payload);
// 根据 evt.Status 更新本地签署记录... return Ok(); }}多厂商路由
public class SigningRouter(IIntegrationProviderResolver<IElectronicSignatureProvider> providers){ public async Task<ContractDetail> GetDetailAsync(string vendor, string contractId) { var provider = providers.GetRequired(vendor); // "Qiyuesuo" / "ESign" / ... var result = await provider.GetContractDetailAsync(contractId); return result.IsSuccess ? result.Data! : throw new InvalidOperationException(result.ErrorMessage); }}合同状态流转
ContractStatus 枚举覆盖了合同的 7 个状态,回调解析后的 CallbackEvent.Status 和 ContractDetail.Status 都用它:
Draft ──创建签署──▶ Signing ──全部签完──▶ Completed │ ├──拒签──▶ Rejected ├──撤销────▶ Cancelled └──超时────▶ Expired任意阶段异常 ──▶ Failed| 枚举值 | 含义 | 是否终态 |
|---|---|---|
Draft | 草稿,尚未发起签署 | 否 |
Signing | 签署中 | 否 |
Completed | 全部签署方完成 | 是 |
Cancelled | 已撤销 | 是 |
Rejected | 已拒签 | 是 |
Expired | 已过期 | 是 |
Failed | 签署失败(系统异常) | 是 |
相关
- 电子签章 API 参考 — 完整方法签名、DTO 字段表、枚举清单
- 三层包结构
- 错误处理约定
- 结构变体