Appearance
V1 Callback Notifications
回调通知模块用于 Sugumart 主动向店铺配置的唯一回调 URL 推送商品审核、订单与物流状态变化。通知为异步事件提示;调用方需要最新完整资料时,应再调用对应的商品详情或订单详情接口查询。
概述
- 一个店铺只能配置一个回调 URL,通过
POST /openapi/v1/shop/callback设置。 - Sugumart 使用
POST向该 URL 发送application/json请求。 - 回调 URL 必须使用 HTTPS,不得包含 URL fragment 或内嵌账号密码。
- 通知只包含当前 Access Token 所绑定店铺的数据。
- 同一 URL 接收全部商品审核、订单和物流事件,不按事件分别配置 URL。
- 回调可能重复或乱序到达,接收方必须实现幂等处理和状态版本比较。
本文中的事件信封是 Sugumart 向接收端发送的 Webhook 请求体,不是 V1 API 的统一响应信封。接收端只需按本文约定返回
2xx,无需返回{ "success": true, "data": ... }。
2.3.1 通知事件
| 事件 | 触发条件 | data 类型 |
|---|---|---|
order.created | 新订单首次同步到 Sugumart 并可被当前店铺查询 | OrderEventData |
order.updated | 订单状态、付款状态、取消回复状态或其他对外订单字段发生变化 | OrderEventData |
order.cancelled | 订单取消交易完成,订单主状态变为 cancelled | OrderEventData |
product.review.submitted | 商品成功提交审核,review_status 变为 pending | ProductReviewEventData |
product.review.approved | 当前版本的商品审核通过,review_status 变为 approved | ProductReviewEventData |
product.review.rejected | 当前版本的商品审核未通过,review_status 变为 rejected | ProductReviewEventData |
logistics.shipped | 集运仓完成出库,履约状态变为 shipped | LogisticsEventData |
logistics.warehouse_received | 大陆快递包裹被集运仓签收入库,预报单状态变为 received | LogisticsEventData |
logistics.delivered | 已取得买家末端签收事实,订单展示状态变为 delivered | LogisticsEventData |
事件边界:
order.created只在订单首次建立时发送;后续同步变化使用order.updated。order.cancelled是取消完成通知。仅进入InCancel或等待卖家确认时,使用order.updated。product.review.submitted表示平台已受理一次新的审核提交,不表示审核已经通过。- 同一商品重新提交审核时会产生新的
review_version;通过或拒绝事件只对应其携带的审核版本。 - 商品审核通过不表示商品已经上架;审核通过后仍需调用商品上架接口。
logistics.shipped表示包裹已从集运仓出库,不表示买家已经签收。logistics.warehouse_received表示大陆快递包裹已进入集运仓,不表示跨境运输完成。logistics.delivered只以露天末端配送的签收、已送达或买家取货事实为准。WMS 的“到达统仓”不得触发该事件。
2.3.2 请求格式
Request Headers
| Header | 类型 | 说明 |
|---|---|---|
Content-Type | string | 固定为 application/json |
User-Agent | string | Sugumart 回调客户端标识 |
X-Sugumart-Event-Id | string | 事件唯一 ID,与请求体 id 相同 |
X-Sugumart-Event | string | 事件名称,与请求体 event 相同 |
X-Sugumart-Timestamp | string | 签名生成时间,Unix 秒 |
X-Sugumart-Signature | string | HMAC-SHA256 签名的小写十六进制结果 |
Request Body
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | string | 是 | 事件唯一 ID;同一次事件重试时保持不变 |
event | string | 是 | 事件名称,取值见通知事件表 |
api_version | string | 是 | 固定为 v1 |
shop_id | string(uuid) | 是 | Sugumart 店铺 ID |
occurred_at | string | 是 | 业务状态变化时间,ISO 8601 UTC |
data | object | 是 | 事件数据;结构由事件类型决定 |
Request Example
json
{
"id": "evt_01K1W27D9A7M6T0C3P4R8H2N5Q",
"event": "logistics.warehouse_received",
"api_version": "v1",
"shop_id": "82dbf488-3467-4825-afc4-31dbc91c115c",
"occurred_at": "2026-08-04T08:30:00.000Z",
"data": {
"order_id": "87eb4314-22a0-4d72-861e-9118f952191b",
"ruten_order_id": "24080412345678",
"fulfillment_id": "9b2d0bb1-8d4e-41ac-9c65-3a6fd514f98d",
"fulfillment_no": "RT202608040001",
"status": "received",
"previous_status": "shipped",
"tracking_number": "SF123456789CN",
"warehouse_code": "20089",
"status_at": "2026-08-04T08:29:42.000Z",
"resource_url": "/openapi/v1/orders/87eb4314-22a0-4d72-861e-9118f952191b"
}
}2.3.3 订单事件数据
order.created、order.updated 和 order.cancelled 使用 OrderEventData。
OrderEventData
| 字段 | 类型 | 说明 |
|---|---|---|
order_id | string(uuid) | Sugumart 订单 ID |
ruten_order_id | string | 露天订单号 |
status | string | 当前订单主状态,见 V1 状态附录 |
previous_status | string | null | 变化前订单主状态;order.created 时为 null |
view_status | string | null | 当前订单展示状态;尚不能计算时为 null |
ruten_order_status | string | null | 最近观察到的露天订单状态 |
ruten_pay_status | string | null | 最近观察到的露天付款状态 |
ruten_respond_status | string | null | 最近观察到的取消回复状态 |
changed_fields | string[] | 本次发生变化的公开字段路径;创建事件为空数组 |
status_at | string | 当前状态生效或被观察到的时间,ISO 8601 UTC |
resource_url | string | 获取最新订单详情的 V1 相对路径 |
order.created Example
json
{
"id": "evt_01K1W21GTY66X5HZGF9WVHZA6A",
"event": "order.created",
"api_version": "v1",
"shop_id": "82dbf488-3467-4825-afc4-31dbc91c115c",
"occurred_at": "2026-08-04T08:00:00.000Z",
"data": {
"order_id": "87eb4314-22a0-4d72-861e-9118f952191b",
"ruten_order_id": "24080412345678",
"status": "pending_payment",
"previous_status": null,
"view_status": null,
"ruten_order_status": "Unpaid",
"ruten_pay_status": "Unpaid",
"ruten_respond_status": null,
"changed_fields": [],
"status_at": "2026-08-04T08:00:00.000Z",
"resource_url": "/openapi/v1/orders/87eb4314-22a0-4d72-861e-9118f952191b"
}
}order.updated Example
json
{
"id": "evt_01K1W23WTPAQGM1CH58G35MT93",
"event": "order.updated",
"api_version": "v1",
"shop_id": "82dbf488-3467-4825-afc4-31dbc91c115c",
"occurred_at": "2026-08-04T08:10:00.000Z",
"data": {
"order_id": "87eb4314-22a0-4d72-861e-9118f952191b",
"ruten_order_id": "24080412345678",
"status": "pending_shipment",
"previous_status": "pending_payment",
"view_status": "pending_shipment",
"ruten_order_status": "ReadyToShip",
"ruten_pay_status": "Paid",
"ruten_respond_status": null,
"changed_fields": ["status", "view_status", "ruten_order_status", "ruten_pay_status"],
"status_at": "2026-08-04T08:09:55.000Z",
"resource_url": "/openapi/v1/orders/87eb4314-22a0-4d72-861e-9118f952191b"
}
}order.cancelled Example
json
{
"id": "evt_01K1W2520E2KYHSMV8VB8TWR56",
"event": "order.cancelled",
"api_version": "v1",
"shop_id": "82dbf488-3467-4825-afc4-31dbc91c115c",
"occurred_at": "2026-08-04T08:20:00.000Z",
"data": {
"order_id": "87eb4314-22a0-4d72-861e-9118f952191b",
"ruten_order_id": "24080412345678",
"status": "cancelled",
"previous_status": "pending_shipment",
"view_status": null,
"ruten_order_status": "Cancelled",
"ruten_pay_status": "Paid",
"ruten_respond_status": "Approved",
"changed_fields": ["status", "ruten_order_status", "ruten_respond_status", "cancelled_at"],
"status_at": "2026-08-04T08:19:58.000Z",
"resource_url": "/openapi/v1/orders/87eb4314-22a0-4d72-861e-9118f952191b"
}
}2.3.4 物流事件数据
logistics.shipped、logistics.warehouse_received 和 logistics.delivered 使用 LogisticsEventData。
LogisticsEventData
| 字段 | 类型 | 说明 |
|---|---|---|
order_id | string(uuid) | Sugumart 订单 ID |
ruten_order_id | string | 露天订单号 |
fulfillment_id | string(uuid) | Sugumart 履约 ID |
fulfillment_no | string | Sugumart 集货单号 |
status | string | 当前事件对应的物流状态 |
previous_status | string | null | 变化前的同类物流状态;无法确定时为 null |
tracking_number | string | null | 当前物流阶段的追踪号;没有追踪号时为 null |
warehouse_code | string | null | 露天物流仓 code;不适用时为 null |
status_at | string | 当前物流状态发生时间,ISO 8601 UTC |
resource_url | string | 获取最新订单详情的 V1 相对路径 |
各事件的 status:
| 事件 | status | 时间来源 |
|---|---|---|
logistics.shipped | shipped | 集运仓出库时间 |
logistics.warehouse_received | received | 集运仓签收入库时间 |
logistics.delivered | delivered | 买家末端签收或取货时间 |
logistics.shipped Example
json
{
"id": "evt_01K1W29C4A91AA4YXYG8RMY8RQ",
"event": "logistics.shipped",
"api_version": "v1",
"shop_id": "82dbf488-3467-4825-afc4-31dbc91c115c",
"occurred_at": "2026-08-04T09:00:00.000Z",
"data": {
"order_id": "87eb4314-22a0-4d72-861e-9118f952191b",
"ruten_order_id": "24080412345678",
"fulfillment_id": "9b2d0bb1-8d4e-41ac-9c65-3a6fd514f98d",
"fulfillment_no": "RT202608040001",
"status": "shipped",
"previous_status": "created",
"tracking_number": "TW123456789",
"warehouse_code": "20089",
"status_at": "2026-08-04T08:59:42.000Z",
"resource_url": "/openapi/v1/orders/87eb4314-22a0-4d72-861e-9118f952191b"
}
}logistics.warehouse_received Example
json
{
"id": "evt_01K1W2D8X1Q0K4V7N5J3P6ABCD",
"event": "logistics.warehouse_received",
"api_version": "v1",
"shop_id": "82dbf488-3467-4825-afc4-31dbc91c115c",
"occurred_at": "2026-08-05T06:30:00.000Z",
"data": {
"order_id": "87eb4314-22a0-4d72-861e-9118f952191b",
"ruten_order_id": "24080412345678",
"fulfillment_id": "9b2d0bb1-8d4e-41ac-9c65-3a6fd514f98d",
"fulfillment_no": "RT202608040001",
"status": "received",
"previous_status": "shipped",
"tracking_number": "SF123456789CN",
"warehouse_code": "20089",
"status_at": "2026-08-05T06:29:42.000Z",
"resource_url": "/openapi/v1/orders/87eb4314-22a0-4d72-861e-9118f952191b"
}
}logistics.delivered Example
json
{
"id": "evt_01K1W2GJTV7HCNYYDTRB613KTV",
"event": "logistics.delivered",
"api_version": "v1",
"shop_id": "82dbf488-3467-4825-afc4-31dbc91c115c",
"occurred_at": "2026-08-08T02:15:00.000Z",
"data": {
"order_id": "87eb4314-22a0-4d72-861e-9118f952191b",
"ruten_order_id": "24080412345678",
"fulfillment_id": "9b2d0bb1-8d4e-41ac-9c65-3a6fd514f98d",
"fulfillment_no": "RT202608040001",
"status": "delivered",
"previous_status": "shipped",
"tracking_number": "TW123456789",
"warehouse_code": "20089",
"status_at": "2026-08-08T02:14:36.000Z",
"resource_url": "/openapi/v1/orders/87eb4314-22a0-4d72-861e-9118f952191b"
}
}2.3.5 商品审核事件数据
product.review.submitted、product.review.approved 和 product.review.rejected 使用 ProductReviewEventData。
ProductReviewEventData
| 字段 | 类型 | 说明 |
|---|---|---|
product_id | string(uuid) | Sugumart 商品 ID |
name | string | 当前商品名称 |
status | string | 当前商品生命周期状态:pending_review、approved 或 rejected |
review_status | string | 当前审核状态:pending、approved 或 rejected |
previous_review_status | string | 本次变化前的审核状态:draft、pending、approved 或 rejected |
review_version | integer | 商品审核提交版本,正整数;同一商品每次重新提交审核时递增 |
rejected_reason | string | null | 审核拒绝原因;仅 product.review.rejected 非空,最长 500 字符 |
submitted_at | string | 当前审核版本的提交时间,ISO 8601 UTC |
reviewed_at | string | null | 当前审核版本的决定时间,ISO 8601 UTC;审核中为 null |
status_at | string | 当前审核状态生效时间,ISO 8601 UTC;提交事件等于 submitted_at,审核决定事件等于 reviewed_at |
resource_url | string | 获取最新商品详情的 V1 相对路径 |
乱序处理规则:
- 同一
product_id应先比较review_version,版本较小的事件不得覆盖版本较大的审核结果。 review_version相同时,pending早于approved或rejected;最终决定事件不得被同版本的提交事件覆盖。approved与rejected不会同时成为同一审核版本的最终结果;如查询结果与回调不一致,以商品详情接口为准。
product.review.submitted Example
json
{
"id": "evt_01K1W2KX6CPAH7Y4M8V3Q9T2NR",
"event": "product.review.submitted",
"api_version": "v1",
"shop_id": "82dbf488-3467-4825-afc4-31dbc91c115c",
"occurred_at": "2026-08-05T07:30:00.000Z",
"data": {
"product_id": "a7b3c8df-bb08-46f0-9ca2-5597a32e0404",
"name": "轻量防泼水通勤背包",
"status": "pending_review",
"review_status": "pending",
"previous_review_status": "draft",
"review_version": 1,
"rejected_reason": null,
"submitted_at": "2026-08-05T07:30:00.000Z",
"reviewed_at": null,
"status_at": "2026-08-05T07:30:00.000Z",
"resource_url": "/openapi/v1/products/a7b3c8df-bb08-46f0-9ca2-5597a32e0404"
}
}product.review.approved Example
json
{
"id": "evt_01K1W2Q8EX6BH4Y7S9C5N0M3VT",
"event": "product.review.approved",
"api_version": "v1",
"shop_id": "82dbf488-3467-4825-afc4-31dbc91c115c",
"occurred_at": "2026-08-05T08:10:00.000Z",
"data": {
"product_id": "a7b3c8df-bb08-46f0-9ca2-5597a32e0404",
"name": "轻量防泼水通勤背包",
"status": "approved",
"review_status": "approved",
"previous_review_status": "pending",
"review_version": 1,
"rejected_reason": null,
"submitted_at": "2026-08-05T07:30:00.000Z",
"reviewed_at": "2026-08-05T08:10:00.000Z",
"status_at": "2026-08-05T08:10:00.000Z",
"resource_url": "/openapi/v1/products/a7b3c8df-bb08-46f0-9ca2-5597a32e0404"
}
}product.review.rejected Example
json
{
"id": "evt_01K1W2T91P7DZ5C4A8M6H3Q0NX",
"event": "product.review.rejected",
"api_version": "v1",
"shop_id": "82dbf488-3467-4825-afc4-31dbc91c115c",
"occurred_at": "2026-08-06T03:20:00.000Z",
"data": {
"product_id": "a7b3c8df-bb08-46f0-9ca2-5597a32e0404",
"name": "轻量防泼水通勤背包",
"status": "rejected",
"review_status": "rejected",
"previous_review_status": "pending",
"review_version": 2,
"rejected_reason": "主图包含无法识别的促销文字,请修改后重新提交。",
"submitted_at": "2026-08-06T02:45:00.000Z",
"reviewed_at": "2026-08-06T03:20:00.000Z",
"status_at": "2026-08-06T03:20:00.000Z",
"resource_url": "/openapi/v1/products/a7b3c8df-bb08-46f0-9ca2-5597a32e0404"
}
}2.3.6 签名验证
设置回调配置时取得的 secret 用于验证通知来源。签名原文为:
text
X-Sugumart-Timestamp + "." + raw_request_body计算方式:
text
HMAC-SHA256(secret, signature_payload)计算结果编码为小写十六进制字符串,并与 X-Sugumart-Signature 做常量时间比较。
接收方应执行以下检查:
- 使用未经解析或重新序列化的原始请求体计算签名。
- 拒绝签名不匹配的请求。
- 检查
X-Sugumart-Timestamp与服务器时间的偏差,防止重放攻击。 - 使用
X-Sugumart-Event-Id或请求体id去重。
2.3.7 响应与重试
接收端完成签名验证并持久化事件后,应返回任意 2xx 响应。响应体可为空,Sugumart 不读取业务字段。
| 接收端结果 | Sugumart 行为 |
|---|---|
2xx | 本次投递成功,不再重试 |
非 2xx | 本次投递失败,进入重试 |
| 网络错误或连接失败 | 本次投递失败,进入重试 |
| 响应超时 | 本次投递失败,进入重试 |
重试约定:
- 同一事件的所有重试保持相同
id、event、occurred_at和请求体。 - 每次重试会重新生成
X-Sugumart-Timestamp和X-Sugumart-Signature。 - 重试可能造成事件乱序,接收方应使用
status_at判断业务状态先后。 - 重试次数与间隔属于平台运行策略,不作为固定 API 契约;接收方不得依赖特定重试次数。
2.3.8 幂等与数据一致性
- 接收方应以事件
id作为幂等键;已成功处理的事件再次到达时直接返回2xx。 - 不得只按
order_id + event去重,同一订单可能多次产生order.updated。 - 商品审核事件不得只按
product_id + event去重;同一商品可能经过多个review_version,每次投递仍以事件id去重。 - 回调是状态变化提示,不是完整资源快照。需要完整字段时,使用
resource_url对应的商品详情或订单详情接口查询。 - 回调与查询结果不一致时,以对应商品详情或订单详情接口
data中的最新结果为准。 - 未识别的
event或状态值应记录为可见错误,不应自动映射为已有状态。 - 接收方不应在回调处理中记录完整签名 Secret、买家证件号码或其他敏感资料。