Webhook 回调
平台在关键事件发生时,向合作伙伴配置的 callback URL 发送 POST 请求。验签方式与入站 Open API 相同。
配置
- 后台「API 凭证」页面填写回调 URL
- 或通过 API:
POST /open-api/v1/webhook/set
{ "callbackUrl": "https://your-server.com/openapi/callback" }事件类型
对外 Webhook 仅 3 种 eventType(与 eSIM Access / 亿点等供应商入站 notifyType 不同)。平台将多种履约/Profile 变更聚合为 DELIVERY_UPDATED,payload 结构与 查单 一致,请根据 orderStatus / deliveryStatus / items[].profiles[] 判断具体变化。
| eventType | 说明 |
|---|---|
ORDER_PAID | 余额支付成功,订单进入履约 |
DELIVERY_UPDATED | 订单发货或 Profile 状态变更(见下方触发场景)。仅在该订单实际发生变更时推送 |
REFUND_COMPLETED | 退款已入账(含同步退款与异步亿点售后完成) |
如何识别 DELIVERY_UPDATED 具体含义
Webhook Body 是全量订单快照(与查单一致),不是增量 patch。除阅读下方状态字段外,请优先看 changeHints 数组:
- 平台将本次 payload 与同一
orderNo+ 同一eventType上一次成功投递的快照对比,生成变更摘要 - 首次
DELIVERY_UPDATED或无历史快照时,按「相对空快照」推断(例如首次出现iccid→ICCID_READY) - 一次推送可含多个 hint(如同时
ICCID_READY+DELIVERY_STATUS_CHANGED) - 重试投递时
changeHints与首次相同(基于同一快照对比结果)
changeHints 取值说明
| changeHints | 含义 | 建议对接动作 |
|---|---|---|
ICCID_READY | 子项或 Profile 首次出现 ICCID | 展示卡号、触发安装引导 |
QR_CODE_READY | 首次出现安装二维码 URL | 展示 QR |
ACTIVATION_CODE_READY | 首次出现 SM-DP+ 激活码 | 展示 LPA 串 |
DELIVERY_STATUS_CHANGED | 主单 deliveryStatus 变化 | 更新发货进度 UI |
ORDER_STATUS_CHANGED | 主单 orderStatus 变化 | 更新订单态 |
ITEM_STATUS_CHANGED | 子项 itemStatus 变化 | 更新子单态 |
PROFILE_LIFECYCLE_CHANGED | Profile lifecycleStatus 变化 | 更新单卡生命周期 |
ESIM_STATUS_CHANGED | Profile esimStatus 变化 | 记录供应商 eSIM 态 |
SMDP_STATUS_CHANGED | Profile smdpStatus 变化 | 记录安装过程态 |
PLAN_USE_STARTED | planUseStatus 变为 STARTED | 套餐开始使用 |
PLAN_USE_ENDED | planUseStatus 变为 ENDED | 套餐结束 |
USAGE_UPDATED | 流量字段变化 | 更新用量展示(实时用量可调 profile/usage) |
PROFILE_CANCELLED | Profile cancelled 变为 true | 标记卡已取消 |
ORDER_SNAPSHOT_SYNC | 有推送但未命中更细规则 | 全量覆盖本地快照 |
ORDER_PAID | ORDER_PAID 事件 | 标记已支付 |
REFUND_COMPLETED | REFUND_COMPLETED 事件 | 标记退款完成 |
REFUND_AMOUNT_UPDATED | 累计退款额增加 | 更新退款金额 |
状态字段枚举(平台归一化)
主单 deliveryStatus
| 值 | 含义 |
|---|---|
PENDING | 未发货 |
PARTIAL | 部分 Profile 已就绪 |
DELIVERED | 全部 Profile 已就绪 |
FAILED | 发货失败 |
主单 orderStatus(节选)
| 值 | 含义 |
|---|---|
PAID | 已支付,待履约 |
FULFILLING | 履约中 |
COMPLETED | 订单完成 |
REFUNDING | 退款处理中(通常不单独推 Webhook,以查单为准) |
REFUNDED | 已退款 |
子项 itemStatus
| 值 | 含义 |
|---|---|
PENDING | 待发码 |
DELIVERED | 已发码 |
ACTIVATING | 激活中 |
ACTIVATED | 已激活 |
FAILED | 失败 |
REFUNDED | 已退款 |
Profile lifecycleStatus(推荐对接方优先使用)
| 值 | 含义 |
|---|---|
PENDING | 待发码 |
DELIVERED | 已发码(有 iccid/qr) |
ACTIVATING | 安装/激活进行中 |
ACTIVATED | 已激活 |
CANCELLED | 已取消 |
esimStatus / smdpStatus 为供应商原文,不同渠道取值不同,仅作补充;判断业务阶段请优先 lifecycleStatus + changeHints。
对接建议(伪代码)
on webhook(payload):
if payload.eventType == "DELIVERY_UPDATED":
for hint in payload.changeHints:
handle(hint) // 按 hint 表分支
upsertLocalSnapshot(payload) // 仍建议存全量快照,便于对账
elif payload.eventType == "REFUND_COMPLETED":
markRefunded(payload)
elif payload.eventType == "ORDER_PAID":
markPaid(payload)DELIVERY_UPDATED 触发场景(平台内部)
以下场景在平台侧都会以 DELIVERY_UPDATED 推送(不会单独再发 ESIM_STATUS、DATA_USAGE 等 eventType):
| 场景 | 典型 payload 变化 |
|---|---|
| ICCID / 二维码 / 激活码就绪 | items[].iccid、qrCodeUrl、activationCode;deliveryStatus 可能变为 PARTIAL / DELIVERED |
| Profile 安装 / eSIM 状态变更 | items[].profiles[].esimStatus、smdpStatus |
| 套餐开始使用 / 结束 | items[].profiles[].planUseStatus、planUseStartTime、planUseEndTime |
| 流量 / 有效期告警 | items[].profiles[].dataUsageBytes、remainDataBytes、usagePercent 等 |
| Profile 激活 / 生命周期 | items[].profiles[].activated、lifecycleStatus |
| 退款审核拒绝等导致 Profile 状态回写 | items[].profiles[].cancelled 等;退款入账另见 REFUND_COMPLETED |
当前不会单独推送的事件
| 期望场景 | 说明 |
|---|---|
REFUNDING 退款处理中 | 退款接口可能返回 REFUNDING;入账前不会单独 Webhook,请轮询查单或等 REFUND_COMPLETED |
| 充值审核通过 | 账户充值走独立流程,不在订单 Webhook 范围 |
| 订单支付失败 | 下单失败不落单,无 Webhook |
推送原则
- 平台按订单维度推送,对外统一为平台订单 / Profile 模型
- 仅推送该订单实际发生的状态变更;无变更则不推送
- 不同套餐可能支持的状态字段不同,以 payload 中
items[].profiles[]实际返回为准
请求格式
Header 与入站一致(平台使用同一 AppKey 对应 Secret 签名)。
Body 示例(支付成功):
{
"eventType": "ORDER_PAID",
"transactionId": "your-transaction-id",
"orderNo": "ORD20260101001",
"orderId": 123456,
"payStatus": "PAID",
"deliveryStatus": "PENDING",
"orderStatus": "PAID",
"currency": "USD",
"items": [
{
"itemNo": "ITEM001",
"packageCode": "PKG001",
"quantity": 1,
"itemStatus": "PAID"
}
]
}发货 / Profile 变更示例(DELIVERY_UPDATED):
{
"eventType": "DELIVERY_UPDATED",
"transactionId": "your-transaction-id",
"orderNo": "ORD20260101001",
"orderId": 123456,
"payStatus": "PAID",
"deliveryStatus": "DELIVERED",
"orderStatus": "COMPLETED",
"currency": "USD",
"changeHints": ["ICCID_READY", "QR_CODE_READY", "DELIVERY_STATUS_CHANGED", "PROFILE_LIFECYCLE_CHANGED"],
"items": [
{
"itemNo": "ITEM001",
"packageCode": "PKG001",
"quantity": 1,
"itemStatus": "DELIVERED",
"iccid": "8901234567890123456",
"qrCodeUrl": "https://cdn.example.com/qr/xxx.png",
"profiles": [
{
"iccid": "8901234567890123456",
"esimTranNo": "E202506180001",
"esimStatus": "IN_USE",
"lifecycleStatus": "ACTIVATED",
"planUseStatus": "STARTED",
"dataUsageBytes": 524288000
}
]
}
]
}退款完成示例:
{
"eventType": "REFUND_COMPLETED",
"transactionId": "your-transaction-id",
"orderNo": "ORD20260101001",
"orderId": 123456,
"payStatus": "REFUNDED",
"deliveryStatus": "DELIVERED",
"orderStatus": "REFUNDED",
"refundAmount": 3.50,
"currency": "USD",
"items": [
{
"itemNo": "ITEM001",
"packageCode": "PKG001",
"quantity": 1,
"itemStatus": "REFUNDED"
}
]
}完整 Schema 见 OpenAPI Schema 中 WebhookCallbackPayload。
重试
投递失败会写入 Outbox 并重试(默认最多 8 次,指数退避)。请保证接口 幂等(按 orderNo + eventType 去重)。
接入建议
- 验签通过后再处理业务
- 快速返回 HTTP 200
- 异步处理内部逻辑