用户目标
- 阅读
Docs/X-01产品文档/2026-06-25-PRD-免费版与Pro解锁.md。
- 基于当前
src/ 代码结构,产出“Pro 权益与购买链路”落地意见。
- 重点覆盖 StoreKit 2 权益模型、老付费用户
originalAppVersion / originalPurchaseDate 迁移、离线缓存 / 退款撤销 / Family Sharing / 恢复购买状态机、以及现有接入点与禁止写入位置。
- 仅做分析,不修改源码。
已完成事项
- 读取项目入口规则、
CODE_GOVERNANCE.md、前置讨论规则、连续推进规则、会话记忆规则。
- 读取 PRD 全文。
- 检查当前仓库状态与分支。
- 扫描
src/Apptag/ 现有结构,确认当前没有任何 StoreKit / 购买代码。
- 核对关键源码入口:
ApptagApp.swift
PreferencesView.swift
ContentView.swift
DataLayer.swift
AppDefaults.swift
AppGridTheme.swift
AppIdentity.swift
Diagnostics.swift
ProcessSingleton.swift
Info.plist
- 用 Apple 官方文档/视频检索核对以下 API 事实:
AppTransaction.shared
originalAppVersion
originalPurchaseDate
Transaction.currentEntitlements
Transaction.ownershipType
Transaction.revocationDate
AppStore.sync()
关键决策
- 建议把 Pro 权益拆成两条独立来源再合并:
- 老付费用户来源:
AppTransaction.shared -> originalAppVersion 主判定,originalPurchaseDate 仅在生产环境下兜底。
- IAP 来源:
Transaction.currentEntitlements + Transaction.updates + 非消耗型商品。
- 建议把“权益真值”与“购买/恢复操作态”分离建模,避免
pending、restore、网络失败覆盖已确认的 Pro 状态。
- 建议新增独立缓存文件到
~/Library/Application Support/TagLauncher/,不要把购买状态写入 tags.json。
- 建议把老用户迁移判定做成“可重算、带证据字段”的规则,而不是首次命中后只写一个永久布尔值。
- 建议把负向撤权做成“仅在有明确已验证信号时生效”;离线或 StoreKit 暂时不可用时保留最近一次已验证 Pro。
文件变更
- 无源码改动。
- 新增本轮会话摘要:
.codex-sessionhistory/2026-06-26-Pro权益链路.md
未决问题
- 免费 + Pro 首个版本对应的精确
CFBundleShortVersionString 尚需冻结,不能默认拿当前仓库 8.0.6 直接当阈值。
originalPurchaseDate 兜底所用的绝对时间戳(含时区)尚需冻结。
- Lifetime Pro 的最终 product ID 尚需冻结。
- Settings 中 Pro 状态区是单独 tab 还是挂在现有 tab 顶部,尚需产品/UI 冻结。
后续建议
- 下一步先产出一份实现级设计稿:状态枚举、缓存结构、启动流程、恢复流程、负向撤权条件、QA 矩阵。
- 实施时优先从
ApptagApp.swift 启动链路和 PreferencesView.swift 设置入口落地,再接 ContentView.swift / TagEditor 的功能门禁。