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

iOS内购:如何通过收据数据获取已取消订阅的活跃产品?

How to Get the Currently Active Product for a Canceled Subscription via Receipt Data

Hey there! Let's walk through exactly how to pull the active product from a canceled subscription using your app's receipt data. Here's a step-by-step breakdown with code examples to make it concrete:

1. Fetch and Parse the Receipt

First, you'll need to retrieve and parse your app's receipt (either the local receipt or the validated one from Apple's server). Convert the receipt data into a dictionary you can work with:

guard let receiptURL = Bundle.main.appStoreReceiptURL,
      let receiptData = try? Data(contentsOf: receiptURL),
      let receiptDict = try? JSONSerialization.jsonObject(with: receiptData, options: []) as? [String: Any] else {
    // Handle receipt not found or parsing error
    return nil
}

2. Access Subscription Transaction Records

The receipt's latest_receipt_info array contains all subscription transaction entries (each entry represents a billing cycle). You'll want to filter this array to focus on the subscription in question, using the original_transaction_id (this ID stays consistent across all billing cycles for the same subscription):

guard let latestReceiptInfo = receiptDict["latest_receipt_info"] as? [[String: Any]],
      let targetOriginalTransactionId = "YOUR_TARGET_ORIGINAL_TRANSACTION_ID" else {
    return nil
}

// Filter entries for the specific subscription
let subscriptionEntries = latestReceiptInfo.filter { entry in
    entry["original_transaction_id"] as? String == targetOriginalTransactionId
}

3. Identify Canceled Subscriptions with Active Access

A canceled subscription will have a cancel_date field in its transaction entry. But remember: even if canceled, the subscription remains active until its expires_date passes. We need to check if the expiration date is still in the future:

// Helper to convert ISO 8601 receipt dates to Date
func receiptDate(from dateString: String?) -> Date? {
    guard let dateString = dateString else { return nil }
    let formatter = ISO8601DateFormatter()
    formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
    return formatter.date(from: dateString)
}

let now = Date()

// Find the latest active canceled subscription entry
if let activeCanceledEntry = subscriptionEntries.last(where: { entry in
    // Check if the subscription was canceled
    entry["cancel_date"] != nil &&
    // Check if it's still active (expiration date is after now)
    let expiresDate = receiptDate(from: entry["expires_date"] as? String),
    expiresDate > now
}) {
    // Extract the active product ID
    let activeProductId = activeCanceledEntry["product_id"] as? String
    print("Currently active product for canceled subscription: \(activeProductId ?? "Unknown")")
    return activeProductId
} else {
    // No active canceled subscription found
    return nil
}

Key Notes to Remember

  • original_transaction_id is critical: This links all billing cycles for the same subscription, so use it to group entries correctly.
  • Cancel vs. Expired: A canceled subscription isn't immediately inactive—users keep access until their current paid cycle ends. Always validate the expires_date against the current time.
  • Server-Side Validation: For production, it's highly recommended to validate receipts on your backend (instead of locally) to avoid tampering. The same logic applies—parse the server's response for the latest_receipt_info array and apply the checks above.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 17:57:27