跳到主要内容

接收订阅事件

云应用可以接收商家后台的订单、会员、团组等订阅事件。应用保持私有,无需开放页面访问权限,也不需要商家复制推送密钥。

商家怎么配置​

  1. 创建云应用,即可提前配置订阅,无需先上线。
  2. 商家后台 → 设置 → 开放平台 → 订阅事件 → 新建订阅。
  3. 选择事件,将「推送到」设为「云应用」,选择当前商家的应用并保存。未上线的应用显示「等待应用上线」。
  4. 让开发助手按本页添加事件接收逻辑,验证并正式上线后,应用自动开始接收新事件,无需重新配置订阅。
  5. 在订阅的「查看日志」中查看推送结果。成功表示应用明确确认接收;失败不会显示为成功。

每条订阅只关联所选事件和应用。原有外部地址订阅继续保留,多个接收方分别投递、分别记录。应用更新正式版本后,事件自动交给当前正式版本,无需修改订阅;预览环境、本地电脑不接收生产业务事件。未上线期间的通知记为「未推送(等待应用上线)」,不反复重试,也不会在上线后自动补发;需要历史数据时,由应用首次初始化时查询补齐。暂停或过期的应用不能接收。

测试环境能收到通知吗?​

不能。真实业务事件只投递到当前正式版,测试版、预览和本地均不接收。即使应用已经有正式版,尚未上线的新代码也不会收到通知。

开发助手应在联调和交付预览时主动说明这个区别,先用模拟事件验证处理逻辑,不让客户在测试版反复等待或重配订阅。正式上线须先获得用户授权,随后用约定的测试业务验证真实投递;模拟测试通过不等于真实投递已成功。

开发约定(云开发与 WorkBuddy 通用)​

  • 使用 JavaScript/Python SDK 0.2.0 或更新版本,安装方式见 SDK 安装。纯静态应用需要增加 Node.js 或 Python 后端才能接收事件。
  • 后端实现固定路由 POST /__xdb/events。此路由先用事件 SDK 验签,不使用员工登录中间件;其他页面和接口继续使用原有鉴权。订阅推送没有当前员工身份,不得伪造员工。
  • 平台先验证 Core 的来源、商家、应用、事件类型及正文,再投递到正式应用的内部端口。应用用 SDK 对原始请求体验签,运行环境自动提供凭据,不把凭据复制到前端或让客户配置。
  • 平台只接受 HTTP 200、JSON { "accepted": true, "eventId": "收到的事件 id" }。登录页面、跳转、普通网页和没有匹配事件编号的响应均视为失败。
  • 验签后先持久化入队,或在数据库事务中完成必要处理,再返回回执。不能先返回成功再用内存异步处理,否则进程退出会丢事件。
  • 同一次推送失败最多尝试 3 次,重试保持同一个 id;请求超时不意味着业务未执行。以 id 建数据库唯一约束,去重记录与业务写入放在同一事务,重复已完成事件返回同一回执。不同业务变更仍可能生成不同事件;按业务单号、变更类型和版本处理,不把同一订单的后续更新全部丢弃。
  • 失败会记录到原订阅日志;当前没有无限补发、历史重放或永久保存事件队列的保证。重要同步保留业务对账/补拉机制,由应用自动完成,勿承诺绝不丢失或严格有序。
  • 本地/预览使用构造数据及测试凭据做自动化测试,不把生产推送改到开发电脑;完整联调在正式应用中使用明确的测试业务。

事件结构​

{
"id": "evt_0123456789abcdef0123456789abcdef01234567",
"appId": "app_0123456789abcdefabcd",
"tenantId": "385",
"subscriptionId": "12",
"type": "order.changed",
"receivedAt": "2026-10-28T08:00:00.000Z",
"payload": {
"type": "order_info",
"exchange_type": "1",
"request_data": "{\"orderNum\":\"示例订单号\"}"
}
}

receivedAt 为平台接收时间,不是业务发生时间。payload 沿用原订阅字段:表单通知转为键值对象,request_data 仍是 JSON 字符串;原本为 JSON 的通知保持原 JSON 对象。不猜测字段名,以 订阅事件文档 为准。

事件type
订单变更order.changed
会员变更member.changed
营员评价student.comment
团组变更group.changed
拼团结束team.ended
发起退款refund.started

JavaScript 接收示例​

下面是 Node.js 原生 HTTP 的路由处理函数。saveEventOnce 必须由应用实现为数据库持久化及重复事件处理,不能用空函数代替。先于普通员工鉴权路由调用。

import { verifyCloudEvent, cloudEventReceipt } from '@xinduobang/xdb-paas-sdk/server';

export async function handleEvent(req, res, saveEventOnce) {
if (req.method !== 'POST' || req.url !== '/__xdb/events') return false;
const chunks = [];
let bytes = 0;
for await (const chunk of req) {
bytes += chunk.length;
if (bytes > 2 * 1024 * 1024) { res.writeHead(413).end(); return true; }
chunks.push(chunk);
}
let event;
try { event = verifyCloudEvent(req.headers, Buffer.concat(chunks)); }
catch { res.writeHead(401).end(); return true; }
try {
await saveEventOnce(event); // 数据库唯一约束 + 事务;处理完成或持久化入队
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(cloudEventReceipt(event)));
} catch { res.writeHead(500).end(); }
return true;
}

Express 等框架须为该路由保留原始 body;不要把解析后的对象重新 JSON.stringify 后验签。

Python 接收示例​

以已有 Flask 应用为例;不要仅为接事件强制更换项目框架。

from flask import request, jsonify
from xdb_paas import verify_cloud_event, cloud_event_receipt, CloudEventError

@app.post('/__xdb/events')
def receive_event():
try:
event = verify_cloud_event(request.headers, request.get_data())
except CloudEventError:
return '', 401
save_event_once(event) # 应用实现:数据库唯一约束 + 事务,失败抛异常返回 5xx
return jsonify(cloud_event_receipt(event)), 200

交付前验证​

测试正确签名、正文篡改、错误应用、过期签名、重复 ID、数据库失败和处理超时。重复事件不能重复扣款、发消息或创建记录;写入失败不得返回接收成功。事件处理里继续查询开放平台时,沿用应用的 统一限流与缓存,不要为每个事件新建无限并发。