外观
接收异步图片回调
提交异步图片任务时传入 callback_url,任务进入成功或失败终态后,Revora 会向该地址发送 HTTPS POST 请求。回调只改变结果通知方式,不改变原有模型权限、分组倍率、扣费或消费记录逻辑。
接入步骤
- 调用
GET /v1/models获取当前 Token 可用的图片模型。 - 前往 Revora 控制台的“令牌管理”,在对应 Token 的操作菜单中选择“回调密钥”,生成并立即保存
whsec_...密钥。密钥只显示一次。 - 提交
POST /v1/images/generations/async时传入公网 HTTPScallback_url。 - 回调服务使用原始请求体和回调密钥验签,按
event_id防重复处理。 - 回调长时间未到时,使用
GET /v1/images/generations/tasks/{task_id}查询兜底。
密钥轮换
重置回调密钥后,新任务使用新密钥;旧密钥保留 24 小时兼容期,用于完成轮换前已提交任务的回调。接收端可在此期间同时尝试新旧两个密钥,之后删除旧密钥。
提交带回调的任务
bash
curl --request POST 'https://revora.vip/v1/images/generations/async' \
--header 'Authorization: Bearer YOUR_API_TOKEN' \
--header 'Content-Type: application/json' \
--data-raw '{
"model": "YOUR_AVAILABLE_MODEL",
"prompt": "白色背景上的商品主图,柔和棚拍光线",
"size": "1024x1024",
"n": 1,
"callback_url": "https://merchant.example.com/webhooks/revora-images"
}'callback_url 每次任务都可以不同,适合多套测试环境或多业务系统。它必须使用 HTTPS,并且不能指向本机、内网、云元数据地址或需要重定向的地址。Idempotency-Key 仍为可选项;只有客户端可能自动重试、且需要避免重复创建任务时才需要传。
请求头
每次回调包含以下请求头:
| 请求头 | 说明 |
|---|---|
X-Revora-Event-Id | 本次终态事件的唯一 ID,用于幂等去重 |
X-Revora-Timestamp | 生成签名时的 Unix 秒级时间戳 |
X-Revora-Signature | v1= 加十六进制 HMAC-SHA256 摘要 |
Content-Type | application/json |
签名原文为:
text
X-Revora-Timestamp + "." + 原始 HTTP 请求体字节请务必在 JSON 解析前保留原始请求体。重新序列化 JSON 后再验签会导致签名不一致。
成功事件
事件名为 image.generation.succeeded:
json
{
"event_id": "evt_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"event": "image.generation.succeeded",
"created_at": 1784019766,
"attempt": 1,
"data": {
"task_id": "img_task_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"status": "succeeded",
"completed_at": 1784019766,
"result": {
"created": 1784019766,
"data": [
{ "url": "https://image.example.com/result.png" }
]
}
}
}结果 URL 是临时签名地址。收到成功事件后请尽快下载并保存到自己的对象存储,不要将该 URL 当作永久地址。
失败事件
事件名为 image.generation.failed:
json
{
"event_id": "evt_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"event": "image.generation.failed",
"created_at": 1784019766,
"attempt": 1,
"data": {
"task_id": "img_task_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"status": "failed",
"completed_at": 1784019766,
"error": {
"code": "upstream_error",
"message": "image generation failed"
}
}
}JavaScript 验签示例
以下示例假设 rawBody 是框架读取到的原始 Buffer,没有经过 JSON 解析或重新编码:
javascript
import crypto from 'node:crypto';
export function verifyRevoraWebhook({ rawBody, headers, secret }) {
const eventId = headers['x-revora-event-id'];
const timestamp = headers['x-revora-timestamp'];
const received = headers['x-revora-signature'];
if (!eventId || !timestamp || !received?.startsWith('v1=')) return false;
const age = Math.abs(Math.floor(Date.now() / 1000) - Number(timestamp));
if (!Number.isFinite(age) || age > 5 * 60) return false;
const expected = `v1=${crypto
.createHmac('sha256', secret)
.update(timestamp)
.update('.')
.update(rawBody)
.digest('hex')}`;
const a = Buffer.from(expected);
const b = Buffer.from(received);
return a.length === b.length && crypto.timingSafeEqual(a, b);
}验签成功后,以 X-Revora-Event-Id(或请求体中的 event_id)作为数据库唯一键。业务处理和记录事件 ID 应放在同一事务中。
Python 验签示例
python
import hashlib
import hmac
import time
def verify_revora_webhook(raw_body: bytes, headers, secret: str) -> bool:
event_id = headers.get("X-Revora-Event-Id")
timestamp = headers.get("X-Revora-Timestamp")
received = headers.get("X-Revora-Signature", "")
if not event_id or not timestamp or not received.startswith("v1="):
return False
try:
if abs(int(time.time()) - int(timestamp)) > 5 * 60:
return False
except ValueError:
return False
message = timestamp.encode() + b"." + raw_body
digest = hmac.new(secret.encode(), message, hashlib.sha256).hexdigest()
return hmac.compare_digest(received, "v1=" + digest)固定验签向量
可使用以下数据验证不同语言的实现是否一致:
text
secret = whsec_test_secret
timestamp = 1784019766
raw body = {"event_id":"evt_test","event":"image.generation.succeeded"}
signature = v1=151f3e7ef4371916001b7b68ec1e2abec6db44c4ce5718c0ff23b39c6aec35e5响应、重试与去重
- 验签并持久化接收后,请尽快返回任意 HTTP
2xx,建议在 3 秒内返回;耗时的图片下载和业务处理放入自己的队列。 - 非
2xx、连接错误或超时会触发重试。最多投递 8 次,间隔依次为:立即、10 秒、1 分钟、5 分钟、30 分钟、2 小时、6 小时、24 小时。 - 回调采用至少一次投递语义,同一
event_id可能到达多次。重复事件应直接返回2xx,不要重复执行订单或素材入库逻辑。 - 为防止重放攻击,建议拒绝与当前时间相差超过 5 分钟的
X-Revora-Timestamp。 - 回调投递失败不会改变任务终态、扣费或消费记录。可随时使用任务查询接口获取最终结果。
接收端检查清单
- 只开放 HTTPS,并使用有效公网证书。
- 对原始请求体执行 HMAC-SHA256 验签。
- 检查 5 分钟时间窗口。
- 使用
event_id唯一索引实现幂等。 - 先落库再快速返回
2xx,耗时工作异步执行。 - 保留任务查询接口作为回调丢失时的兜底。