我正在为我的 SwiftUI 应用程序设计一个自动续订订阅。我能够对升级和降级到不同订阅级别以及自动续订等事件做出反应。
问题是当用户取消订阅时我没有收到更新。 取消本身发生在应用程序外部(从“设置/配置文件/订阅”或在 Xcode 中通过“管理 StoreKit 事务”),因为应用程序中没有取消按钮。
理想情况下,我只想在 UserDefaults 中为每个高级功能存储两个布尔值,以便知道是否应该将其呈现给用户。 但对于当前的问题,我正在考虑使用过期日期(将在每次续订时更新)而不是布尔值,并每次根据当前日期检查它们以查看它们是否过期。
您是否看到我是否错过了
PurchaseManager
中的某些内容而无法获得订阅取消的更新?您认为这个解决方法足以完成工作吗?
import Foundation
import StoreKit
typealias SkTransaction = StoreKit.Transaction
enum PremiumFeature: String {
case unlimitedWallets = "unlimitedWallets"
case unlimitedReceiptScans = "unlimitedReceiptScans"
}
@MainActor
class PurchaseManager: ObservableObject {
static let productIds: Set<String> = Set(["plus","premium"])
@Published var products: [Product] = []
private var subscriptionManager: SubscriptionManager
private var updates: Task<Void, Never>? = nil
init(subscriptionManager: SubscriptionManager) {
self.subscriptionManager = subscriptionManager
updates = observeTransactionUpdates()
}
deinit {
updates?.cancel()
}
func loadProducts() async throws {
products = try await Product.products(for: PurchaseManager.productIds)
}
func purchase(_ product: Product) async throws {
print("Purcahse initiated for \(product.id)")
let result = try await product.purchase()
switch result {
case let .success(.verified(transaction)):
// Successful purhcase
await transaction.finish()
await updatePurchasedProducts()
case .success(.unverified(_, _)):
// Successful purchase but transaction/receipt can't be verified
// Could be a jailbroken phone
break
case .pending:
// Transaction waiting on SCA (Strong Customer Authentication) or
// approval from Ask to Buy
break
case .userCancelled:
// ^^^
break
@unknown default:
break
}
}
func updatePurchasedProducts() async {
for await result in SkTransaction.currentEntitlements {
guard case .verified(let transaction) = result else {
continue
}
let currentDate = Date()
let isSubscriptionActive = transaction.expirationDate == nil || currentDate <= transaction.expirationDate!
if transaction.revocationDate == nil && isSubscriptionActive {
// Subscription is active
switch transaction.productID {
case "plus":
print("Plus subscription active")
subscriptionManager.unlimitedWallets = true
case "premium":
print("Premium subscription active")
subscriptionManager.unlimitedWallets = true
subscriptionManager.unlimitedReceiptScans = true
default:
print("Unknown product ID \(transaction.productID)")
}
} else {
// Subscription is cancelled or expired
switch transaction.productID {
case "plus":
print("Plus subscription cancelled or expired")
subscriptionManager.unlimitedWallets = false
case "premium":
print("Premium subscription cancelled or expired")
subscriptionManager.unlimitedWallets = false
subscriptionManager.unlimitedReceiptScans = false
default:
print("Unknown product ID \(transaction.productID)")
}
}
await transaction.finish()
}
}
private func observeTransactionUpdates() -> Task<Void, Never> {
Task(priority: .background) { [unowned self] in
for await result in SkTransaction.updates {
print("transaction updated observed")
guard case .verified(let transaction) = result else {
continue
}
await updatePurchasedProducts()
await transaction.finish()
}
}
}
}
这是我从@main 调用它的方式
/*...*/
ContentView()
.task {
await purchaseManager.updatePurchasedProducts()
do {
try await purchaseManager.loadProducts()
} catch {
print(error)
}
}
/*...*/
我仅使用 Xcode 及其工具“管理 StoreKit 事务”来测试订阅,因此我不确定这是否不仅仅是该工具中的错误。
我在互联网上找到的解决此问题的另一种方法是使用计时器,但经过快速研究后发现计时器的使用时间很短且不可靠,尤其是在 1 个月的时间范围内。
另一个有效的选择是设置一个服务器,该服务器将从 AppStore 接收更新,然后与我的应用程序进行通信,但我想避免为此使用服务器。
没有“订阅取消”事件。当用户取消订阅时,订阅期的剩余时间将继续,然后不会续订。
currentEntitlements
是否有您的订阅产品。如果存在订阅产品标识符,则表明存在有效订阅(或处于宽限期内的订阅),您应该提供适当的权益。如果没有订阅产品标识符,则删除福利。