接入第三方 SaaS 订阅与支付
通过 GoodVision 客户工作区,第三方 SaaS 可以接入套餐选择、订阅、支付和账单管理。本文介绍如何配置平台入口、处理登录和支付跳转,并同步订阅状态。
当前方案支持固定月付或年付 SaaS 使用费、套餐月度 Credits 和已发布的一次性加量包。Credits 表示使用额度,不是可提现储值余额;未声明的超额扣费不会自动启用。
接口使用 billing_interval=month|year 指定月付或年付周期。
当前可用性
客户的套餐、订阅、支付和账单页面由 GoodVision 客户工作区提供。Commerce Checkout Sandbox API Product 已实现,当前应用在开发者控制台获得对应调用配置后才能使用;Production 路径尚未开放。
可先完成平台跳转、OIDC BFF 和页面接入,再按已开通的 Scope 接收脱敏的订阅、支付和退款事件。
接入方式
| 场景 | 第三方产品 | GoodVision 平台 |
|---|---|---|
| 产品功能 | 展示合作方自己的业务页面 | 不复制第三方业务功能 |
| 定价、比较、FAQ | 用独立页面承载平台 iframe,并提供独立打开入口 | /console/pricing 提供个人/企业及月/年套餐 |
| 订阅、支付、账单、权益管理 | 在弹窗中嵌入平台管理页,或独立打开 | /console/subscription 与 /console/billing 提供管理操作 |
| Application、API Product、Webhook | 只显示 Developer Console 入口 | Developer Console 管理技术接入 |
| 产品权限判断 | 按已开通接口查询必要状态 | 根据订单、付款和权限检查返回当前结果 |
第三方不需要实现第二套本地套餐目录、订单或支付状态机。SaaSdemo 的 /app/pricing 在工作区中保留左侧导航并嵌入平台定价 iframe;/pricing 提供带官网顶栏的独立宿主页。两个入口的“我的订阅”都打开管理弹窗;平台价格页与管理页分别承载不同任务。侧栏在定价、订阅和账单之间保留所选 product,独立链接保留同一应用/环境/产品。第三方可以承接平台已冻结的支付返回导航,但价格、订单、支付、账单和使用权限仍由平台管理。
跳转到客户工作区
在第三方服务端配置当前环境的客户工作区 URL。定价入口为 /console/pricing,订阅管理为 /console/subscription,账单为 /console/billing。配置示例请参见 SaaSdemo 的 platform.subscriptionUrl 和 platform.billingUrl。
可独立打开,也可在已登记的 Origin 下通过 iframe 嵌入。仅支持已公布的范围选择和展示参数:
| 参数 | 必填性 | 支持值/省略行为 |
|---|---|---|
embed | 可选 | 1;省略时独立打开 |
locale | 可选 | zh-CN|en-US;省略时沿用当前语言 |
view | 可选 | subscription|billing;省略时沿用当前页面 |
theme | 可选 | light|dark;省略时沿用当前主题 |
application、environment | 🔴 条件必填 | 定价入口或显式选择应用时成组填写:已授权 slug 和 sandbox|production |
product | 🔴 条件必填 | 多产品无法唯一确定时填写;可唯一确定时省略 |
例如 /console/pricing?application=<slug>&environment=sandbox&product=<code>&locale=zh-CN。参数只选择当前已授权范围,不授予权限;SaaSdemo 实例由服务端绑定应用和环境,URL 不匹配时拒绝。production 枚举不表示 Production 路径已开放。
不接受未发布的业务 Query 参数或第三方 handoff Token。不得附加 Organization、ProductTenant、Plan、金额、币种、Provider、伪造支付结果、Token、Secret 或任意 return_url。客户工作区根据实时身份和服务端权限确定操作范围。
iframe 未登录时会向父页面发送 goodvision.customer-billing.auth_required,父页面应使用固定 targetOrigin 把登录提升到顶层窗口,登录后刷新 iframe。支付方式更新和套餐操作仍校验 CSRF 与幂等键;Stripe Hosted Checkout 始终整页打开,不在第三方 iframe 内模拟支付完成。
标准独立页使用平台 Session;已开通嵌入合同的应用可使用 auth=parent-bff 通道,由宿主同源 BFF 转发平台页面的请求。双方验证精确 Origin、原 iframe 和封闭消息,服务端继续核验当前真人、企业、应用及能力。Token、Cookie 和 CSRF Secret 不穿过消息或 URL。Session 过期时恢复统一登录。
按应用打开方式返回
应用内置到 Console,与平台定价/订阅页嵌入应用,是两个独立的接入方向。应用打开方式详见网页应用开发与配置。
平台商业 iframe 创建原付款后,向其直接父窗口发送:
{
"type": "goodvision.customer-billing.checkout_started",
"provider": "stripe",
"checkout_url": "https://checkout.stripe.com/<平台返回的原付款路径>"
}父窗口须核验平台配置的精确 Origin、原商业 iframe 的 event.source、消息字段及 HTTPS checkout.stripe.com 来源,再处理原 URL;不要仅凭消息名称接受任意地址。
| 应用打开方式 | 付款与返回 |
|---|---|
| 跳转/独立访问 | 顶层打开原付款 URL,支付服务商返回平台已冻结的应用入口,再读取原订阅 |
| 隔离 iframe 内置 | 保留原 Console 和应用 iframe;用户点击后在独立付款窗口打开,完成或取消后恢复原应用订阅页 |
| 受信 qiankun 直挂 | 同样保留原 Console 挂载;经应用自己的已登记来源处理付款返回,不把独立应用路径写入 Console 顶层地址 |
内置模式的窗口通信、关联值校验和清理由应用实现,步骤见处理订阅付款与回跳。平台不接受浏览器自定义 return_url,也不会把支付完成消息作为商业授权。弹窗被拦截、用户关闭或未收到回执时保留原订单;回读并恢复原付款。仅在原订阅、履约和使用权限均确认后开放相应功能。
平台侧商业流程
以下步骤全部发生在 GoodVision 平台和支付服务商页面中:
- 新客户创建企业、提交本人客户资料,经平台审核并完成合法账务和商业准备。创建企业不等于可以付款,不复制其它客户的账务账户。
- 定价页读取当前应用、环境和产品已发布的方案;Free 是否提供、个人/企业、周期、席位、FAQ 和优惠均由配置决定。
- 客户点击订阅,在确认弹窗核对条款、首期和续期金额;平台冻结原订单与订阅。
- 付费进入顶层 Stripe Hosted Checkout;免费方案明确接受后激活,不创建零金额 Stripe 付款。
- 平台通过可信 Webhook 或服务端查询确认原单付款,更新原订阅。年付 Credits 仍逐月发放,重放不会重复到账。
- 签名履约事件进入接入方 Inbox,完成产品开通并回传结果;确认 ProductAccess 后开放功能。
- 回跳原订阅,读取套餐、Credits 和投递结果;失败或结果不明时恢复原订单,不创建替代新购。
页面回跳只代表导航完成,不代表支付成功或权益生效。
第三方服务端职责
- Authorization Code + PKCE、同源 Session 和 CSRF。
- 第三方产品自己的用户体验和业务 API。
- 从运行时配置读取平台入口,不在前端硬编码环境地址。
- 已开通对应 API 产品时,查询必要的 ProductAccess 状态或接收签名 Webhook。
- 用户回到第三方产品后重新读取权限;未确认前保持受限状态。
第三方 BFF 可按已开通的嵌入合同转发平台页面的命令,但不拥有订单、支付意图或 Hosted Checkout 状态机。取消、退款、方案和付款方式操作仍由平台管理页发起并由对应 Owner 最终核验。
可选 API 与事件接入
部分产品需要在后台感知访问状态或生命周期事件。只有 Developer Console 已为当前 Application 签发对应 API Product 时才实现:
- ProductAccess/Entitlement 只读查询;
- 订阅状态只读查询;
- 版本化订阅、支付/退款和访问状态事件;
- 通过签名 Webhook 进行幂等同步。
从当前应用的 API 配置获取 Base URL、Audience、Scope、路径、字段和事件 Schema。接收 Webhook 时验签,按 event_id 去重,并用 resource_version 防止旧事件覆盖新状态;版本缺口需通过已授权接口重新查询。
套餐/VIP 权限变更
在 Developer Console 为 Application 申请 oem.access_context.changed.v1。该事件表示有效权限上下文已变化,不表示“用户被授予 VIP Role”。套餐等级属于 Plan/SaaSSubscription;产品可用能力由 ProductAccess/Entitlement 表达;Role/Capability 只表达管理和操作权限。
- 对原始 Body 校验
X-GV-Signature,校验 Header 与信封中的事件 ID/类型一致,并要求data.open_platform_application_id + environment与当前 Receiver 的服务端配置精确匹配;不得按客户 Organization 或应用 slug 回退。 - 按
event_id持久化到 Inbox 后快速返回2xx。 - 使用
data.access_revision识别重复或旧版本。新版本进入pending_readback,并立即限制对应成员的本地访问权限。 - 产品权限快照尚未向标准第三方应用开放,申请 Scope 不代表已经能够回查。需要处理新权限版本或版本缺口时,保持受限并通过支持渠道确认恢复方式;不要调用内部接口或猜测用户映射。
- 正式回查能力开放后,按公开合同确认同一应用、客户空间和成员,且查询结果版本不小于事件版本时,再一次性更新本地必要的权限记录。授权撤销、范围不匹配或查询失败时,继续限制访问。
使用接口定义的 Entitlement 判断套餐能力,例如合成值 relaydesk.tier.pro。role=VIP 表示角色,不能替代套餐权限判断;接入方也不维护 plan_code -> entitlement 的固定映射。
上线验收
- 定价宿主页、订阅管理弹窗和独立链接均消费平台页面,不维护第二套商业事实。
- 客户商业入口来自服务端环境配置,且没有未发布 Query 参数。
/console/pricing、/console/subscription与/console/billing仅在已登记 Origin 嵌入;第三方不能自行扩大frame-ancestors。- 同一嵌入和独立链接保持已授权应用/环境/产品;错误范围拒绝,主题、FAQ 与原请求恢复保持稳定。
- 独立认证或已批准的父 BFF 通道均核验当前身份,第三方不通过 URL 或消息传递 Token、Secret 或组织标识。
- 支付页面回跳、Webhook 投递和登录成功都不直接开放产品功能。
- 第三方只在 ProductAccess/Entitlement 已确认生效后开放受限能力。
- Client Secret、支付凭证和完整卡片数据不进入浏览器、日志、监控或工单。
- 未签发 SaaS Billing API Product 时,第三方不调用猜测的商业路径。
继续阅读 SaaSdemo 接入案例、SaaS 账务契约、认证与授权和接收 Webhook。
权限快照接口尚未向标准应用开放。接入前请查看产品权限快照回查,并通过支持渠道确认客户范围的读取权限。