回调通知
当您在创建视频任务时提供了notify_url
,Scenext会在任务完成(成功或失败)时向您的服务器发送回调通知。
回调数据格式
Scenext会向您的notify_url
发送POST请求,包含以下数据:
任务ID
任务状态:
COMPLETED
、FAILED
或 IN_PROGRESS
任务进度信息列表
任务结果(仅在完成时存在)
时间戳(Unix时间戳)
签名验证
为确保安全性,每个回调请求都包含HMAC-SHA256签名,位于X-Signature
请求头中。
验证步骤
- 获取请求体的JSON字符串
- 使用您的API密钥生成HMAC-SHA256签名
- 比较生成的签名与
X-Signature
头部的值
Python验证示例
Copy
import hmac
import hashlib
import json
from flask import Flask, request
app = Flask(__name__)
def verify_signature(payload, signature, api_key):
"""
验证HMAC-SHA256签名
"""
# 将payload转换为JSON字符串(按键排序)
if isinstance(payload, dict):
payload_str = json.dumps(payload, sort_keys=True)
else:
payload_str = str(payload)
# 生成签名
expected_signature = hmac.new(
key=api_key.encode('utf-8'),
msg=payload_str.encode('utf-8'),
digestmod=hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected_signature, signature)
@app.route('/webhook', methods=['POST'])
def webhook():
# 获取签名
signature = request.headers.get('X-Signature')
if not signature:
return 'Missing signature', 400
# 获取请求数据
data = request.get_json()
# 验证签名
api_key = "YOUR_API_KEY" # 您的API密钥
if not verify_signature(data, signature, api_key):
return 'Invalid signature', 401
# 处理回调数据
task_id = data.get('task_id')
status = data.get('status')
result = data.get('result')
if status == 'COMPLETED':
# 任务完成,处理结果
video_url = result.get('video_url')
print(f"Task {task_id} completed. Video URL: {video_url}")
elif status == 'FAILED':
# 任务失败,处理错误
error = result.get('error')
print(f"Task {task_id} failed. Error: {error}")
return 'OK', 200
Node.js验证示例
Copy
const crypto = require('crypto');
const express = require('express');
const app = express();
app.use(express.json());
function verifySignature(payload, signature, apiKey) {
// 将payload转换为JSON字符串(按键排序)
const payloadStr = JSON.stringify(payload, Object.keys(payload).sort());
// 生成签名
const expectedSignature = crypto
.createHmac('sha256', apiKey)
.update(payloadStr, 'utf8')
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expectedSignature, 'hex'),
Buffer.from(signature, 'hex')
);
}
app.post('/webhook', (req, res) => {
const signature = req.headers['x-signature'];
if (!signature) {
return res.status(400).send('Missing signature');
}
const data = req.body;
const apiKey = 'YOUR_API_KEY'; // 您的API密钥
if (!verifySignature(data, signature, apiKey)) {
return res.status(401).send('Invalid signature');
}
// 处理回调数据
const { task_id, status, result } = data;
if (status === 'COMPLETED') {
console.log(`Task ${task_id} completed. Video URL: ${result.video_url}`);
} else if (status === 'FAILED') {
console.log(`Task ${task_id} failed. Error: ${result.error}`);
}
res.status(200).send('OK');
});
回调示例
任务完成回调
Copy
{
"task_id": "12345",
"status": "COMPLETED",
"progress": [
{
"id": "frame1",
"image": "https://oss.scenext.cn/frame_img/d988d4d9-72f0-4b76-97b0-ca974335dcf4/ed6e7bae-cb5d-4e75-a6ae-b4beced80c92/frame1.png?OSSAccessKeyId=LTAI5tDkzckmQzw7eKj4s47h&Expires=1749287113&Signature=ZofSGtAyhO3KolVaMC8ICUZJNWE%3D"
},
{
"id": "frame2",
"image": "https://oss.scenext.cn/frame_img/d988d4d9-72f0-4b76-97b0-ca974335dcf4/ed6e7bae-cb5d-4e75-a6ae-b4beced80c92/frame3.png?OSSAccessKeyId=LTAI5tDkzckmQzw7eKj4s47h&Expires=1749287134&Signature=5zB4nuv7PQkPDCzbKLMQBlb9f%2Bo%3D"
},
...
],
"result": [
{
"type": "video",
"content": "https://oss.scenext.cn/Video/d988d4d9-72f0-4b76-97b0-ca974335dcf4/Manimed6e7bae-cb5d-4e75-a6ae-b4beced80c92.mp4?OSSAccessKeyId=LTAI5tDkzckmQzw7eKj4s47h&Expires=1749288010&Signature=CZRkEsLpWctM9MS3v7IbpRvMcKI%3D"
}
],
"timestamp": 1672531200
}
任务失败回调
Copy
{
"task_id": "12345",
"status": "FAILED",
"progress": [
{
"id": "frame1",
"image": "https://oss.scenext.cn/frame_img/d988d4d9-72f0-4b76-97b0-ca974335dcf4/ed6e7bae-cb5d-4e75-a6ae-b4beced80c92/frame1.png?OSSAccessKeyId=LTAI5tDkzckmQzw7eKj4s47h&Expires=1749287113&Signature=ZofSGtAyhO3KolVaMC8ICUZJNWE%3D"
},
{
"id": "frame2",
"image": "https://oss.scenext.cn/frame_img/d988d4d9-72f0-4b76-97b0-ca974335dcf4/ed6e7bae-cb5d-4e75-a6ae-b4beced80c92/frame3.png?OSSAccessKeyId=LTAI5tDkzckmQzw7eKj4s47h&Expires=1749287134&Signature=5zB4nuv7PQkPDCzbKLMQBlb9f%2Bo%3D"
},
...
],
"result": [
{
"type": "text",
"content": "任务执行失败"
}
],
"timestamp": 1672531200
}
提示
- 请务必验证回调签名以确保安全性
- 回调通知无需返回响应,该回调通知只会发送一次