回调通知说明
回调通知是电子签平台在合同、印章、模板等业务发生状态变化时,主动向贵方服务推送消息的机制。贵方无需轮询查询,即可实时感知业务动态。
本文将按照以下顺序,带您从零完成回调的接入:
① 了解回调机制 → ② 选择要监听的场景 → ③ 配置加密与校验 → ④ 对照结构体与样例解析数据 → ⑤ 设计可靠的接收架构 → ⑥ 排查常见问题(FAQ)
一. 回调说明
在开始之前,请先了解回调的基本约定。这些是贵方接收端必须满足的前提条件:
| 项目 | 说明 |
|---|---|
| 回调地址 | 在企业应用管理的企业版应用中配置(配置方式见下方 回调 FAQ → 回调地址是否支持更改或删除) |
| 请求方式 | 电子签平台的回调均为 POST 请求。如果收到的是 GET 请求,请确认回调地址是否存在 http 到 https 的转发 |
| 数据格式 | JSON 格式请求 ("Content-Type":"application/json"),数据样式:{"encrypt":"base64后的密文"} |
| 回调成功条件 | 只要贵方返回 httpcode 200,平台即认为回调成功,不再重发本条消息 |
返回 200 是回调成功的唯一判定标准。若贵方处理失败但仍返回 200,平台不会重试;反之未返回 200 则会按重试机制重发(详见 FAQ)。
如何验证回调地址是否可用?
如果已配置回调 URL 却收不到通知,可先在外网环境用下方 curl 命令做一次连通性自测。地址能正常返回 httpcode 200 即表示可用:
curl https://tsign.tencent.com/callback -H 'Content-type: application/json' -X POST -d '{}'
# 请将 https://tsign.tencent.com/callback 替换成贵方配置的回调地址

二. 回调场景
确认接收端就绪后,接下来选择贵方需要监听的业务场景。
通知类型以 MsgType 字段做区分,按业务主要分为以下几类。请根据实际需求点击对应文档,查看该场景下的具体字段定义:
三. 回调加密与校验
选定场景后,为保障数据安全,强烈建议开启加密和签名校验。本节介绍两项可选的安全能力及其接入方式。
若希望先看一遍完整流程,可观看视频:回调通知配置和接收端代码编写示例(视频)
1. 回调消息加解密
回调加密 key
配置了回调加密 key(可选参数)后,电子签的回调请求体会被加密,贵方需用该 key 解密才能得到真正的回调消息。
配置回调加密 key 即开启消息加密,去掉即关闭(发送明文回调消息)。建议开启加密以确保数据安全。
解密步骤

