Webhooks
Webhook 会把订单状态变化推送到你的服务器,让你无需轮询。创建 API key 时你可以提供一个 callback_url;如果提供了,该 key 所创建订单的每次状态变化都会 POST 到该地址,并使用该 key 的 secret 签名。
Webhook 是可选的。如果不设置
callback_url,请继续轮询GET /api/v1/energy/orders/:order_id。
开启 webhook
- 在门户中打开 API keys,创建(或重新签发)一个 key。
- 填写 Webhook URL —— 一个由你控制的
https://端点。该地址会被校验为可公开访问;内网、回环与链路本地地址会被拒绝。 - 该 key 创建的订单会投递到此地址。
回调地址按 API key 配置。创建订单的那个 key 决定该订单的通知发往何处。
你会收到什么
向你的 callback_url 发起的 POST:
content-type: application/json
user-agent: tron-energy-aggregator-webhook/1.0
x-signature: t=1727520000,v1=<lowercase-hex>
请求体:
{
"event": "order.status_changed",
"event_id": "ord_9c2b7f0a:COMPLETED",
"created_at": 1727520000,
"data": {
"order_id": "ord_9c2b7f0a",
"status": "COMPLETED",
"product": "TRON_ENERGY",
"order_type": "RENTAL",
"amount": 64400,
"duration_minutes": 60,
"target_address": "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE",
"customer_price": "2.093",
"order_total": "2.093",
"actual_consumed": "2.093",
"currency": "TRX",
"created_at": "2026-09-22T12:00:00.000Z",
"completed_at": "2026-09-22T12:00:05.000Z",
"failure_reason": null
}
}
event恒为order.status_changed。data与GET /api/v1/energy/orders/:order_id返回的结构完全一致;data.status为PENDING、COMPLETED、FAILED、RECONCILIATION_REQUIRED之一。event_id形如${order_id}:${status}—— 对同一状态变化的多次重试保持稳定。请按它去重。
校验投递
HMAC 密钥是你的 API key 的 sk_… secret(与你给请求签名用的同一串)。签名为:
v1 = hex(HMAC-SHA256(secret, `${t}.${rawBody}`))
其中 t 是 x-signature 的 t= 分量(unix 秒),rawBody 是收到的原始 JSON 字节(未解析)。校验步骤:
- 若
|now − t| > 300秒则拒绝。 - 对
${t}.${rawBody}重新计算 HMAC,并与v1值做恒定时间比较。
Python 与 Go 仅使用标准库;Rust 示例需要 hmac、sha2 与 hex。
import hashlib, hmac, re, time def verify(raw_body: bytes, x_signature: str, secret: str) -> bool: t_match = re.search(r"(?:^|,)t=(\d+)", x_signature) v_match = re.search(r"(?:^|,)v1=([0-9a-f]+)", x_signature) if not t_match or not v_match: return False t = int(t_match.group(1)) if abs(int(time.time()) - t) > 300: return False expected = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest() return hmac.compare_digest(expected, v_match.group(1))
务必先读取原始请求体再解析 JSON —— 签名覆盖的是精确字节,重新序列化解析后的对象可能改变它们。
投递语义
- 至少一次(at-least-once)。 失败的投递(网络错误、
429、5xx)会以指数退避重试,最多 5 次。 - 重复处理。 重试使用相同的
event_id,因此请把处理函数按event_id做成幂等。 - 不保证顺序。 最终的状态变化始终是权威状态 —— 以
data.status为准,不要假设先后顺序。 - 2xx 即成功。 任何
2xx响应都表示确认接收。3xx、429及其他非 2xx 按上述规则视为失败。 - 重试耗尽 后投递被丢弃;轮询仍是读取订单状态的兜底方式。
如果你的端点宕机时间超过重试窗口,请轮询 GET /api/v1/energy/orders/:order_id 进行对账。
安全
- 地址必须为
https://且可公开路由;绝不跟随重定向。 - 只有创建订单的那个 API key 会收到它的 webhook。
- 吊销 key 会立即停止该 key 的后续投递。
