# Launch 购买期限与账号 SDK 边界

本次变更仅在本地开发、验证；未部署 Launch，未发布 SDK 包。当前源码依赖 Licentivo 的账号授权协议和配套官方 SDK 更新，不能与仅支持长期许可证密钥的旧服务端混用。

## 职责

| 能力 | 负责方 |
| --- | --- |
| 注册、登录、账号安全、商品、订单、支付、退款 | Launch |
| 续费/升级报价、购买周期和绝对起止时间计算 | Launch |
| 套餐引用到授权产品/策略的映射执行与策略校验 | Licentivo；Launch 提供选择和登记入口 |
| 许可证、功能权限、设备名额、授权会话、离线许可 | Licentivo 与官方 SDK |
| 设备列表和远程解绑 | Licentivo；Launch 代理当前账号的请求并展示结果 |
| 登录凭据的保存、账号恢复、退出入口 | Launch SDK |

Launch 的订单、购买期限、同步完成标记都不能放行受保护功能。客户端必须通过官方 SDK 的 `RunFeature` 等运行接口执行受保护业务。

每个 Launch 产品保存一个稳定的公开 Licentivo 产品路由。同一产品的套餐可选择不同策略，但不能切换到另一个远端产品；映射保存通过共同产品的数据库行锁串行校验。客户身份、套餐发布与 SDK 接入配置读取同一绑定，草稿不能改变已购客户的产品上下文。这只校验接入配置的一致性，不决定功能或设备授权。旧库缺少稳定字段时优先参考购买及发布历史，无法唯一确认时返回 `PRODUCT_MAPPING_CONFLICT`，详见 [映射并发修复](/zh/launch-documents/product-mapping-fix-2026-10-24/)。

## 购买期限同步

复用现有接口，不新增付款事实、退款事实或报价服务：

- `POST /api/v1/launch/connections`、`PUT /connections/{id}`：登记身份签名公钥，正式与测试环境分别建立连接。
- `PUT /api/v1/launch/connections/{id}/plans/{plan}`：登记不可重复的 Launch 套餐版本引用及远端产品、策略版本。
- `POST /api/v1/launch/connections/{id}/events`：提交完整的版本化购买快照。
- `GET /api/v1/launch/connections/{id}/events/{event}`：确认处理结果。
- `POST /api/v1/launch/connections/{id}/events/{event}/retry`：核对后重试原事件。

快照只含事件编号、环境、事件类型、购买引用、匿名账号引用、Launch 产品、套餐版本引用、递增版本和绝对 `valid_from` / `valid_until`；迁移时附旧许可证 ID。不发送支付密钥、价格、客户邮箱或长期许可证密钥。永久购买的 `valid_until` 为 `null`，由 Licentivo 校验是否与策略匹配。

Launch 在同一数据库事务内确认付款、记录不可变的购买覆盖区间并保存同步任务。按日/月/年的日历周期计算截止时间；续费沿用原购买锚点，已过期后才付款则从实际付款时间开始。升级报价和期限仍在 Launch 计算。

每次变化发送完整绝对值，重试使用同一个事件 ID 和相同正文，不发送“再加 N 天”。任务持久化请求、远端连接和处理进度；响应丢失、进程重启和并发工作进程均不会重复叠加期限。

HTTP 202 只代表 Licentivo 已接收。仅收到 `applied` 才标记同步完成；`pending` 继续查询，`manual_review` 和无法确认的 `stale` 保留待核对状态。网络错误退避重试，连续失败达到上限后进入人工核对。

退款只移除对应订单的覆盖区间：退升级订单恢复基础套餐；退续费订单移除对应延长部分；仍有未退款购买时同步重算结果，不撤销全部购买。全部购买均退款后发送 `entitlement.refunded`。以后重新购买使用新的购买引用，防止复活已撤销的远端许可证。

渠道确认的败诉拒付或撤款也只移除对应订单的购买覆盖，订单显示为 `reversed`，实际授权变化仍由 Licentivo 应用购买快照后处理。未决争议不改变购买记录。历史待核对撤款通知可通过原渠道重新验签重发恢复。

未知退款进入人工核对后，可由管理员通过 `POST /api/v1/admin/refunds/{id}/reconcile` 恢复原操作。回执需匹配原交易、币种和金额；不得变更原操作编号及审批时间，超出安全重试窗口后不得盲目重新提交资金请求。

## 账号与客户端

1. 客户端使用 `POST /api/v1/auth/login` 建立真实桌面登录会话。
2. Launch SDK 请求 `POST /api/v1/software/products/{id}/identity`，指定 `sandbox` 或 `live`。
3. Launch 签发最长 120 秒、指定接收方及产品/购买上下文的身份断言，包含真实登录 `sid` 和登录到期。断言不含功能、设备额度或离线权限。
4. Launch SDK 仅在内存中把 `identity_token`、`launch_connection_id`、`environment` 传给官方 SDK，`license_key` 为空。
5. 官方 SDK 调用 Licentivo `/api/v1/launch/exchange`，由 Licentivo 决定授权并下发设备凭据和签名许可。

