All checks were successful
Build docker and publish / build (20.15.1) (push) Successful in 6m37s
新增苹果IAP相关接口与逻辑,包括产品列表查询、交易绑定、状态查询和恢复购买功能。移除旧的IAP验证逻辑,重构订阅系统以支持苹果IAP交易记录存储和权益计算。 - 新增/pkg/iap/apple包处理JWS解析和产品映射 - 实现GET /products、POST /attach、POST /restore和GET /status接口 - 新增apple_iap_transactions表存储交易记录 - 更新文档说明配置方式和接口规范 - 移除旧的AppleIAP验证和通知处理逻辑
4.1 KiB
4.1 KiB
目标
- 不使用自动续期订阅;采用“非续期订阅”或“非消耗型”作为内购模式。
- 仅实现 Go 后端 API;客户端(iOS/StoreKit 2)按说明调用。
产品模型
- 非续期订阅:固定时长通行证(如 30/90/365 天),产品ID:
com.airport.vpn.pass.30d|90d|365d。 - 非消耗型(可选):一次性解锁某附加功能,产品ID:
com.airport.vpn.addon.xyz。 - 服务器以
productId→权益/时长进行配置映射。
后端API设计(Go/Gin)
- 路由注册:
internal/handler/routes.goGET /api/iap/apple/products:返回前端展示的产品清单(含总价/描述/时长映射)POST /api/iap/apple/transactions/attach:绑定一次购买到用户账户(需登录)。入参:signedTransactionJWSPOST /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/appleParseTransactionJWS:解析 JWS,提取transactionId/originalTransactionId/productId/purchaseDate/revocationDateVerifyBasic:基础校验(bundleId、签名头部与证书链存在性);如客户端已transaction.verify(),可采用“信任+服务器最小校验”的模式快速落地
- 配置:
doc/config-zh.mdIAP_PRODUCT_MAP:productId → tier/duration(例如:30d→+30天、addon→解锁功能X)APPLE_IAP_BUNDLE_ID:用于 JWS 内部校验
数据模型
- 新表:
apple_iap_transactionsid、user_id、original_transaction_id(唯一)、transaction_id、product_id、purchase_at、revocation_at、jws_hash
- 统一订阅表增强(现有
SubscribeModel)- 新增来源:
source=apple_iap、external_id=original_transaction_id、tier、expires_at
- 新增来源:
- 索引:
original_transaction_id唯一、user_id+source、expires_at
与现有系统融合
internal/svc/serviceContext.go:初始化 IAP 模块与模型QueryPurchaseOrderLogic/SubscribeModel:聚合苹果IAP来源;冲突策略:按最高权益与最晚到期。- 不产生命令行支付订单,仅记录订阅流水与审计(避免与 Stripe 等混淆)。
安全与合规
- 仅显示商店在可支付时;价格、描述清晰;使用系统确认表单。
- 服务器进行最小校验:
bundleId、productId白名单、purchaseDate有效性;保存jws_hash做去重。 - 退款:在 App 内提供“请求退款”的帮助页并使用系统接口触发;后端无需额外API。
客户端使用说明(StoreKit 2)
- 产品拉取与展示:
- 通过已知
productId列表调用Product.products(for:);展示总价与描述,检查canMakePayments
- 通过已知
- 购买:
- 调用
purchase(),系统确认表单弹出→返回Transaction;执行await transaction.verify() - 成功后将
transaction.signedDataPOST 到/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 沙盒购买与恢复;记录审计与日志。
里程碑
- 基础能力:
products/status与transactions/attach落地 - 恢复与融合:
restore+ 统一订阅聚合 - 上线前验证:沙盒测试与文案、监控