工作原理
- 1注册 Webhook
在Webhook 面板中或通过
POST /webhooks注册,填写您的服务器 URL 和希望接收的事件。您的账户需要有有效的 API 密钥。URL 必须以https://开头,并使用可通过 DNS 解析的公共域名。IP 地址和包含用户名或密码的 URL 会被拒绝,返回INVALID_INPUT。 - 2保存签名密钥
它随创建响应返回,格式为
whsec_…,之后可用GET /webhooks/{id}?include_signing_secret=true再次查看。无法重新生成:如果泄露,请删除该 Webhook 并创建一个新的。不要与 API 密钥混淆。 - 3接收 POST
每个事件都带有
svix-id、svix-timestamp和svix-signature请求头,正文为 JSON 格式的事件。 - 4验证、保存并响应 2xx
先核对签名并保存事件,然后再响应。任何非 2xx 的响应或没有响应都会触发新的重试。
事件
| 事件 | 何时发送 |
|---|---|
order.created | 订单已创建。查询订单可获取最终金额和支付信息。 |
order.completed | 订单已支付并完成。在向您的客户交付商品之前,请先查询确认 status 为 completed。 |
order.expired | 订单未付款,已过期。 |
order.canceled | ByteMax 取消了订单,例如付款无法被接受并已退还给付款人。 |
order.updated | 订单数据变化但状态未变:已退款金额、已冻结金额或支付信息。这不表示已支付:订单支付后会发送 order.completed。 |
refund.created | 您的账户(通过 API 或控制台)发起的退款已登记。客服发起的退款只会体现在 order.updated 中。 |
refund.succeeded | 退款已完成:款项已退还给付款人。 |
refund.failed | 退款未能完成。 |
webhook.test | 测试事件,只发送到您测试的那个 Webhook。 |
每个事件包含 type(说明发生了什么)和 related_object:对象的 type(order、refund 或 webhook)、id 和 url(https://bytemax.exchange 上的查询路径)。处理前请查询该 url:即使事件延迟、重复或乱序到达,查询始终返回当前状态。
{
"id": "evt_example",
"type": "order.completed",
"created_at": "2026-09-10T13:05:00.000Z",
"related_object": {
"type": "order",
"id": "ord_example",
"url": "/api/v1/orders/ord_example"
}
}发布状态
GET /events 和 GET /events/{id} 会返回 publication_status,表示事件是否已从 ByteMax 发出。事件在您服务器上的结果见投递记录。
- pending
- 已记录,正在等待发送或重试。ByteMax 会尝试发布约 2 小时;之后事件变为
not_published。 - published
- 已提交投递。每次尝试的结果见投递记录。
- not_published
- 未发送:当时您的账户没有有效的 API 密钥、没有启用的 Webhook 订阅该事件类型,或发布失败。此事件无法重新发送;请查询订单或退款的当前状态。
接收并验证
在解析 JSON 之前,用密钥和原始请求正文进行验证。使用您所用语言的 Svix 官方库:它会核对签名以及 svix-timestamp 的时间容差。不要自行实现验证。
POST /webhooks/bytemax HTTP/1.1
Content-Type: application/json
svix-id: msg_2gkQ7Wc9cD6yYq1xkKz7GJb3wQm
svix-timestamp: 1789045500
svix-signature: v1,K5oZfzN95Z9UVu1EsfQmfVNQhUjM8G3xRk2w1vNq4T0=
{ "id": "evt_example", "type": "order.completed", ... }避免重复处理
事件可能多次到达,也可能乱序。五条规则可避免重复效果:
- 把事件的
id作为唯一键:响应前先保存,并忽略重复的事件。 - 不要依赖到达顺序:每收到一个事件,都查询订单或退款,并按查询返回的状态处理。
- 在同一个事务中保存事件的效果和已处理标记。
- 保存后才响应 2xx。如果保存失败,响应 5xx 以便再次接收。
- 耗时的工作放在响应之外:先入队,快速响应。
测试与重发
POST /webhooks/{id}/test-events,带上 Content-Type: application/json 并以 {} 作为正文,会向该 Webhook 发送一个 webhook.test;该 Webhook 必须处于启用状态。用 GET /events/{id} 跟踪发布情况,并在您的服务器上确认事件已到达且签名已通过。测试只证明投递和签名有效;要测试订单和退款的处理,请跟踪一笔真实订单。
投递失败会自动重试,最多 7 次:分别在 5 秒、5 分钟、30 分钟、2 小时、5 小时、10 小时和 10 小时后。连续失败 5 天的 Webhook 会被自动停用。每次尝试的历史记录在控制台和 GET /webhooks/{id}/deliveries 中。要重发特定事件,使用 POST /webhooks/{id}/redeliveries,最多 100 个 ID。即使部分事件被拒绝,该调用也会返回 200:请查看每一项的 status。
- queued
- 重发已被接受。
- rejected
- 未重发任何内容。原因见
code:EVENT_NOT_FOUND(ID 不存在)、EVENT_NOT_ACCEPTED(该 Webhook 未订阅此事件类型,或该测试事件属于另一个 Webhook)、EVENT_NOT_PUBLISHED(事件仍为pending或已是not_published)或REDELIVERY_REJECTED(投递服务拒绝)。pending事件在GET /events/{id}显示published后即可重发。 - retry
- 重发未执行或无法确认:调用已超时,或投递服务繁忙或出错。请重新发送;如果事件到达两次,事件的
id可以避免重复处理。
限制
- 每个账户最多 200 个 Webhook,每个 URL 一个。
- 事件在发布后 30 天内可查询。
- 列表使用
items和next_cursor:每页 30 条,最多 100 条。游标只能用于同一个列表和相同的筛选条件。
错误
身份验证、通用错误和调用限制见 API 页面。以下代码为 Webhook 专有:
WEBHOOK_URL_EXISTSHTTP 409- 您已有使用此 URL 的 Webhook。
WEBHOOK_LIMIT_REACHEDHTTP 409- 您已有 200 个 Webhook。请先删除一个,再创建新的。
WEBHOOK_NOT_ENABLED_FOR_V1HTTP 409- 测试和重发需要已启用的 v1 Webhook。请启用它;如果它来自旧版 API,请用
PATCH迁移至 v1。 WEBHOOK_VERSION_REQUIREDHTTP 400- 来自旧版 API 的 Webhook 只能在迁移至 v1 的同时修改:请发送
api_version: "v1"和新的events。 WEBHOOK_NOT_FOUNDHTTP 404- 您的账户中没有该 ID 的 Webhook。
EVENT_NOT_FOUNDHTTP 404- 没有该 ID 的事件。事件在发布后保留 30 天。
INVALID_CURSORHTTP 400- 游标不属于当前列表和筛选条件。请去掉
cursor重新开始。
返回 503 SERVICE_UNAVAILABLE 表示投递服务暂时不可用。请几秒后重试。
参考
共十个操作,全部需要 Authorization: Bearer <API_KEY>。最常用的三个完整列出;其余格式相同。
/webhooks创建 Webhook
创建 Webhook 并返回签名密钥。
urlstring必填: 是- 您的服务器接收事件的
https://地址,须使用公共域名。 eventsarray必填: 是- Webhook 接收的事件类型,不可重复:从事件中的 8 种订单和退款事件里选择一个或多个。
webhook.test不能订阅:它只会通过测试发送。 enabledboolean必填: 否- Webhook 是否接收事件。默认为
true。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
url | string | 是 | 您的服务器接收事件的 https:// 地址,须使用公共域名。 |
events | array | 是 | Webhook 接收的事件类型,不可重复:从事件中的 8 种订单和退款事件里选择一个或多个。webhook.test 不能订阅:它只会通过测试发送。 |
enabled | boolean | 否 | Webhook 是否接收事件。默认为 true。 |
curl --request POST "https://bytemax.exchange/api/v1/webhooks" \
--header "Authorization: Bearer <API_KEY>" \
--header "Content-Type: application/json" \
--data '{"url":"https://merchant.example/webhooks/bytemax","events":["order.created","order.completed","order.expired","order.canceled","order.updated","refund.created","refund.succeeded","refund.failed"],"enabled":true}'{
"id": "ep_example",
"url": "https://merchant.example/webhooks/bytemax",
"events": [
"order.created",
"order.completed",
"order.expired",
"order.canceled",
"order.updated",
"refund.created",
"refund.succeeded",
"refund.failed"
],
"enabled": true,
"api_version": "v1",
"signing_secret": "<WEBHOOK_SIGNING_SECRET>"
}/webhooks/{id}/test-events发送测试
向此 Webhook 发送一个 webhook.test。正文为空的 JSON 对象。
| 参数 | 位置 | 必填 |
|---|---|---|
id | 路径 | 是 |
curl --request POST "https://bytemax.exchange/api/v1/webhooks/ep_example/test-events" \
--header "Authorization: Bearer <API_KEY>" \
--header "Content-Type: application/json" \
--data '{}'{
"id": "evt_test_example",
"type": "webhook.test",
"created_at": "2026-09-10T13:05:00.000Z",
"related_object": {
"type": "webhook",
"id": "ep_example",
"url": "/api/v1/webhooks/ep_example"
},
"publication_status": "pending"
}/webhooks/{id}/redeliveries重发事件
向此 Webhook 重发最多 100 个事件。响应包含每个事件的结果。
| 参数 | 位置 | 必填 |
|---|---|---|
id | 路径 | 是 |
event_idsarray必填: 是- 要重发的事件 ID,1 到 100 个,不可重复。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
event_ids | array | 是 | 要重发的事件 ID,1 到 100 个,不可重复。 |
curl --request POST "https://bytemax.exchange/api/v1/webhooks/ep_example/redeliveries" \
--header "Authorization: Bearer <API_KEY>" \
--header "Content-Type: application/json" \
--data '{"event_ids":["evt_example"]}'{
"items": [
{
"event_id": "evt_example",
"status": "queued"
}
]
}其他操作
| 操作 | 名称 | 作用 |
|---|---|---|
GET/webhooks | 列出 Webhook | 列出您账户中的所有 Webhook,包括通过旧版 API 创建的 Webhook(api_version: "legacy")。 |
GET/webhooks/{id} | 查询 Webhook | 单个 Webhook。带 include_signing_secret=true 时包含签名密钥。 |
PATCH/webhooks/{id} | 修改 Webhook | URL、事件或启用状态。要把在旧版 API 中创建的 Webhook 迁移至 v1,请同时发送 api_version: "v1" 和新的 events。 |
DELETE/webhooks/{id} | 删除 Webhook | 删除该 Webhook。 |
GET/events | 列出事件 | 最近 30 天的事件及其发布状态。 |
GET/events/{id} | 查询事件 | 单个事件及其发布状态。 |
GET/webhooks/{id}/deliveries | 列出投递 | 某个 Webhook 的投递尝试:您的服务器返回 2xx 时为 succeeded,没有响应或返回其他状态码时为 failed。可按状态和类型筛选。 |