Launch 正常账号记录使用 v2，只保存账号会话及公开路由信息，不保存断言、许可证密钥或授权许可。显式退出时转为仅用于撤销的私有 v3 待办，不能恢复为登录；确认 Launch 会话撤销后再删除。旧 `/credential` 接口返回 HTTP 410。公钥和产品 ID 可以随应用分发，管理密钥只留在服务端。

## 退出、关闭、远程解绑

| 操作 | 行为 |
| --- | --- |
| 普通 Close / Dispose / Drop | 停止运行时，保留购买、设备绑定和主动记住的账号 |
| Logout / ReleaseDevice 兼容入口 | 标记本地账号退出并调用官方 SDK 退出，再向 Launch 撤销会话；未确认的账号退出保留安全待办 |
| 无法接触设备时，在 Launch 管理设备 | 代理 Licentivo 的设备列表、解绑请求及处理结果 |

官方 SDK 的退出会立即停止本进程使用授权，清理其运行凭据，使用专用退出票据撤销远端绑定。网络失败必须保留 SDK 的 `.logout.json`，结果应显示远端撤销待完成，不能提示解绑成功。Launch SDK 不删除、遍历清理或修改官方 SDK 的缓存目录；待撤销票据由官方 SDK 在对应缓存再次初始化后恢复处理。

Launch 账号退出也有独立的待办状态：网络失败、取消或响应丢失时，不删除重试所需的安全记录；再次登录、恢复或退出只补交原会话撤销，待办不能用于恢复登录。成功或 HTTP 401 确认会话失效后删除。跨进程恢复需要持久化安全 Store，MemoryStore 只在本进程有效。详见 [退出重试修复与联调](/zh/launch-documents/logout-retry-fix-2026-10-24/)。

远程解绑请求携带原列表返回的 `activation_id` 和 `expected_generation`，重试沿用 `operation_id`。Launch 不计算解绑次数，不自行审批或执行解绑队列，也不刷新代次后自动重试旧操作。剩余次数、是否允许释放、审核及最终结果均来自 Licentivo。

账号恢复 `Restore` 需要联网确认账号并取得新身份断言；Launch 不在账号服务故障时自行选择离线放行。需要离线启动的应用按官方 SDK 的恢复契约接入；签名、离线期限和时钟检查仍由官方 SDK 完成。

## 服务端身份撤销

对接 Licentivo V39 的现有接口，无需商业事实或报价接口：

- `POST /api/v1/launch/connections/{id}/session-revocations`：撤销指定 `sid` 或账号的旧登录，返回同步提交的回执。
- `GET /api/v1/launch/connections/{id}/session-revocations/{event_id}`：取回原事件的已提交结果，恢复响应丢失的请求。

普通桌面退出、强制退出指定会话使用 `scope=session`；改密、重置密码、邮箱变更、停用账号及网站人员角色变更导致的全部会话失效使用 `scope=account`。网站会话的普通退出不影响客户软件。Launch 只通知身份已失效，远端授权会话、运行凭据和设备释放均由 Licentivo 完成，不修改购买期限，也不消耗 Launch 维护的解绑额度。

本地会话失效与通知入队在同一事务内完成。队列冻结当时已存在的连接和环境，使用同一事件 ID、账号匿名标识及原请求重试；后来创建的连接不接收历史账号撤销。先查询原回执，确认不存在时才重新提交；每个环境分别持久化结果，只有全部收到匹配的成功回执才显示完成。断网自动重试，凭据/契约冲突进入人工核对。管理员可在同步任务页查看通知并重试；重试不生成新的撤销事件。

账号范围撤销以 Licentivo 首次接收的 `revoked_before` 为界。通知待完成期间暂停新的桌面登录和身份断言；完成后，真实登录的 `auth_time` 必须严格晚于所有环境回执中的截止秒。同秒登录可稍后重新认证；刷新 `iat` 或身份断言不能续用旧登录。网站登录仍可用于恢复操作。账号重新启用后也必须重新登录，不恢复旧会话。

## 旧数据迁移

有已确认旧许可证绑定的购买使用 `entitlement.migrated`，只提交旧许可证 ID，不读取或下发旧密钥。Licentivo 确认迁移后，再发送独立的新版本快照同步当前商业期限和状态。未确认过的旧发放结果及不兼容的授权需要人工核对；旧管理命令不会自动重放。

普通 SDK 退出、远程设备解绑和服务端身份撤销均对接现有接口。远程停用仍受官方 SDK 已签发离线许可的期限约束，Launch 不承诺离线设备收到即时撤销。

<!-- account-store-ownership-2026-10-24 -->

账号退出采用独立安全待办、本机会话退出摘要及跨进程协调，旧实例只处理自己的登录。存储故障后的恢复先补交账号撤销，不取得身份或启动授权；功能、离线许可、设备代次和解绑额度继续由 Licentivo 决定。详见 [修复与两端联调记录](/zh/launch-documents/account-store-fix-2026-10-24/)。