- 对收到数据
{"encrypt":"base64后的密文"}中的 base64 后的密文进行 Base64 解码,得到原始密文。 - 对原始密文进行对称解密:算法为 AES-256-CBC,密钥为电子签提供的 CallbackUrlKey,IV 取 CallbackUrlKey 值的前 16 位,数据采用 PKCS#7 填充。
- 解密后得到的即为输入参数的 JSON 格式数据。
解密代码可参考 解密代码 demo
2. 回调消息校验
签名验证 token
配置了签名验证 token(可选参数)后,电子签的回调请求会在 header 中附带签名参数 [Content-Signature],贵方可据此验证请求确实来自电子签,防止伪造。
原理可参考:GitHub Webhooks 安全实践
校验步骤
当贵方回调服务接收到回调时:
- 取出 header 中的 [Content-Signature]。
- 按下方代码验证签名:验证通过则继续处理,不通过则忽略该请求。
// payload_body 为接收到的消息体内容
// verify_token 为在第三方应用中配置的 "签名验证 token"
// content_signature 为从请求头部中取得的 Content-Signature 的值
def verify_signature(payload_body,content_signature,verify_token):
signature = 'sha256=' + OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), verify_token, payload_body)
return halt 500, "Signatures didn't match!" unless Rack::Utils.secure_compare(signature, content_signature)
payload_body 为整个原始消息体(即 {"encrypt":"base64后的密文"}),验签时请不要做任何更改,否则签名将无法匹配。
四. 通用结构体
完成解密与校验后,贵方即可按下述结构体解析回调数据。根据是否开启加密,接收到的原始请求分为两种:
1. 明文回调消息结构体
未设置回调加密 key 时,回调服务接收到的请求即为该结构体的 JSON 序列化:
| 参数名称 | 参数类型 | 参数描述 |
|---|---|---|
| MsgId | String | 消息唯一 ID,为 32 位字符串,用于唯一确定本消息 |
| MsgType | String | 消息类型,标识本消息由哪个场景发送,如 FlowStatusChange 标识合同状态变更,详见各场景回调 |
| MsgVersion | String | 消息版本,固定为 ThirdPartyApp |
| MsgData | 结构体 | 消息数据,各消息类型的结构体不同,详见各场景回调中的 MsgData 结构体定义 |
2. 密文回调消息结构体
设置了回调加密 key 时,回调服务接收到的请求为该密文结构体的 JSON 序列化,解密后才是上方的明文回调消息结构体:
| 参数名称 | 参数类型 | 参数描述 |
|---|---|---|
| encrypt | String | 加密后的消息体(需通过回调加密 key 解密得到明文回调消息结构体的 JSON 序列化) |
五. 回调样例
下面通过一个完整样例,直观展示上述结构体在真实回调中的样子:从收到加密数据,到解密还原为明文消息。
1. 回调服务收到加密数据
POST /callback HTTP/1.1
Host: www.esstest.com
User-Agent: Go-http-client/1.1
Content-Length: xxx
Content-Type: application/json
Accept-Encoding: gzip
{"encrypt":"62KE4r5Wz0yHzEpMOwVRbM1KV0pPjj+cmJkT+i65MMscgfHAdNP+9K0nV/fFw1xriwi08APc/wM0mHprE43Hc91VPhRDnu2Wn0+bjzgjmy/FgZKZATR9oquy0/BCWu4C77AjkpkoU1/E7gGLr8M9u9t7zbS4AkkGK5xL5TtwI0sS+CMygmyV7bRjxebMycI52U3QJiwDRIPxFO+7yqeXYXV9AQrRskpCDBNFGW72bh+Ixw9dtX00kWcwVQ93V+mayrvdQ8oGSsL32m72kbBfahsIvIxSYSdDAEeTyokqKGfaLWD27vm55QG218IFKEsOJFDGdqCF+IBcM/+rOFeOrewvP5ehIO2KjFBecTDn0RQTlIiokXIQ4zJKvu6njePFRFoFCZjd4oiEIVn/OBw+rjXml3qwgVBQjPRtYdvDJFNENlVjlkVVmLWeS8MIdqsFWhm6Sa7O8X57mwc0cLJ22mGbyVEzNTFqeFJ/mkueW0leLcoZdjv/+IxZusqa1cpfwzkZhwi5rY6kJffNkkrxIc6OeRvpU4ECgBe/b+kxX+ObC0z9u7nzoZAOHx4akYviyIU5B1romjdfHQ/wDr5udm4Rl4NBhU/6V06Rvaadw0Ta9oBkZHGNxFWv32MnL7fVA0zVNOFDP8n+kaQiNGFAXLF4F5oIItYc5+Gp/IxfkltEki7ni7LztViE7b/ZiKSM+gzQn6fLsJ/dlUoZmh141Y0V/GPpsbxBOnWCjBZdNkLTKxdKCMScLCTysJxv7l6Swff8nAEurbzx1tvyhJAvUDnIaLyP8pRPRFq8p0xm3ZVpOo9k7A952XxVHSs40g4sr/Dihkn60aVhGtKK9DueCzn8P3cWG4TYc03M1hNlPfF+UAfnvQ1ZYAMKT/XPLqYtgRFpRkK96YfVecIrfUe9MjWl0/g4hYCAAOJurFoeGwkJiyQ8Q7DCI5EaHa3s/vI621yQyytC6D2u86RiDJxMW0PdvkUfayT7iPwC83EsfEzpQXr0yeSCQCSBgNByEuCNnZl8LAhYl05Y9+bgCzSPt6EUvmaXclYL+/EPrEmi+hzIdXUwBfhXgICT8MteJgMSgmJM2FjjGxy6uZtfHKRIzf1wk6OORPkPJtMgjlMtMs6VFC62EEeo5Xy2v1S95WT/WQ0tnGR8KjbNnmjNSRyD8VtS2mjlLXaK0xRb71YGt57O19YxQQ3R/Hq9zGqOjG+Agdl+pcvh47RlF8o3CnlU7Q=="}
此处使用回调加密 key:TencentEssEncryptTestKey12345678,参考解密代码 Demo 解密后可获取以下明文:
⚠️ 该回调加密 key 仅用于本测试样例。
{
"MsgId": "yDwgKUUckp1jouutUymITAlB0ZirQWfm",
"MsgType": "FlowStatusChange",
"MsgVersion": "CustomApp",
"MsgData": {
"FlowId": "yDRtrAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"DocumentId": "yDRtrBBBBBBBBBBBBBBBBBBBBBBBBBB",
"CallbackType": "sign",
"FlowName": "测试流程",
"FlowDescription": "",
"FlowType": "",
"FlowCallbackStatus": 4,
"Unordered": true,
"CreateOn": 1658892449,
"UpdatedOn": 1659604019,
"DeadLine": 1661615999,
"UserId": "",
"RecipientId": "yDRtrCCCCCCCCCCCCCCCCCCCCCCCCCCC",
"Operate": "sign",
"UserData": "",
"Approvers": [
{
"UserId": "yDRtrDDDDDDDDDDDDDDDDDDDDDDDDDDD",
"RecipientId": "yDRtrCCCCCCCCCCCCCCCCCCCCCCCCCCC",
"ApproverType": 1,
"OrganizationName": "",
"Required": true,
"ApproverName": "张三",
"ApproverMobile": "15912345678",
"ApproverIdCardType": "ID_CARD",
"ApproverIdCardNumber": "440300200101010001",
"ApproveCallbackStatus": 3,
"ApproveMessage": "",
"ApproveTime": 1659604019
}
],
"CallbackUrl": "https://www.esstest.com"
}
}
2. 明文回调消息
若未配置回调加密 key,回调服务将直接收到如下明文消息:
POST /callback HTTP/1.1
Host: www.esstest.com
User-Agent: Go-http-client/1.1
Content-Length: xxx
Content-Type: application/json
Accept-Encoding: gzip
{"MsgId":"yDwgKUUckp1jouutUymITAlB0ZirQWfm","MsgType":"FlowStatusChange","MsgVersion":"CustomApp","MsgData":{"FlowId":"yDRtrAAAAAAAAAAAAAAAAAAAAAAAAAAA","DocumentId":"yDRtrBBBBBBBBBBBBBBBBBBBBBBBBBB","CallbackType":"sign","FlowName":"测试流程","FlowDescription":"","FlowType":"","FlowCallbackStatus":4,"Unordered":true,"CreateOn":1658892449,"UpdatedOn":1659604019,"DeadLine":1661615999,"UserId":"","RecipientId":"yDRtrCCCCCCCCCCCCCCCCCCCCCCCCCCC","Operate":"sign","UserData":"","Approvers":[{"UserId":"yDRtrDDDDDDDDDDDDDDDDDDDDDDDDDDD","RecipientId":"yDRtrCCCCCCCCCCCCCCCCCCCCCCCCCCC","ApproverType":1,"OrganizationName":"","Required":true,"ApproverName":"张三","ApproverMobile":"15912345678","ApproverIdCardType":"ID_CARD","ApproverIdCardNumber":"440300200101010001","ApproveCallbackStatus":3,"ApproveMessage":"","ApproveTime":1659604019}],"CallbackUrl":"https://www.esstest.com"}}
六. 回调最佳实践
了解数据结构后,还需要一套可靠、可追溯、可补偿的接收架构,才能在生产环境中稳定处理回调。我们推荐采用 统一回调接收服务 + MQ 异步解耦 的方案:

