Tổng Kho eSIM
出站 Webhook · 8 种事件

Webhook

通过 POST /v1/webhooks 注册 URL,PUT /v1/webhooks/{id} 更新。创建时 CHECK_HEALTH 需返回 2xx。事件使用独立于 API 密钥的专用密钥签名。

REST 才是最终事实来源。即使漏收事件,也可通过 GET /v1/orders?updated_since=… 恢复状态,请设计定期对账。

webhook-events.zh-CN.json

事件目录

event_type触发时机关键数据字段
order.completed订单已确认,eSIM 已就绪
order_idexternal_refitems[] (order_item_id, iccid)
order.failed订单未完成(交付失败等)
order_idexternal_refreason
esim.delivered已发放 eSIM 并准备好安装信息

使用 `order_id` 和 `external_ref` 对应订单。`lpa` 仅在 POST 前解密,不在投递记录中明文保存。载荷不含 QR 图片 URL,请从 `lpa` 生成,或通过 `GET /v1/esims/{id}` 获取 `qr_code_base64`。

order_item_idorder_idexternal_reficcidlpa
esim.activatedeSIM 已安装并激活(smdp enabled/in_use)

使用 `order_id` 和下单时发送的 `external_ref` 将 eSIM 对应到您的订单。

order_item_idorder_idexternal_reficcidactivated_at
esim.usage_threshold使用量达到 75%/90% 阈值

用 `order_id` 和 `external_ref` 对应订单。每次只发送新跨越的最高阈值(0→95% 只发送 90)。`threshold` 表示至少已超过此阈值。

order_item_idorder_idexternal_refthreshold (75/90)used_bytesdata_bytes
esim.expiring距离到期 3 天/1 天

使用 `order_id` 和下单时发送的 `external_ref` 将 eSIM 对应到您的订单。

order_item_idorder_idexternal_refdays_remaining (3/1)expires_at
wallet.low_balance钱包达到 25%/10% 警告阈值

`threshold` 为百分比(25/10),仅在有真实百分比时附带,否则省略(请自行比较 `balance`)。绝不以 `threshold` 发送原始 VND 金额。

threshold (25/10)balancecurrency
catalog.updated套餐目录变化:新增、修改、隐藏、基础价格变化或售罄

不包含价格,因为各组织价格不同;请调用 `GET /v1/catalog/packages` 获取自身价格。短窗口内(默认 10 分钟)的变化合并为一个 `changes[]` Webhook,同套餐多次变化仅保留一项。同步数百套餐不会触发数百 Webhook。适用于测试和真实环境。

changes[] (package_code, change: price|availability|package, occurred_at)change_count
json
{
  "event_id": "018f3c00-0000-7000-8000-0000000000c1",
  "event_type": "ping",
  "occurred_at": "2026-07-11T07:29:00Z",
  "is_testmode": true,
  "data": {
    "message": "TongKho eSIM webhook health check"
  }
}

事件封装与签名请求头

所有 Webhook 使用相同信封。以 `HMAC-SHA256(webhook_secret, timestamp\nraw_body)` 签名,放入 `X-TKE-Webhook-Signature`(小写 hex)。24 小时轮换窗口内另附 `X-TKE-Webhook-Signature-Old`。至少投递一次,合作伙伴须按 `event_id` 去重。

json
{
  "event_id": "018f3c00-0000-7000-8000-0000000000b0",
  "event_type": "order.completed",
  "occurred_at": "2026-07-11T07:30:00Z",
  "is_testmode": false,
  "data": {
    "order_id": "018f2b00-0000-7000-8000-00000000009a",
    "external_ref": "partner-cart-9931",
    "items": [
      {
        "order_item_id": "018f2b00-0000-7000-8000-000000000001",
        "iccid": "8984012345678901234"
      }
    ]
  }
}
字段说明
data按事件类型区分的载荷(见下文)。
event_id每个事件稳定且唯一的 UUID(用于去重)。
event_type事件类型。
is_testmode由测试密钥生成时为 true。
occurred_atISO-8601 UTC.

目录同步: catalog.updated

套餐新增、修改、隐藏、基础价格或库存变化时触发,变更聚合于 changes[] 数组中,数百项变更仅产生 一次 对每个接收端的投递。

json
{
  "event_id": "018f3c00-0000-7000-8000-0000000000c1",
  "event_type": "catalog.updated",
  "occurred_at": "2026-07-11T08:10:00Z",
  "is_testmode": false,
  "data": {
    "changes": [
      {
        "package_code": "THAY-BANG-MA-GOI-CUA-BAN",
        "change": "price",
        "occurred_at": "2026-07-11T08:02:11Z"
      },
      {
        "package_code": "VN_10GB_30D",
        "change": "availability",
        "occurred_at": "2026-07-11T08:04:52Z"
      },
      {
        "package_code": "JP_3GB_7D",
        "change": "package",
        "occurred_at": "2026-07-11T08:09:30Z"
      }
    ],
    "change_count": 3
  }
}
负载不含价格是有意设计

销售价格 按组织不同 ,发送统一价格既不准确也会泄露信息。此事件是 同步信号 :请调用 GET /v1/catalog/packages?market=… 刷新您的价格。

change 接受三个值: price (基础成本变化)、 availability (有货/缺货)、 package (新增/更新/隐藏)。遇到未知值也应触发同步。

签名、重试与去重依据 event_id 与标准事件相同,测试与正式环境按以下字段隔离 is_testmode.

验证 Webhook 签名

X-TKE-Signature 为使用 webhook_secret 与原始请求体字节计算的 HMAC-SHA256。

text
signing_string = timestamp + "\n" + raw_body   # timestamp = header X-TKE-Webhook-Timestamp
signature      = HMAC_SHA256(webhook_secret, signing_string)   → hex lowercase

# 检查时间有效性(±300 秒),防止重放。
# 对收到的原始字节签名,不要重新序列化 JSON。

重试退避策略

1 phút→5 phút→30 phút→2 giờ→6 giờ

非 2xx 响应或 5 秒超时后,在 24 giờ 窗口内按指数间隔重试:1 phút → 5 phút → 30 phút → 2 giờ → 6 giờ。

Webhook 的 4 条关键建议

1. 异步处理并立即返回 200
先将消息入队并在 1 秒内返回 200,避免同步执行耗时任务,5 秒后会超时。
2. 幂等与去重
事件至少投递一次,请使用 event_id 或 data.order_id / data.order_item_id 去重。
3. 分离正式与测试接收地址
每个密钥独立配置 Webhook,使用不同 URL,避免测试数据混入正式数据库。
4. 定期 REST 拉取对账
长时间网络故障可能造成事件丢失,请定期调用 GET /v1/orders?updated_since=… 补齐状态。

字段行为说明

esim.delivered.lpa 在 POST 前即时解密,不存明文。请用 LPA 生成二维码或调用 GET /v1/esims/{id}。

esim.usage_threshold: 仅触发新超出的最高阈值(0→95% 仅发送 threshold=90)。

wallet.low_balance.threshold 是百分比(25/10),并非 VND 金额。

catalog.updated 不含价格或详细库存,仅包含套餐代码及变更类型。