---
title: Webhook
description: Coinetic 加密支付 Webhook 指南：配置 Endpoint、校验 HMAC 签名、按事件 ID 去重、失败重试、投递顺序、事件类型说明，以及 Webhook 签名密钥的无停机轮换。
url: https://coinetic.money/zh/docs/webhooks/
lang: zh-Hans
---

# Webhook

账单发生变化时，Coinetic 会向您的服务器发送 POST 请求。Webhook 只是“去查看账单”的信号，而不是付款凭证。

## 配置

- 后台 →「密钥与 Webhook」→「添加」。URL 必须为 `https://`，指向公网地址和 443 端口（沙盒中还可以使用 `http://localhost`）。
- Endpoint 的签名密钥只显示一次。最多可配置 5 个有效 Endpoint，每个事件都会发送到所有 Endpoint。

## 请求

```http
POST /webhooks/coinetic HTTP/1.1
Content-Type: application/json
User-Agent: cryptopay-webhooks/1
X-Webhook-Id: evt_3f9c…
X-Webhook-Timestamp: 1760000000
X-Webhook-Signature: v1=5d1e…

{"id":"evt_3f9c…","type":"invoice.paid","created_at":"2026-10-12T15:04:05Z","livemode":true,"data":{"invoice_id":"inv_x7k2…","order_id":"ORDER-1001","status":"paid","paid_amount":"49990000"}}
```

重试时 `X-Webhook-Id` 保持不变。轮换签名密钥期间，`X-Webhook-Signature` 中会包含两个以逗号分隔的签名。技术名称（`cryptopay-webhooks/1`）属于历史遗留，您无需在自己的系统中修改。

## 校验签名

```
expected = "v1=" + hex(HMAC-SHA256(webhook_secret, X-Webhook-Timestamp + "." + raw_body))
```

- 使用**原始请求体**，即 JSON 解析之前的字节。
- 检查 `X-Webhook-Timestamp` 与当前时间相差不超过 5 分钟。
- 按逗号拆分 `X-Webhook-Signature`。只要**任意**一段与预期值一致（使用恒定时间比较），签名即有效。
- 不一致时返回 `401`，且不做任何处理。

参考示例：签名密钥 `whsec-0123456789abcdef0123456789abcdef`，timestamp `1760000000`，请求体 `{"id":"evt_1","type":"invoice.paid"}` → `v1=aceeb56215ba09e7a2923d48f89afb8331f422f2d0a8cf60f76fef69fe06a6a4`。

## 处理

```
校验签名 → 按 X-Webhook-Id 去重 → GET /v1/invoices/{invoice_id}
→ 按 API 返回的状态更新订单（只发货一次）→ 返回 2xx
```

- **去重。**同一事件可能送达多次。请保存已处理的 `X-Webhook-Id`；“已发货”标记应与发货操作在同一个事务中写入。
- **快速响应**，10 秒以内。耗时任务请放入队列处理。
- **响应码：**`2xx` 表示已接收；`410 Gone` 表示不再重试本次投递；其他任何响应码（包括 `3xx`，不会跟随重定向）或超时都会触发重试。
- 请忽略未知的字段和事件类型，后续还会增加新的内容。

四种语言的现成处理程序见[快速开始](https://coinetic.money/zh/docs/quickstart/#webhook)。

## 重试

10 秒 → 30 秒 → 2 分钟 → 10 分钟 → 30 分钟 → 1 小时 → 3 小时 ×7 → 1 小时，间隔带少量随机抖动。最后一次尝试大约在一天之后。投递失败的事件可以重新发送：后台 →「Webhook」→「重新发送」。

## 顺序

**同一张账单**的事件按顺序投递到 Endpoint：上一条投递仍在等待重试时，下一条不会发出。无论上一条最终成功与否，后续事件都会继续发送。因此请以 `GET /v1/invoices/{id}` 返回的状态为准，而不是依赖事件的先后顺序。不同账单的事件彼此独立。

## 事件类型

| 事件 | 触发时机 |
| --- | --- |
| `invoice.detected` | 链上已看到付款 |
| `invoice.confirming` | 付款已打包进区块，正在累积确认数 |
| `invoice.paid` | 已支付并确认，可以发货（需再次核实）。`data.late: true` 表示稳定币在过期后到账并已自动计入 |
| `invoice.overpaid` | 已支付且有多付 |
| `invoice.underpaid` | 已确认金额少于应付金额 |
| `invoice.awaiting_payment` | 账单重新等待付款（例如唯一一笔付款因链重组被回滚后） |
| `invoice.expired` | 已过期且未付款 |
| `invoice.late_payment` | 过期后使用波动性资产付款 |
| `invoice.manual_review` | 付款正在接受 AML 审查 |
| `invoice.payment_held` | 付款被 AML 暂扣（隔离） |
| `invoice.reorged` | 包含付款的区块被回滚，状态已重新计算（例如 `paid` → `confirming`） |
| `invoice.payment_after_close` | 付款转入了已关闭的账单：不计入订单，可申请退款 |
| `invoice.cancelled` | 账单因执行退款而在未付款的情况下关闭 |
| `refund.sent` | 退款已执行，`data.refund` 中包含金额、地址和 `tx_hash` |

计费事件（`billing.*`）与平台手续费账单相关：已开具、已支付、已逾期、服务暂停及恢复。

> **数字商品与 `invoice.reorged`.** 在 `paid` 之后发生链重组的情况很少见，但确实存在。如果您的商品是即时交付的，请订阅此事件，并将此类情况转交客服处理。

## 轮换签名密钥

后台 →「更换签名密钥」：Coinetic 开始同时使用**新旧两个**签名密钥签名。

- 在服务器上添加新密钥（同时接受两个密钥）。
- 点击「完成轮换」，旧密钥随即被删除。
- 从服务器上移除旧密钥。
