hi-server/.trae/documents/实现 Apple IAP 订阅并与现有后端整合.md
shanshanzhong 62186ca672
All checks were successful
Build docker and publish / build (20.15.1) (push) Successful in 6m37s
feat(iap/apple): 实现苹果IAP非续期订阅功能
新增苹果IAP相关接口与逻辑,包括产品列表查询、交易绑定、状态查询和恢复购买功能。移除旧的IAP验证逻辑,重构订阅系统以支持苹果IAP交易记录存储和权益计算。

- 新增/pkg/iap/apple包处理JWS解析和产品映射
- 实现GET /products、POST /attach、POST /restore和GET /status接口
- 新增apple_iap_transactions表存储交易记录
- 更新文档说明配置方式和接口规范
- 移除旧的AppleIAP验证和通知处理逻辑
2025-12-13 20:54:50 -08:00

4.1 KiB
Raw Permalink Blame History

目标

  • 不使用自动续期订阅;采用“非续期订阅”或“非消耗型”作为内购模式。
  • 仅实现 Go 后端 API客户端iOS/StoreKit 2按说明调用。

产品模型

  • 非续期订阅:固定时长通行证(如 30/90/365 天产品IDcom.airport.vpn.pass.30d|90d|365d
  • 非消耗型可选一次性解锁某附加功能产品IDcom.airport.vpn.addon.xyz
  • 服务器以 productId→权益/时长 进行配置映射。

后端API设计Go/Gin

  • 路由注册:internal/handler/routes.go
    • GET /api/iap/apple/products:返回前端展示的产品清单(含总价/描述/时长映射)
    • POST /api/iap/apple/transactions/attach:绑定一次购买到用户账户(需登录)。入参:signedTransactionJWS
    • POST /api/iap/apple/restore:恢复购买(批量接收 JWS 列表并绑定)
    • GET /api/iap/apple/status:返回用户当前权益与到期时间(统一来源聚合)
  • 逻辑目录:internal/logic/iap/apple/*
    • AttachTransactionLogic:解析 JWS→校验 bundleId/productId/purchaseDate→根据 productId 映射权益与时长→更新订阅统一表
    • RestoreLogic:对所有已购记录执行绑定去重(基于 original_transaction_id
    • QueryStatusLogic:聚合各来源订阅,返回有效权益(取最近到期/最高等级)
  • 工具包:pkg/iap/apple
    • ParseTransactionJWS:解析 JWS提取 transactionId/originalTransactionId/productId/purchaseDate/revocationDate
    • VerifyBasic:基础校验(bundleId、签名头部与证书链存在性);如客户端已 transaction.verify(),可采用“信任+服务器最小校验”的模式快速落地
  • 配置:doc/config-zh.md
    • IAP_PRODUCT_MAPproductId → tier/duration(例如:30d→+30天addon→解锁功能X
    • APPLE_IAP_BUNDLE_ID:用于 JWS 内部校验

数据模型

  • 新表:apple_iap_transactions
    • iduser_idoriginal_transaction_id(唯一)、transaction_idproduct_idpurchase_atrevocation_atjws_hash
  • 统一订阅表增强(现有 SubscribeModel
    • 新增来源:source=apple_iapexternal_id=original_transaction_idtierexpires_at
  • 索引:original_transaction_id 唯一、user_id+sourceexpires_at

与现有系统融合

  • internal/svc/serviceContext.go:初始化 IAP 模块与模型
  • QueryPurchaseOrderLogic/SubscribeModel聚合苹果IAP来源冲突策略按最高权益与最晚到期。
  • 不产生命令行支付订单,仅记录订阅流水与审计(避免与 Stripe 等混淆)。

安全与合规

  • 仅显示商店在可支付时;价格、描述清晰;使用系统确认表单。
  • 服务器进行最小校验:bundleIdproductId白名单、purchaseDate有效性;保存 jws_hash 做去重。
  • 退款:在 App 内提供“请求退款”的帮助页并使用系统接口触发后端无需额外API。

客户端使用说明StoreKit 2

  • 产品拉取与展示:
    • 通过已知 productId 列表调用 Product.products(for:);展示总价与描述,检查 canMakePayments
  • 购买:
    • 调用 purchase(),系统确认表单弹出→返回 Transaction;执行 await transaction.verify()
    • 成功后将 transaction.signedData POST 到 /api/iap/apple/transactions/attach
  • 恢复:
    • 调用 Transaction.currentEntitlements,遍历并验证每条 Transaction,将其 signedData 批量 POST 到 /api/iap/apple/restore
  • 状态显示:
    • 访问 GET /api/iap/apple/status 获取到期时间与权益用于 UI 展示
  • 退款入口:
    • 在购买帮助页直接使用 beginRefundRequest(for:in:);文案简洁,按钮直达

测试与验收

  • 单元测试JWS 解析、productId→权益/时长 映射、去重策略。
  • 集成测试:绑定/恢复接口鉴权与幂等、统一订阅查询结果。
  • 沙盒:使用 iOS 沙盒购买与恢复;记录审计与日志。

里程碑

  1. 基础能力:products/statustransactions/attach 落地
  2. 恢复与融合:restore + 统一订阅聚合
  3. 上线前验证:沙盒测试与文案、监控