# Webhook

Webhook 用于让第三方系统主动通知本系统:平台为每个 Webhook 生成一个专用的调用地址,第三方系统按约定的 HTTP 方法调用这个地址,平台就执行对应的动态逻辑,并把逻辑的返回值作为 JSON 响应返回。

典型场景:电商平台在订单支付后推送订单通知、支付平台推送支付结果、设备网关推送告警等。

提示

1.0 起已移除旧的“系统集成”(Incoming/Outgoing 集成、Enable Logic、集成执行用户)。传入集成改用本页的 Webhook;传出集成没有直接的替代功能,迁移方法见 从传出集成迁移。

# 目标读者

本文档的目标读者为:本系统的开发和实施人员

# Webhook 列表

菜单位置:开发定制 > 系统集成 > Webhook

Webhook 列表

一个 Webhook 包含以下字段:

界面显示 属性名 说明
Name name 名称,租户内唯一,创建后不可修改(编辑表单中为只读)
Description description 描述
Url url 调用地址中的标识,创建时自动生成(UUID),租户内唯一
Http method httpMethod 调用方式,每个 Webhook 只能选一种:GET 或 POST
Active active 是否激活
Effective date effectiveDate 有效期开始时间,可以为空
Expiry date expiryDate 有效期结束时间,可以为空
Core logic coreLogic 收到调用时执行的动态逻辑
Exec records execRecords 执行记录,见 执行记录

Webhook 只在同时满足以下条件时才会执行:Active 为开启;Effective date 为空或早于当前时间;Expiry date 为空或晚于当前时间。

列表中的 Http method 列当前显示为 -,这是当前版本的界面问题,实际值以创建时的请求为准。

# 创建 Webhook

当前版本的界面问题

在 1.0.0-beta18 中,新建和修改表单里的 Http method 下拉框加载不出选项,Url 又被表单标记为必填,所以暂时无法在界面上保存新的 Webhook(下面第 2 步的截图就是这个状态)。在平台修复之前,请按第 2 步之后的方法通过 REST API 创建。

  1. 在 开发定制 > 动态逻辑 > 动态逻辑 中创建 Webhook 要执行的动态逻辑。平台没有 Webhook 专用的逻辑类型,逻辑类型可以任选,引擎选 GROOVY_CODE。逻辑的写法见下文 注入变量 和 返回结果。
  2. 在 Webhook 列表页点击 创建,填写名称、描述、HTTP 方法,打开 Active,选择上一步创建的核心逻辑后保存。保存时平台自动生成 Url。

新建 Webhook

截图中 Http method 为空、Url 标着必填,点 保存并关闭 无法保存,这就是上面说的界面问题;弹窗标题显示为领域类名 DynamicWebhook,也是当前版本的界面问题。在平台修复之前,请通过 REST API 创建(先按 平台 API 登录拿到 token):

curl -X POST http://<服务器地址>/api/data/DynamicWebhook \
  -H 'Authorization: Bearer <access_token>' \
  -H 'Content-Type: application/json' \
  -d '{"name":"接收订单通知","description":"电商平台在订单支付后推送通知","httpMethod":"POST","active":true,"coreLogic":{"id":<动态逻辑 id>}}'
1
2
3
4

响应中的 data.url 就是自动生成的地址标识。

# 调用地址

Webhook 的调用地址为:

http://<服务器地址>/api/webhook/<Url>
1

其中 <Url> 是列表中 Url 列的值。调用方式必须与 Http method 一致。Webhook 接口不需要登录,任何知道地址的人都可以调用,所以要把地址当作密钥保管,必要时 重新生成地址。

以“接收订单通知”为例(Http method 为 POST):

curl -X POST http://<服务器地址>/api/webhook/9a72bdc1-bc78-469d-83e3-8fa976afc4ce \
  -H 'Content-Type: application/json' \
  -d '{"orderNo":"SO20260923001","amount":1280.00}'
1
2
3

返回:

{"webhook":"接收订单通知","orderNo":"SO20260923001","received":true,"receivedAt":"2026-09-23T13:33:39.078338160Z[UTC]"}
1

# 注入变量

核心逻辑执行时,可以使用以下注入变量:

变量名称 变量类型 描述
webhook tech.muyan.dynamic.integration.DynamicWebhook 当前被调用的 Webhook
headers Map<String, String> 请求头,键为小写,例如 headers['x-signature']
parameters Map<String, Object> 请求参数,见下方说明
requestTime java.time.ZonedDateTime 收到请求的时间
requestUrl String 后端收到的请求地址,不含 queryString,也不含 /api 前缀。主机名取自 nginx 转发的 Host,不带端口,例如 http://localhost/webhook/<Url>
application grails.core.GrailsApplication 当前的 grails 应用上下文
log Closure<?> 用于打印执行日志的 log 闭包,日志写进执行记录
  • Http method 为 GET 时,parameters 是 URL 参数,类型为 grails.web.servlet.mvc.GrailsParameterMap。除了调用方传的参数,里面还有平台路由附带的 controller、action、webhookKey 三个键(实测 ?orderNo=SO1&amount=10 得到 [orderNo:SO1, amount:10, controller:dynamicWebhook, action:get, webhookKey:<Url>]),遍历参数时要跳过它们。
  • Http method 为 POST 时,parameters 是 JSON 请求体,类型为 org.grails.web.json.JSONObject。调用方必须带 Content-Type: application/json;表单编码(application/x-www-form-urlencoded)的请求体不会被解析,parameters 为空。

