You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS内购场景下如何授权AWS Cognito用户,避免订阅共享?

问题根源

这个问题的核心是你的应用没有将Apple IAP订阅记录与AWS Cognito用户身份做绑定关联。iOS沙盒(正式环境同理)中,IAP购买凭证是绑定当前登录的Apple ID的,当同一设备切换Cognito用户时,如果应用直接读取本地缓存的订阅状态、或仅验证Apple ID的订阅而不校验用户身份,就会出现不同Cognito用户共享同一Apple ID订阅的情况。

解决办法

1. 购买成功后,绑定订阅凭证与Cognito用户

用户完成订阅购买后,不要仅在本地保存状态,需执行以下操作:

  • 获取Apple返回的交易凭证(receipt data)
  • 将凭证与当前登录的Cognito用户ID(可通过AWSCognitoIdentityUserPool.default().currentUser()?.username或用户属性中的sub字段获取)一同上传至你的后端服务
  • 后端调用Apple的凭证验证接口(沙盒环境用https://sandbox.itunes.apple.com/verifyReceipt,正式环境用https://buy.itunes.apple.com/verifyReceipt)确认有效性,验证通过后,将订阅状态(有效期、订阅类型、续订状态等)与该Cognito用户ID关联存储(比如存入DynamoDB)

2. 用户登录时,从后端拉取专属订阅状态

当新用户(比如用户B)登录应用时:

  • 禁止直接读取本地缓存的订阅信息,必须向你的后端发起请求,携带当前用户的Cognito ID
  • 后端根据用户ID查询对应的专属订阅记录,返回该用户自身的订阅状态
  • 应用完全依据后端返回的结果来解锁/限制订阅功能,不再依赖本地的Apple ID订阅状态

3. 处理订阅自动续订与状态同步

  • 开启Apple的Server-to-Server Notifications(服务器通知),当订阅发生续订、过期、取消等状态变化时,Apple会主动向你的后端推送通知
  • 后端收到通知后,立即更新对应Cognito用户的订阅状态,确保用户的订阅状态始终为最新
  • 用户每次启动应用时,可主动向后端发起一次订阅状态同步请求,避免本地缓存的状态过期失效

4. 清理本地订阅缓存,避免跨用户串用

  • 尽量不要在本地持久化订阅状态(比如UserDefaults);如果必须使用本地缓存,在用户切换账号时,务必清空之前的订阅缓存数据,防止新用户读取到旧用户的订阅信息
关键代码示例(iOS端)

购买成功后上传凭证与用户ID

func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
    for transaction in transactions {
        switch transaction.transactionState {
        case .purchased:
            // 获取交易凭证
            guard let receiptURL = Bundle.main.appStoreReceiptURL,
                  let receiptData = try? Data(contentsOf: receiptURL) else {
                // 处理凭证获取失败逻辑
                break
            }
            let receiptBase64 = receiptData.base64EncodedString()
            
            // 获取当前Cognito用户ID
            if let currentUser = AWSCognitoIdentityUserPool.default().currentUser() {
                currentUser.getDetails().continueWith { task in
                    guard let userAttributes = task.result?.userAttributes else { return nil }
                    let userId = userAttributes["sub"] ?? currentUser.username
                    // 上传凭证与用户ID到后端
                    self.uploadReceiptToBackend(receipt: receiptBase64, userId: userId)
                    return nil
                }
            }
            
            // 务必完成交易
            SKPaymentQueue.default().finishTransaction(transaction)
        // 处理其他交易状态(如失败、恢复等)
        default:
            break
        }
    }
}

用户登录后拉取专属订阅状态

func fetchUserSubscriptionStatus(userId: String) {
    guard let url = URL(string: "https://your-backend-domain/subscription/status") else { return }
    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    request.httpBody = try? JSONSerialization.data(withJSONObject: ["userId": userId])
    request.setValue("application/json", forHTTPHeaderField: "Content-Type")
    
    URLSession.shared.dataTask(with: request) { data, _, error in
        guard let data = data, error == nil else { return }
        do {
            let status = try JSONDecoder().decode(SubscriptionStatus.self, from: data)
            DispatchQueue.main.async {
                // 根据后端返回的状态更新UI或功能权限
                self.updateSubscriptionAccess(with: status)
            }
        } catch {
            // 解析失败处理
        }
    }.resume()
}

// 自定义订阅状态模型
struct SubscriptionStatus: Codable {
    let isActive: Bool
    let expirationDate: Date?
    let subscriptionType: String
}

内容的提问来源于stack exchange,提问作者Rino

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 00:50:45