实现Apple应用内购支付功能,包括: 1. 新增AppleIAP和ApplePay支付平台枚举 2. 添加IAP验证接口/v1/public/iap/verify处理初购验证 3. 实现Apple服务器通知处理逻辑/v1/iap/notifications 4. 新增JWS验签和JWKS公钥缓存功能 5. 复用现有订单系统处理IAP支付订单 相关文档已更新,包含接入方案和实现细节
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
## 实施目标
|
||||
- 复用现有订单与队列赋权,接入 Apple 自动续期(IAP),保持报表/审计/通知一致。
|
||||
|
||||
## 方案选择
|
||||
- 采用“平台化复用 + 合成订单”的方式:Apple 由客户端结算 + 服务器通知驱动,服务端生成“已支付订单”进入现有赋权与续费流程。
|
||||
|
||||
## 具体改动(按文件)
|
||||
1) 平台标识
|
||||
- 更新 `pkg/payment/platform.go`:新增 `AppleIAP` 枚举与名称(仅标识,不参与 `PurchaseCheckout`)。
|
||||
|
||||
2) 路由与 Handler
|
||||
- 新增公共接口:`POST /v1/public/iap/verify`
|
||||
- 位置:`internal/handler/public/iap/verifyHandler.go`
|
||||
- 逻辑:调用 `internal/logic/public/iap/verifyLogic.go`,以 `originalTransactionId` 验证 Apple 购买,生成“已支付订阅订单”,入队激活。
|
||||
- 新增通知接口:`POST /v1/iap/notifications`
|
||||
- 位置:`internal/handler/notify/appleIAPNotifyHandler.go`
|
||||
- 逻辑:调用 `internal/logic/notify/appleIAPNotifyLogic.go`,JWS 验签后按事件(初购/续期/退款)生成或更新订单,触发续费或撤销权益。
|
||||
- 路由注册:
|
||||
- `internal/handler/routes.go` 增加 `/v1/public/iap/verify` 路由。
|
||||
- `internal/handler/notify.go` 增加独立 `/v1/iap/notifications` 路由(Apple 不带 `:token`)。
|
||||
|
||||
3) 数据与模型
|
||||
- 在用户订阅(或新建 `iap_binding` 表)绑定:`originalTransactionId`、`environment`、`latestExpiresDate`。
|
||||
- 订单字段复用:`Method=AppleIAP`、`TradeNo=originalTransactionId`、`Type=1/2`(订阅/续费),`Status=2`(已支付),金额可取通知中的价格;取不到则置 `Amount=0` 保证流程。
|
||||
|
||||
4) 逻辑复用与改造点
|
||||
- 赋权:复用 `queue/logic/order/activateOrderLogic.go:165 NewPurchase`。
|
||||
- 续费:复用 `queue/logic/order/activateOrderLogic.go:529 updateSubscriptionForRenewal`。
|
||||
- 不改动 `internal/logic/public/portal/purchaseCheckoutLogic.go` 的渠道路由(Apple 不走此流程)。
|
||||
|
||||
5) 安全与幂等
|
||||
- Apple JWS 验签:拉取并缓存 JWKS 公钥,校验通知;拒绝无效签名。
|
||||
- 幂等:以 `notificationId`/`transactionId` 与 `originalTransactionId` 去重处理。
|
||||
|
||||
6) 客户端协作
|
||||
- iOS:完成 StoreKit 购买后携带 `originalTransactionId` 调用 `/v1/public/iap/verify`。
|
||||
- 续费:依赖 Server Notifications v2 自动驱动,无需客户端调用。
|
||||
|
||||
7) 测试与监控
|
||||
- 沙盒验证初购、续期、重试与宽限期、退款撤销;注意元数据延迟(~1小时)。
|
||||
- 指标:通知验签失败、API 调用失败、幂等冲突、状态不一致报警。
|
||||
|
||||
## 交付节奏
|
||||
- 第一步:平台枚举与路由骨架;
|
||||
- 第二步:`verify` 验证与“合成订单”生成;
|
||||
- 第三步:通知验签与事件映射;
|
||||
- 第四步:沙盒联调,确认队列赋权与续费延长。
|
||||
@@ -0,0 +1,92 @@
|
||||
## 结论
|
||||
|
||||
* 可以复用你现有的“订单→支付成功→订单激活(赋权)→通知/返佣”的主干流程,但“支付环节”不能复用第三方网关逻辑,必须改为 Apple IAP 的校验与事件驱动。
|
||||
|
||||
* 复用范围:订单模型、续费与赋权队列、优惠/手续费计算、通知与返佣;差异点:支付下单与回调换成“StoreKit 客户端购买 + 服务端向 Apple 校验 + Apple Server Notifications v2”。
|
||||
|
||||
## 可复用的部分
|
||||
|
||||
1. 订单激活与赋权
|
||||
|
||||
* 新购赋权:`queue/logic/order/activateOrderLogic.go:164-193` 的 `NewPurchase`
|
||||
|
||||
* 续费赋权:`queue/logic/order/activateOrderLogic.go:473-515` 的 `Renewal`
|
||||
|
||||
* 流量重置与充值:`queue/logic/order/activateOrderLogic.go:564-626`, `630-675`
|
||||
|
||||
1. 订单与费用模型
|
||||
|
||||
* 订单结构:`internal/model/order/order.go:5-29` 可继续承载 IAP 订单(新增字段映射 Apple 交易)
|
||||
|
||||
* 费用/折扣/礼金计算逻辑保持不变
|
||||
|
||||
1. 队列驱动
|
||||
|
||||
* 仍使用“支付成功→入队→处理”的模式:`queue/logic/order/activateOrderLogic.go:65-86`
|
||||
|
||||
## 必须独立实现的部分
|
||||
|
||||
1. Apple IAP 支付与校验
|
||||
|
||||
* 客户端使用 StoreKit 购买,拿到 `originalTransactionId`
|
||||
|
||||
* 服务端调用 App Store Server API,基于 `originalTransactionId` 校验订阅有效性并取交易历史
|
||||
|
||||
1. Apple Server Notifications v2
|
||||
|
||||
* 在 App Store Connect 配置通知 URL
|
||||
|
||||
* 服务端实现 JWS 验签,解析事件并落库:续期、失败、宽限期、退款、撤销等
|
||||
|
||||
## 整合方式(复用策略)
|
||||
|
||||
1. 引入平台枚举“AppleIAP”
|
||||
|
||||
* 在 `pkg/payment/platform.go` 增加 `AppleIAP`,用于平台标识与管理端展示
|
||||
|
||||
1. 订单创建策略(两种)
|
||||
|
||||
* 方案 A(推荐):用户在 iOS 内购完成后由客户端上报 `originalTransactionId`,服务端校验通过后“合成一个已支付订单”(`status=2`),触发既有赋权队列
|
||||
|
||||
* 方案 B:也可预建“待支付订单”,但 `PurchaseCheckout` 不走网关,只返回“client\_iap”类型,提示客户端用 StoreKit;支付完成后再校验并更新为 `Paid` 入队
|
||||
|
||||
1. 状态与权益判定
|
||||
|
||||
* 服务端统一以 Apple 校验与通知为准,抽象为 `active/in_grace_period/in_billing_retry/expired/revoked` 并映射到你的订阅与订单状态
|
||||
|
||||
## 服务端接口与流程
|
||||
|
||||
* `POST /apple/iap/verify`:入参 `originalTransactionId`,校验并创建/更新订单与用户订阅,返回当前权益
|
||||
|
||||
* `POST /apple/iap/notifications`:接收 Apple JWS 通知,验签后更新订阅与订单状态(幂等)
|
||||
|
||||
* `GET /subscriptions/me`:面向客户端查询当前订阅与权益(聚合 Apple 校验结果)
|
||||
|
||||
## 数据模型映射
|
||||
|
||||
* 在订单/订阅表补充字段(建议):`Provider=apple_iap`、`OriginalTransactionId`、`Environment`、`ExpiresDate`、`AutoRenewStatus`、`InGracePeriod`、`LastEventType`
|
||||
|
||||
* 产品映射:`productId(App Store) ↔ internal subscribeId`,保证同一权益统一计费
|
||||
|
||||
## 回调与安全
|
||||
|
||||
* JWS 验签:缓存 Apple JWKS 公钥、短生命周期缓存
|
||||
|
||||
* App Store Server API:使用 App Store Connect API Key(ES256)发起请求,区分生产/沙盒
|
||||
|
||||
* 幂等:按 `notificationId/transactionId` 去重
|
||||
|
||||
## 测试与上线
|
||||
|
||||
* 沙盒测试:购买、续期、失败、宽限期、退款全链路;注意沙盒元数据生效可能需 \~1 小时
|
||||
|
||||
* 监控:通知处理失败、验签失败、API 调用异常报警
|
||||
|
||||
## 交付物(最小实现)
|
||||
|
||||
1. 平台枚举新增 `AppleIAP`
|
||||
2. `POST /apple/iap/verify` 与 `POST /apple/iap/notifications` 路由与逻辑骨架
|
||||
3. App Store Server API 客户端封装(校验、交易历史、订阅状态)
|
||||
4. 订单合成与入队赋权打通(复用 `OrderStatusPaid` → `ProcessTask`)
|
||||
5. 数据表字段扩展与迁移脚本
|
||||
|
||||
Reference in New Issue
Block a user