Webhook 接收
QI DTVM 以 POST 请求、JSON body 的形式将每个 webhook 发送到您配置的 URL。请求携带 SIGNATURE header, 这是一个使用您 webhook 配置中的 Signature Key 以 HS256 签名的 JWT:Signature Key 是一个对称密钥,只有 QI 和您知道。
验证签名
import json
from hashlib import md5
from jose import jwt
signature_key = "YOUR_SIGNATURE_KEY"
webhook_url = "https://your-company.com.br/webhooks/qi" # 配置中注册的 URL
def validate_webhook(headers: dict, raw_body: bytes) -> dict:
# 如果签名与您的 Signature Key 不匹配,jwt.decode 会拒绝该 token。
claims = jwt.decode(headers["SIGNATURE"], key=signature_key, algorithms=["HS256"])
if claims["uri"] != webhook_url:
raise ValueError("uri diferente da URL configurada")
if claims.get("payload_md5") != md5(raw_body).hexdigest():
raise ValueError("payload_md5 não corresponde ao body recebido")
return json.loads(raw_body)
解码后的 JWT 包含:
| 字段 | 描述 |
|---|---|
timestamp | 签名的日期和时间,UTC,格式为 AAAA-MM-DDTHH:MM:SS。每次发送都会变化。 |
method | 请求的 HTTP 方法:POST。 |
uri | 为您的 webhook 配置的完整目标 URL。 |
payload_md5 | 所发送 body 的 MD5 哈希值。 |
请基于接收到的字节计算 MD5,即在解析 JSON 之前计算。解析后再重新编码(re-encode)JSON 会改变空格和字段顺序,导致哈希值不一致。
Body 格式
所有 webhook 使用相同的外层结构。data 的内容取决于事件,详见各产品的 webhook 页面。
{
"webhook_type": "settlement.payment_batch_status_change",
"webhook_datetime": "2026-10-07T14:32:10Z",
"data": {}
}
| 字段 | 描述 |
|---|---|
webhook_type | 事件,格式为 <produto>.<evento>。 |
webhook_datetime | 事件生成的日期和时间。所有重试中该值都相同。 |
data | 事件数据:根据事件不同,为一个对象或一个列表。 |
请勿对 webhook 做严格映射:外层结构和 data 中随时可能新增字段。
AGENT-KEY header
AGENT-KEY header 携带的是该 webhook 配置所属代理方的 key,也就是您自己的 key:管理人的 manager_key、顾问的 consultant_key 或转让方的 assignor_key。它不标识基金。要确定事件属于哪个基金,请使用 data 中的字段,例如 fund_class_key。
投递与重试
- 当您的 URL 在 30 秒内返回小于 300 的 HTTP 状态码时,投递视为成功。任何其他状态码、超时或网络错误均视为失败。
- 每个事件最多发送 4 次。第 4 次失败后,该事件被标记为失败,不再自动重发。
- 请快速响应:先保存 webhook,再异步处理。您的应用处理所花的时间也计入 30 秒。
已用尽重试次数的 webhook 可以由 QI DTVM 重发。请联系 integracao.dtvm@qitech.com.br,并提供时间段和事件类型。重发不是您的集成可调用的操作。
重复投递
您可能会多次收到同一个 webhook,原因有两种:
- 您的应用已收到 webhook,但响应未在时限内到达 QI,于是重复发送。
- 同一事件可能需要投递给多个代理方:例如基金的管理人、顾问和转让方。这些发送会作为一次尝试依次进行。只要任何一方的 URL 失败,整次尝试都会重复,已经收到的一方也会再次收到。
第二种情况意味着其他代理方 URL 的失败会在您的 URL 上产生重复投递。请将接收处理设计为幂等:重复投递的 body 逐字节相同(webhook_type、webhook_datetime 和 data 都相同),因此 payload_md5 也相同。保存已处理 webhook 的 payload_md5,丢弃重复的。不要用 header 中的 JWT 来去重:timestamp 每次发送都会变化。
来源验证
除验证签名外,您还可以将 URL 限制为只接受 QI 的出口 IP:
| 环境 | IP |
|---|---|
| 生产环境 | 54.205.166.229 |
| Sandbox | 52.72.221.4 |