该方案的五个核心要点:
- 统一接收:由一个统一回调服务集中接收并校验所有第三方回调,避免各业务重复实现接收与验签逻辑。
- 日志留存:收到回调后先记录完整回调日志,为问题排查和事后追溯提供依据。
- MQ 解耦:回调统一投递至消息队列(MQ),各业务方按需独立消费,互不影响。
- 异常补偿:业务处理异常或回调丢失时,可基于回调日志查询并重新投递,实现补偿重放。
- 快速响应:回调服务在完成消息的可靠接收(落库/入队)后立即返回 200,把耗时的业务处理交给下游异步执行,避免业务逻辑拖慢响应、触发第三方重试。
核心原则是「接收」与「处理」分离:接收端只做校验、落库、入队并快速返回 200;真正的业务逻辑由下游异步消费。这样既能满足平台的超时与成功判定要求(详见回调说明与 FAQ),又能借助日志与 MQ 实现丢失补偿。
七. 回调 FAQ
接入过程中的常见问题汇总如下。
回调地址是否支持同时配置多个?
不支持。一个 SaaS 企业只能配置一个回调地址。
回调地址是否支持更改或删除?
支持。您可在控制台的企业应用管理 → 企业应用配置中修改回调地址。

回调配置后多长时间生效?
配置完成后立即生效。若修改了回调加密 key 或签名验证 token,请确保贵方回调服务已同步支持这两个新的 key。
为什么收到 ALL(合同签署完成)后,又收到了 PART(合同签署中)或 INIT(合同创建)通知?
以单方签署合同为例,FlowCallbackStatus 状态一般由合同签署中或合同创建变为合同签署完成。
少量回调可能因状态变化间隔短、重发或网络传输等原因,出现到达顺序不一致的小概率情况。建议贵方从代码层面做适当控制,例如:状态更新为合同签署完成后,不再回退为合同签署中或合同创建。
电子签发送回调的超时时间是多久?
超时时间为 5 秒。
回调失败后最大重试多少次?重试机制如何?
最大重试 36 次,重试间隔随次数递增:
1 秒、2 秒、3 秒、4 秒、5 秒、10 秒、15 秒、20 秒、25 秒、30 秒、35 秒、40 秒、45 秒、50 秒、55 秒、1 分、2 分、3 分、4 分、5 分、6 分、7 分、8 分、9 分、10 分、15 分、25 分、35 分、45 分、55 分、1 时、2 时、3 时、4 时、5 时、6 时
若 36 次全部失败,平台将认为该消息无法送达并丢弃此消息。请务必保证回调服务的可用性。
回调延时多长?
回调在对应事件发生后立即执行,含处理与网络请求耗时,一般在几百毫秒左右。
具体延时取决于网络状况、服务器负载等因素。若长时间未收到回调,请先检查贵方回调服务是否正常;确认正常后仍无回调,可联系客服处理。