跳至主要内容

Webhook 配置

Webhook 允许您的应用在 ONE 项目中发生事件时接收实时 HTTP POST 通知 -- 交易确认、交易执行、支付完成等。您可以通过仪表板或使用 OneEngineClient 以编程方式配置 Webhook。

客户端设置

import { OneEngineClient } from '@one_deploy/sdk';

const engine = new OneEngineClient({
baseUrl: process.env.ONE_ENGINE_URL!,
clientId: process.env.ONE_CLIENT_ID!,
secretKey: process.env.ONE_SECRET_KEY!,
});

方法

方法说明
createWebhook(input)创建新的 Webhook 订阅。
getWebhooks(projectId)列出项目的所有 Webhook。
getWebhook(webhookId)按 ID 获取单个 Webhook。
updateWebhook(webhookId, updates)更新现有 Webhook。
deleteWebhook(webhookId)删除 Webhook 订阅。
testWebhook(webhookId)向 Webhook 端点发送测试事件。
getWebhookDeliveries(webhookId, options?)获取 Webhook 的投递日志。

WebhookEventType

WebhookEventType 枚举定义了您可以订阅的所有事件。

enum WebhookEventType {
// Transaction events
TRANSACTION_CONFIRMED = 'transaction.confirmed',
TRANSACTION_FAILED = 'transaction.failed',
TRANSACTION_PENDING = 'transaction.pending',

// Wallet events
WALLET_CREATED = 'wallet.created',
WALLET_BALANCE_CHANGE = 'wallet.balance_change',

// Payment events
ONRAMP_COMPLETED = 'onramp.completed',
ONRAMP_FAILED = 'onramp.failed',
OFFRAMP_COMPLETED = 'offramp.completed',
OFFRAMP_FAILED = 'offramp.failed',
SWAP_COMPLETED = 'swap.completed',
SWAP_FAILED = 'swap.failed',

// AI Trading events
TRADE_OPENED = 'trade.opened',
TRADE_CLOSED = 'trade.closed',
TRADE_PROFIT_TAKEN = 'trade.profit_taken',
TRADE_STOP_LOSS = 'trade.stop_loss',
STRATEGY_ACTIVATED = 'strategy.activated',
STRATEGY_PAUSED = 'strategy.paused',

// Forex events
FOREX_POSITION_OPENED = 'forex.position_opened',
FOREX_POSITION_CLOSED = 'forex.position_closed',
FOREX_POOL_UPDATED = 'forex.pool_updated',

// Contract events
CONTRACT_DEPLOYED = 'contract.deployed',
CONTRACT_EVENT = 'contract.event',

// Project events
PROJECT_QUOTA_WARNING = 'project.quota_warning',
PROJECT_QUOTA_REACHED = 'project.quota_reached',
}

创建 Webhook

CreateWebhookInput

interface CreateWebhookInput {
projectId: string;
url: string; // HTTPS endpoint that receives POST requests
events: WebhookEventType[]; // Events to subscribe to
description?: string; // Human-readable label
secret?: string; // Shared secret for HMAC signature verification
active?: boolean; // Defaults to true
headers?: Record<string, string>; // Custom headers sent with every delivery
}

基本设置

const res = await engine.createWebhook({
projectId: 'proj_abc123',
url: 'https://api.myapp.com/webhooks/one',
events: [
WebhookEventType.TRANSACTION_CONFIRMED,
WebhookEventType.SWAP_COMPLETED,
WebhookEventType.TRADE_CLOSED,
],
description: 'Production transaction notifications',
secret: 'whsec_my_signing_secret',
});

if (res.success) {
console.log('Webhook ID:', res.data.id);
console.log('URL:', res.data.url);
console.log('Events:', res.data.events);
}

Webhook 响应类型

interface Webhook {
id: string;
projectId: string;
url: string;
events: WebhookEventType[];
description?: string;
secret: string;
active: boolean;
headers?: Record<string, string>;
createdAt: string; // ISO-8601
updatedAt: string; // ISO-8601
}

列出 Webhook

const res = await engine.getWebhooks('proj_abc123');

if (res.success) {
for (const webhook of res.data) {
console.log(`[${webhook.id}] ${webhook.url} -- ${webhook.active ? 'active' : 'paused'}`);
console.log(' Events:', webhook.events.join(', '));
}
}

更新 Webhook

只传递您想要更改的字段。未传递的字段保持其当前值。

const res = await engine.updateWebhook('whk_abc123', {
events: [
WebhookEventType.TRADE_OPENED,
WebhookEventType.TRADE_CLOSED,
WebhookEventType.TRADE_STOP_LOSS,
],
active: true,
});

if (res.success) {
console.log('Updated events:', res.data.events);
}

测试 Webhook

向您的端点发送合成测试事件,以验证连接和负载处理。

const res = await engine.testWebhook('whk_abc123');

if (res.success) {
console.log('Test delivery ID:', res.data.deliveryId);
console.log('Status code:', res.data.statusCode);
console.log('Response time:', res.data.durationMs, 'ms');

if (res.data.statusCode === 200) {
console.log('Webhook endpoint is responding correctly.');
} else {
console.warn('Endpoint returned non-200 status.');
}
}

测试事件的负载如下所示:

{
"id": "evt_test_123",
"type": "webhook.test",
"projectId": "proj_abc123",
"timestamp": "2025-01-15T12:00:00Z",
"data": {
"message": "This is a test webhook delivery."
}
}

