事件目录
本文列出 GoodVision 事件类型、触发时机和字段说明。事件使用带版本的名称,通过已批准且已启用的 Webhook 订阅投递。
接收端的签名验证、Token 校验、去重和应答方式,请参见接收 Webhook。实际字段、枚举和权限以当前应用 API 产品提供的事件 Schema 为准。
订阅事件结构
下面是订阅激活的完整业务事件,包含全部必需字段。HTTP 明文投递还会增加顶层 verification_token,校验并移除后才得到这里的信封;加密投递先验签、解密和核验 Token。
{
"event_id": "evt_demo_activated",
"event_type": "oem.subscription.activated.v1",
"schema_version": "1",
"occurred_at": "2026-10-06T00:00:00Z",
"producer": "opaque",
"tenant_id": "org_customer_demo",
"resource_id": "sub_demo_cancel",
"resource_version": 2,
"correlation_id": "req_demo_activation",
"data": {
"id": "sub_demo_cancel",
"organization_id": "org_customer_demo",
"open_platform_application_id": "app_demo_saas",
"environment": "sandbox",
"product_tenant_id": "tenant_demo_cancel",
"party_id": "party_customer_demo",
"order_id": "order_demo_initial",
"product_code": "relaydesk",
"plan_code": "basic_monthly_demo",
"plan_version": 1,
"status": "active",
"current_period_start": "2026-10-06T00:00:00Z",
"current_period_end": "2026-11-06T00:00:00Z",
"status_effective_at": "2026-10-06T00:00:00Z",
"payment_reference": "payment_demo_initial",
"operation_id": "op_demo_activation",
"reason_code": "example_activation",
"resource_version": 2,
"created_at": "2026-10-05T23:55:00Z",
"updated_at": "2026-10-06T00:00:00Z"
}
}所有 ID、套餐和原因码都是合成值,producer: "opaque" 是不透明来源的展示占位,实际来源枚举以已签发 Schema 为准。不要按占位来源或示例原因码硬编码。可下载 JSON,或查看含 Header、Token 和可复算签名的完整 HTTP 请求。
以下顶层字段在订阅事件中全部必需:
| 字段 | 类型 | 含义与校验 |
|---|---|---|
event_id | string | 稳定业务事件 ID,用于持久化和去重 |
event_type | string | 下表列出的完整、带版本事件名;与 Header 一致 |
schema_version | string | 当前为 "1",不是整数 1 |
occurred_at | RFC3339 string | 事件发生时间;不是用于验签的投递时间戳 |
producer | string | 不透明来源,按已签发 Schema 校验 |
tenant_id | string | 购买方客户 Organization,等于 data.organization_id;不是应用所属企业 |
resource_id | string | SaaS 订阅 ID,等于 data.id |
resource_version | integer ≥ 1 | 订阅版本,与 data.resource_version 一致 |
correlation_id | string | 关联排障,不用作去重键 |
data | object | 当前订阅摘要,见下面的字段定义 |
按当前事件 Schema 验证字段。开放对象可忽略受支持主版本内新增的字段;设置 additionalProperties: false 的封闭对象需拒绝或隔离未知字段。不支持的主版本也需拒绝或隔离,避免按旧格式解析。
订阅 data 的必需字段
所有 19 种订阅事件共用此摘要;不能只发送或只按 {id, status} 处理。字段组合需与事件及当前状态一致。
| 字段 | 类型 | 含义 |
|---|---|---|
id | string | 订阅 ID,与顶层 resource_id 一致 |
organization_id | string | 购买方客户 Organization,与顶层 tenant_id 一致 |
party_id | string | 商业客户档案标识,不是用户或 Membership ID |
product_code | string | 已批准的产品编码 |
plan_code | string | 当前套餐编码;预约套餐变更时仍指当前套餐 |
plan_version | integer ≥ 1 | 不可变套餐定义版本,不是订阅版本 |
status | string | 当前商业状态,必须符合事件表中的允许值 |
current_period_start | RFC3339 string | 当前周期开始时间 |
current_period_end | RFC3339 string | 当前周期结束时间,必须晚于开始 |
status_effective_at | RFC3339 string | 当前 status 开始生效的时间 |
operation_id | string | 关联商业异步操作的标识,不是支付凭证 |
reason_code | string | 变更原因,按当前事件 Schema 解释 |
resource_version | integer ≥ 1 | 同一订阅单调递增的版本 |
created_at | RFC3339 string | 订阅创建时间 |
updated_at | RFC3339 string | 摘要最近更新时间 |
接入范围与状态相关字段
这些字段在通用摘要中为可选,以实际事件和 Schema 为准。字段缺失时保持缺失,不转换为 null。
第三方投递需明确绑定应用和环境。缺少绑定时拒绝处理,不能使用客户企业或应用 slug 替代。
| 字段 | 类型 | 用途 |
|---|---|---|
open_platform_application_id / environment | string / sandbox 或 production | 成对核对当前第三方 Application 与环境;不接受其他应用或环境的数据 |
application_slug / brand_profile_code | string | 应用与品牌展示标识,不代替 Application ID 作授权判断 |
product_tenant_id | string | 客户在产品中的租户标识,不代替 Organization |
order_id | string | 原商业订单引用;付款结果独立查询 |
billing_mode | free / paid | 商业计费方式;免费激活不制造付款证据 |
upgrade_from_subscription_id | string | 付费升级前的免费订阅 ID;两份订阅分别按自身版本更新 |
price_version_ref | string | 不可变价格引用,不允许消费者覆盖金额 |
trial_ending_at / trial_end | RFC3339 string | 试用结束提醒与试用结束时间 |
renewal_window_start / grace_deadline | RFC3339 string | 续费窗口开始与宽限截止时间 |
pause_end / resume_status | RFC3339 string / 状态 string | 暂停结束与恢复状态,按事件实际值处理 |
cancel_at / cancel_at_period_end | RFC3339 string / boolean | 取消生效时间与是否周期末取消 |
pending_plan_code / pending_plan_version / pending_plan_effective_at | string / integer ≥ 1 / RFC3339 string | 预约套餐及其生效时间,不代表当前权益已改变 |
ended_at | RFC3339 string | 订阅结束时间;与取消、到期或终止状态一致 |
订阅摘要不提供金额、币种、old_plan、new_plan、Role 或 Entitlement。需要付款信息或成员权限时,使用各自已授权的查询接口。
完整订阅事件表
事件名与 data.status 不总是一一对应,例如支付失败可进入不同状态,支付方式更新不会改变状态。下面列出当前 v1 的全部 20 种订阅事件。相关字段描述场景,不代替正式 Schema 的 required 定义。
| 完整事件名 | 触发时机 | 允许的 data.status | 重点处理 |
|---|---|---|---|
oem.subscription.created.v1 | 商业订阅创建 | trialing / pending_payment / active | 根据实际状态展示,不假设创建即付费成功 |
oem.subscription.updated.v1 | 订阅记录修订,例如受理取消或恢复请求 | active / trialing / trial_ending / renewal_pending / plan_change_pending / pause_pending / paused / cancellation_pending / past_due / grace_period / suspended | 保存连续版本和真实当前状态;等待后续确认事件,不表示取消或恢复已完成 |
oem.subscription.trial_started.v1 | 试用开始 | trialing | 读取周期和 trial_end,不自行延长试用 |
oem.subscription.trial_ending.v1 | 进入试用结束提醒阶段 | trial_ending | 读取 trial_ending_at、trial_end,提示转付费 |
oem.subscription.activated.v1 | 商业订阅激活 | active | 更新订阅记录,单独核对成员权限;完整请求 |
oem.subscription.renewal_upcoming.v1 | 进入续费窗口 | renewal_pending | 读取 renewal_window_start,提醒不表示续费成功 |
oem.subscription.renewed.v1 | 续费完成 | active | 应用新周期边界,不能自行按月/年加时间 |
oem.subscription.payment_failed.v1 | 订阅支付失败 | payment_failed / past_due / grace_period | 按实际状态及宽限截止处理,等待恢复或到期 |
oem.subscription.past_due.v1 | 订阅进入逾期/宽限处理 | past_due / grace_period | 读取 grace_deadline,不推断固定宽限时长 |
oem.subscription.payment_method_updated.v1 | 支付方式更新 | 保持当前订阅状态 | 更新关联提示,不当作支付或续费成功 |
oem.subscription.pause_scheduled.v1 | 暂停已预约 | pause_pending | 区分预约与生效,不自行推断暂停时间 |
oem.subscription.paused.v1 | 暂停已生效 | paused | 按 pause_end / resume_status 和访问回查处理 |
oem.subscription.resumed.v1 | 恢复操作生效 | active / trialing / trial_ending / renewal_pending / plan_change_pending / cancellation_pending / paused | 读取实际恢复状态,不能固定写成 active |
oem.subscription.cancellation_scheduled.v1 | 取消已预约 | cancellation_pending | 读取 cancel_at / cancel_at_period_end;完整场景 |
oem.subscription.cancelled.v1 | 取消已生效 | cancelled | 记录 ended_at、收紧访问并回查,不推断退款 |
oem.subscription.plan_change_scheduled.v1 | 套餐变更已预约 | plan_change_pending | 读取 pending_plan_*,不提前启用新权益 |
oem.subscription.plan_changed.v1 | 套餐变更已生效 | active / trialing / trial_ending | 当前 plan_code / plan_version 更新;完整场景 |
oem.subscription.suspended.v1 | 商业订阅被限制 | suspended | 收紧访问,按获批恢复流程处理 |
oem.subscription.expired.v1 | 周期或宽限结束、订阅到期 | expired | 读取 ended_at,引导重新订阅;完整场景 |
oem.subscription.terminated.v1 | 商业订阅被终止 | terminated | 立即停止访问,保留历史和审计 |
事件分类
| 分类 | 代表性事件 |
|---|---|
| 身份与访问 | identity.registration.completed.v1、oem.product.activated.v1、oem.access_context.changed.v1 |
| 订阅 | 全部完整名称与状态映射见订阅事件表,包含记录修订、支付方式更新、暂停、取消、套餐变更、到期等 20 种事件 |
| 套餐变更 | oem.subscription.plan_changed.v1 |
| 支付结果 | payment.order.created.v1、payment.intent.created.v1、payment.requires_action.v1、payment.pending.v1、payment.succeeded.v1、payment.failed.v1、payment.cancelled.v1、payment.unknown.v1 |
| 退款结果 | payment.refund.pending.v1、payment.refund.unknown.v1、payment.refund.succeeded.v1、payment.refund.failed.v1 |
| 账务状态 | billing.statement.published.v1 |
| Support | oem.support.request_received.v1、reply_created、status_changed |
只有当前 API 调用授权明确列出的事件会被投递。不要假设相似名称或未发布事件可用。
接收端要求
- 使用
event_id去重。 - 使用单调递增的
resource_version更新状态。 - 保存
correlation_id,用于跨系统诊断。 - 发现版本缺口后通过授权的状态 API 对账。
- 日志中不记录 Secret、Token 或不必要的业务敏感数据。
有效权限变更
oem.access_context.changed.v1 表示某个主体在指定 Organization 和 Product 下的有效访问上下文已经失效。它不是 Role、Entitlement 或 Capability 快照。
{
"event_id": "evt_opaque",
"event_type": "oem.access_context.changed.v1",
"schema_version": "1",
"producer": "opaque",
"occurred_at": "2026-09-17T03:00:00Z",
"tenant_id": "org_opaque",
"resource_id": "membership_opaque",
"resource_version": 42,
"correlation_id": "req_opaque",
"data": {
"subject_type": "membership",
"subject_id": "membership_opaque",
"organization_id": "org_opaque",
"open_platform_application_id": "app_opaque",
"environment": "sandbox",
"product_code": "relaydesk",
"access_revision": 42
}
}接收权限变更事件后:
- 按当前事件 Schema 校验
producer,示例占位值不可用于实际判断。 - 核对
tenant_id/organization_id对应购买方客户企业。将open_platform_application_id和environment与接收端服务端配置精确匹配,不能用企业或应用 slug 替代。 - 确认
resource_version等于data.access_revision,完成验签并按event_id去重。 - 使用当前应用的服务身份及
openplatform.api.invoke调用POST /open-platform/v1/applications/{id}/events/{event_id}/access-snapshot,正文为{}。平台从原事件选定唯一客户和 Membership,返回当前roles、entitlements、capabilities和access_revision;当前登录用户上下文不能代替目标成员。请求与失败处理见产品权限快照回查。 - 使用接口定义的 Entitlement 判断付费等级,而不是检查名为
VIP的 Role。版本不确定或查询失败时继续限制访问。
成员快照需包含布尔字段 product_access_allowed:
| 返回情况 | 处理方式 |
|---|---|
true,且身份、Membership、ProductAccess 均有效 | 应用当前角色、权益和能力 |
false | 清除本地有效权限,限制该成员使用产品 |
| 字段缺失或不是布尔值 | 按响应格式错误处理,继续限制访问 |
企业购买、角色名称和 active Membership 不能单独证明成员已分配。采用此版本前,升级接收程序并清理旧权限记录,避免旧程序忽略该字段后继续放行。
产品访问已开通
oem.product.activated.v1 只表示指定 Organization 的 ProductAccess 已进入 trial 或 active,不能单独给某个成员开放功能。
{
"event_id": "evt_product_access_opaque",
"event_type": "oem.product.activated.v1",
"schema_version": "1",
"producer": "opaque",
"occurred_at": "2026-09-17T03:00:00Z",
"tenant_id": "org_opaque",
"resource_id": "product_access_opaque",
"resource_version": 3,
"correlation_id": "req_opaque",
"data": {
"organization_id": "org_opaque",
"product_access_id": "product_access_opaque",
"product_code": "relaydesk",
"product_tenant_id": "tenant_opaque",
"open_platform_application_id": "app_opaque",
"environment": "sandbox",
"status": "active",
"version": 3
}
}核对 Application、环境、Organization、ProductAccess ID 和版本,按 event_id 去重,使用版本防止旧事件覆盖本地脱敏状态。成员权限需另行通过 oem.access_context.changed.v1 和已授权查询确认。
支付与退款事件当前使用 USD。payment.succeeded.v1 表示付款成功,payment.refund.succeeded.v1 表示退款成功。
pending、requires_action、unknown 和失败状态需等待后续 Webhook 或服务端查询确认。确认前不能激活订阅或使用权限。
权限快照接口尚未向标准应用开放。接入前请查看产品权限快照回查,并通过支持渠道确认客户范围的读取权限。
用户与事件的关联键
订阅归属于客户空间;自动续费没有“当前登录用户”前置。成员 subject 与公开用户上下文的 membership_id 关联,公开别名不合并空间或订阅;完整规则见多应用、用户与事件接入。