注意

Webhook 接口不需要登录,逻辑里没有当前用户(没有 userContext)。如果需要校验调用方,可以约定一个请求头(例如签名),在逻辑里通过 headers 读取并校验,校验不通过时抛出异常。

# 返回结果

核心逻辑应返回 Map<String, Object>,平台把它转成 JSON 原样返回给调用方,HTTP 状态码为 200。逻辑中抛出异常时,平台返回 HTTP 500,响应体为异常信息文本。

“接收订单通知”使用的核心逻辑示例:

log("收到订单通知:${parameters}")
String orderNo = parameters.orderNo
if (!orderNo) {
  throw new IllegalArgumentException('缺少订单号 orderNo')
}
// 在这里写业务处理,例如更新订单状态
return [
  received  : true,
  webhook   : webhook.name,
  orderNo   : orderNo,
  receivedAt: requestTime.toString(),
]
1
2
3
4
5
6
7
8
9
10
11
12

# HTTP 状态码

以下是实测的各场景返回:

场景 状态码 响应体
找不到该地址,或调用方式与 Http method 不一致 404 Webhook not found
Webhook 未激活或不在有效期内 500 Webhook is not active or expired
核心逻辑抛出异常 500 异常信息,例如 缺少订单号 orderNo
核心逻辑执行成功 200 核心逻辑返回值的 JSON

注意

请勿根据响应体中的提示文本做业务判断,这些文本不保证在平台升级后保持不变。

# 重新生成调用地址

调用地址泄露后,可以重新生成:在 Webhook 列表中勾选要处理的 Webhook,点击 重新生成传入集成地址,在确认框中点 确定。平台为选中的 Webhook 生成新的 Url,旧地址作废(最长有 10 分钟延迟,见 缓存与生效时间),所有调用方都要改用新地址。

重新生成调用地址

确认框标题 Regenerate URL 未翻译,操作成功后的提示是半句话“已重新生成传入集成接口的”,这些都是当前版本的界面问题,不影响功能。

# 缓存与生效时间

平台按地址缓存 Webhook 定义,缓存在首次调用后 10 分钟过期。因此修改 Webhook 后(包括重新生成地址、关闭 Active、修改有效期),最长要等 10 分钟才生效。实测重新生成地址后,旧地址在缓存过期前仍然可以调用,之后才返回 404。发现地址泄露时,请把这 10 分钟考虑进去。

核心逻辑的代码修改不受这个缓存影响,保存后下一次调用即生效。

# 执行记录

除了地址找不到(404)的请求,每次调用都会生成一条执行记录,Webhook 未激活时也会记一条失败记录。在 Webhook 列表中点击某一行 Exec records 列的按钮,可以查看该 Webhook 的执行记录:

Webhook 执行记录

界面显示 说明
Webhook 对应的 Webhook。当前版本这一列显示的是 Webhook 的 id,不是名称
Status 执行结果:Success 或 Failed
Exec params 执行参数(当前版本中为空,请求参数见 Exec result)
Start time 开始时间
Finish time 结束时间
Exec result 请求方法、请求参数、请求头和核心逻辑的返回值;未激活时为提示信息。当前版本实测“Request headers”一栏为空
Exec log 逻辑中通过 log 打印的日志
Stack Trace 出错时的异常堆栈

执行记录 菜单下没有 Webhook 的入口,只能从 Webhook 列表进入。

注意

按平台源码的设计,执行结果会记录全部请求头,包括 Authorization、Cookie 和签名等请求头,并以明文保存在数据库中。当前版本实测这一栏为空,但平台修复后就会记录,所以仍然不要让调用方在 Webhook 请求中携带长期有效的凭据。

# 从传出集成迁移

旧版的传出集成(Outgoing)用于在对象创建、修改、删除时通知外部系统,1.0 起已移除,没有直接替代的功能。迁移方法:为对应的领域模型创建 AFTER_CREATE / AFTER_UPDATE / AFTER_DELETE 类型的对象钩子,在钩子的动态逻辑里自行发送 HTTP 请求。对象钩子的用法见 对象钩子。

提示

平台没有提供把旧集成定义转换为 Webhook 的迁移脚本。升级前请把旧的传入集成逐个在新版中重建为 Webhook,并把新的调用地址通知给调用方。

Last Updated: 2026/9/24 14:27:35