Webhook 投递日志

跟踪每个 Webhook 的投递尝试、响应代码和重试状态。

getWebhookDeliveries

interface GetWebhookDeliveriesOptions {
page?: number; // 1-based (default: 1)
limit?: number; // Items per page (default: 20, max: 100)
status?: 'success' | 'failed' | 'pending';
}

interface WebhookDelivery {
id: string;
webhookId: string;
eventType: WebhookEventType;
url: string;
statusCode: number | null; // null if the request failed to connect
requestBody: string; // JSON payload sent
responseBody?: string; // Response from your endpoint
durationMs: number;
attempt: number; // 1 = first attempt, 2+ = retries
maxAttempts: number;
status: 'success' | 'failed' | 'pending';
createdAt: string; // ISO-8601
nextRetryAt?: string; // ISO-8601, present if status is 'pending'
}

查询投递记录

const res = await engine.getWebhookDeliveries('whk_abc123', {
page: 1,
limit: 25,
status: 'failed',
});

if (res.success) {
for (const delivery of res.data) {
console.log(
`[${delivery.id}] ${delivery.eventType} -- ` +
`${delivery.status} (attempt ${delivery.attempt}/${delivery.maxAttempts})`
);
if (delivery.statusCode) {
console.log(` Status: ${delivery.statusCode}`);
}
if (delivery.nextRetryAt) {
console.log(` Next retry: ${delivery.nextRetryAt}`);
}
}
}

重试行为

当 Webhook 投递失败(非 2xx 响应或连接错误)时,ONE Engine 会使用指数退避策略进行重试:

尝试次数失败后延迟
第 1 次重试30 秒
第 2 次重试2 分钟
第 3 次重试15 分钟
第 4 次重试1 小时
第 5 次重试4 小时

在 5 次重试失败后(包括原始尝试共 6 次),投递将被标记为永久失败。您可以在仪表板中或通过 getWebhookDeliveries 查看失败的投递。

注意

如果 Webhook 端点持续失败 24 小时,该 Webhook 将被自动暂停以防止不必要的负载。修复端点后,可以从仪表板重新启用,或调用 updateWebhook(webhookId, { active: true }) 来恢复。

验证 Webhook 签名

每次投递都包含一个 X-One-Signature 响应头,其中包含请求正文的 HMAC-SHA256 签名,使用您的 Webhook secret 进行签名。请始终验证此签名以确保负载是由 ONE Engine 发送的。

server/webhook-handler.ts
import crypto from 'crypto';

function verifyWebhookSignature(
body: string,
signature: string,
secret: string
): boolean {
const expected = crypto
.createHmac('sha256', secret)
.update(body, 'utf8')
.digest('hex');

return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}

// Express example
app.post('/webhooks/one', (req, res) => {
const signature = req.headers['x-one-signature'] as string;
const body = JSON.stringify(req.body);

if (!verifyWebhookSignature(body, signature, process.env.WEBHOOK_SECRET!)) {
return res.status(401).send('Invalid signature');
}

const event = req.body;
console.log('Received event:', event.type);
console.log('Data:', event.data);

// Process the event...
res.status(200).send('OK');
});

完整的 Webhook 设置流程

以下示例演示了完整的生命周期:创建 Webhook、测试它、验证投递和清理。

webhook-setup.ts
import { OneEngineClient, WebhookEventType } from '@one_deploy/sdk';

const engine = new OneEngineClient({
baseUrl: process.env.ONE_ENGINE_URL!,
clientId: process.env.ONE_CLIENT_ID!,
secretKey: process.env.ONE_SECRET_KEY!,
});

async function setupWebhooks() {
// 1. Create a webhook
const createRes = await engine.createWebhook({
projectId: 'proj_abc123',
url: 'https://api.myapp.com/webhooks/one',
events: [
WebhookEventType.TRANSACTION_CONFIRMED,
WebhookEventType.TRANSACTION_FAILED,
WebhookEventType.SWAP_COMPLETED,
WebhookEventType.TRADE_OPENED,
WebhookEventType.TRADE_CLOSED,
WebhookEventType.FOREX_POSITION_OPENED,
WebhookEventType.FOREX_POSITION_CLOSED,
WebhookEventType.PROJECT_QUOTA_WARNING,
],
description: 'Main production webhook',
secret: 'whsec_production_secret_value',
});

if (!createRes.success) {
console.error('Failed to create webhook:', createRes.error);
return;
}

const webhookId = createRes.data.id;
console.log('Created webhook:', webhookId);

// 2. Test the endpoint
const testRes = await engine.testWebhook(webhookId);

if (testRes.success && testRes.data.statusCode === 200) {
console.log('Test delivery successful.');
} else {
console.warn('Test delivery issue. Check your endpoint.');
}

// 3. Check delivery logs
const deliveries = await engine.getWebhookDeliveries(webhookId, { limit: 5 });

if (deliveries.success) {
console.log(`Recent deliveries: ${deliveries.data.length}`);
for (const d of deliveries.data) {
console.log(` ${d.eventType} -- ${d.status} (${d.statusCode})`);
}
}

// 4. List all webhooks for the project
const allWebhooks = await engine.getWebhooks('proj_abc123');

if (allWebhooks.success) {
console.log(`Total webhooks: ${allWebhooks.data.length}`);
}
}

setupWebhooks();

下一步