黑苹果macOS StoreKit 2现代内购与订阅管理框架完全指南
发布时间:2026年6月 | 分类:黑苹果 | 关键词:StoreKit 2、内购开发、订阅管理
前言:StoreKit 2带来的内购革命
Apple在2022年随Xcode 14推出的StoreKit 2是对内购开发的一次全面革新。基于Swift Concurrency构建的全新API彻底取代了原有的delegate回调模式,让内购和订阅管理的代码变得更加简洁和可靠。对于在黑苹果上进行macOS应用开发的独立开发者来说,理解并掌握StoreKit 2是实现商业变现的关键能力。
本文将从StoreKit 2的核心概念出发,逐步讲解产品查询、购买流程、交易验证、订阅管理等完整链路,并特别关注在黑苹果环境下的开发调试要点。
一、StoreKit 2架构概览
StoreKit 2基于Swift 5.5的async/await语法构建,提供了更加类型安全和可组合的API设计。相比旧版StoreKit,主要变化包括:
1.1 核心类型对比
| StoreKit 1 | StoreKit 2 | 说明 |
| SKProduct | Product | 商品信息,新增类型属性 |
| SKPayment | Product.purchase() | 购买变为产品的方法 |
| SKPaymentTransaction | Transaction | 交易记录,自动验证 |
| SKPaymentQueue | Transaction.updates | 交易监听,AsyncSequence |
| SKProductsRequest | Product.products(for:) | 商品查询,async简化 |
| Receipt | Transaction JWS | 收据验证变为JWS签名验证 |
1.2 新增关键特性
- 自动JWS验证:Transaction自带Apple签名,无需服务端验证收据
- Swift Concurrency原生支持:所有API均为async函数
- Product type enum:类型安全的商品类型区分
- Transaction.updates:AsyncSequence监听交易变更
- AppTransaction:应用购买记录的统一查询
- RefundChecker:退款状态实时监听
二、商品查询与展示
在实现内购之前,首先需要从App Store Connect查询已配置的商品信息。
2.1 查询商品列表
import StoreKit
// 查询指定商品ID的产品信息
let productIDs = ["com.example.pro_monthly", "com.example.pro_yearly", "com.example.coin_pack"]
let products = try await Product.products(for: productIDs)
for product in products {
print("商品名称: \(product.displayName)")
print("商品描述: \(product.description)")
print("价格: \(product.displayPrice)")
print("类型: \(product.type)")
// 订阅特有属性
if case .autoRenewable = product.type {
if let subscription = product.subscription {
print("订阅周期: \(subscription.subscriptionGroupID)")
print("优惠价格: \(subscription.introductoryOffer?.price ?? 0)")
}
}
}
2.2 Product类型详解
StoreKit 2中的Product通过type属性区分不同商品类型:
switch product.type {
case .autoRenewable:
// 自动续期订阅:月度/年度会员
break
case .nonRenewable:
// 非续期订阅:限时服务
break
case .consumable:
// 消耗型:金币、道具
break
case .nonConsumable:
// 非消耗型:永久解锁功能
break
@unknown default:
break
}
2.3 订阅优惠信息
对于自动续期订阅,可以获取丰富的优惠信息:
if let subscription = product.subscription {
// 新用户优惠
if let intro = subscription.introductoryOffer {
print("优惠类型: \(intro.paymentMode)")
print("优惠价格: \(intro.displayPrice)")
print("优惠周期: \(intro.period.value) \(intro.period.unit)")
}
// 促销优惠(需服务端签名)
for promo in subscription.promotionalOffers {
print("促销ID: \(promo.id)")
print("促销价格: \(promo.displayPrice)")
}
}
三、购买流程实现
StoreKit 2将购买流程简化为一个async函数调用,大大降低了实现复杂度。
3.1 基础购买流程
import StoreKit
func purchase(_ product: Product) async throws -> Transaction? {
let result = try await product.purchase()
switch result {
case .success(let verification):
// 验证交易签名
let transaction = try checkVerified(verification)
print("购买成功: \(transaction.productID)")
// 完成交易(必须调用,否则交易会一直待处理)
await transaction.finish()
return transaction
case .userCancelled:
print("用户取消购买")
return nil
case .pending:
// 交易需要家长批准(Ask to Buy)
print("交易待处理")
return nil
@unknown default:
return nil
}
}
// 验证交易JWS签名
func checkVerified(_ result: VerificationResult<Transaction>) throws -> Transaction {
switch result {
case .unverified(let transaction, let error):
throw error
case .verified(let transaction):
return transaction
}
}
3.2 交易监听与处理
对于可能发生在App外部的交易(如家庭共享、优惠兑换),需要持续监听交易更新:
import StoreKit
class TransactionObserver {
var updateTask: Task<Void, Never>?
func startObserving() {
updateTask = Task(priority: .background) {
for await result in Transaction.updates {
do {
let transaction = try checkVerified(result)
// 处理交易:解锁功能、发放虚拟货币等
await handleTransaction(transaction)
await transaction.finish()
} catch {
print("交易验证失败: \(error)")
}
}
}
}
func stopObserving() {
updateTask?.cancel()
}
}
四、订阅管理深度实现
自动续期订阅是许多macOS应用的核心商业模式,StoreKit 2提供了强大的订阅管理能力。
4.1 当前订阅状态查询
import StoreKit
func getCurrentSubscriptionStatus(groupID: String) async throws -> Product.SubscriptionInfo.Status? {
let statuses = try await Product.SubscriptionInfo.status(for: groupID)
// 找到有效的订阅状态
let validStatus = statuses
.filter { $0.state == .subscribed || $0.state == .inGracePeriod || $0.state == .inBillingRetryPeriod }
.first
return validStatus
}
// 获取最高等级的订阅
func getHighestSubscription() async throws -> Transaction? {
let groupID = "your_subscription_group_id"
let statuses = try await Product.SubscriptionInfo.status(for: groupID)
var highestTransaction: Transaction?
var highestLevel = 0
for status in statuses {
if case .verified(let transaction) = status.transaction {
// 假设产品ID包含等级信息
let level = getSubscriptionLevel(transaction.productID)
if level > highestLevel {
highestLevel = level
highestTransaction = transaction
}
}
}
return highestTransaction
}
4.2 订阅状态变化处理
func handleSubscriptionStatusChange() async {
let groupID = "your_subscription_group_id"
for await result in Transaction.updates {
guard let transaction = try? checkVerified(result) else { continue }
if transaction.productType == .autoRenewable {
// 获取最新订阅状态
let statuses = try? await Product.SubscriptionInfo.status(for: groupID)
let currentStatus = statuses?.first { $0.state == .subscribed }
switch currentStatus?.state {
case .subscribed:
print("用户已订阅")
case .expired:
print("订阅已过期")
case .inGracePeriod:
print("订阅宽限期")
case .inBillingRetryPeriod:
print("账单重试期")
case .revoked:
print("订阅被撤销")
default:
break
}
}
}
}
4.3 订阅优惠签名
对于促销优惠,需要在服务端生成签名。以下是签名生成逻辑:
import CryptoKit
func generatePromotionalOfferSignature(
keyID: String,
nonce: UUID,
timestamp: Int64,
productID: String,
offerID: String,
applicationUsername: String?,
privateKey: P256.KeyAgreement.PrivateKey
) throws -> String {
let payload = "\(keyID)\(nonce.uuidString)\(timestamp)\(productID)\(offerID)\(applicationUsername ?? "")"
let signature = try privateKey.signature(for: Data(payload.utf8))
return signature.rawRepresentation.base64EncodedString()
}
五、黑苹果StoreKit 2开发注意事项
5.1 StoreKit Testing in Xcode
在黑苹果上开发StoreKit 2最大的优势是可以使用Xcode内置的StoreKit Testing环境,无需真机即可完整测试内购流程:
- 创建StoreKit Configuration文件,定义产品和订阅
- 在Scheme中启用StoreKit Configuration
- 使用Xcode的StoreKit管理器控制交易状态
- 模拟各种购买场景:购买、退款、订阅续期等
5.2 黑苹果环境特殊考量
| 项目 | 说明 | 建议 |
| 沙盒测试 | StoreKit Configuration可完全替代 | 使用Xcode内置测试 |
| 收据验证 | StoreKit 2无需传统收据 | 使用JWS验证 |
| 网络连接 | 需稳定的网络连接App Store | 确保博通网卡正常工作 |
| Xcode版本 | 需Xcode 14+ | 黑苹果Sonoma上可安装最新版 |
5.3 调试技巧
- 使用
Transaction.currentEntitlements快速检查用户的当前权益 - 在StoreKit Configuration中启用"Enable StoreKit Logging"查看详细日志
- 使用断点在purchase()调用处暂停,观察完整的交易流程
- 测试退款场景时,使用Xcode的"Refund Purchase"功能
六、App Store Server API集成
对于需要服务端验证的场景,App Store Server API V2提供了完整的交易管理能力。
6.1 JWT签名认证
import CryptoKit
import Foundation
func generateJWT(keyID: String, issuerID: String, bundleID: String, privateKey: P256.KeyAgreement.PrivateKey) throws -> String {
let header = "{\"alg\":\"ES256\",\"kid\":\"\(keyID)\",\"typ\":\"JWT\"}"
let now = Int(Date().timeIntervalSince1970)
let payload = "{\"iss\":\"\(issuerID)\",\"iat\":\(now),\"exp\":\(now + 3600),\"aud\":\"appstoreconnect-v1\",\"bid\":\"\(bundleID)\"}"
// Base64URL编码
let headerData = Data(header.utf8).base64URLEncodedString()
let payloadData = Data(payload.utf8).base64URLEncodedString()
let signingInput = "\(headerData).\(payloadData)"
// ES256签名
let signature = try privateKey.signature(for: Data(signingInput.utf8))
let signatureData = signature.rawRepresentation.base64URLEncodedString()
return "\(signingInput).\(signatureData)"
}
6.2 服务器通知V2
App Store Server Notifications V2通过Webhook实时推送交易状态变更:
// 通知类型示例
// DID_RENEW: 订阅成功续期
// DID_FAIL_TO_RENEW: 续期失败
// REFUND: 用户获得退款
// REVOKE: 撤销家庭共享访问
// CONSUMPTION_REQUEST: 消耗型购买请求
总结
StoreKit 2代表了Apple内购开发的未来方向。基于Swift Concurrency的API设计让内购代码从繁琐的回调嵌套变为清晰的线性流程,JWS自动验证消除了收据验证的痛点。对于在黑苹果上进行macOS应用开发的独立开发者来说,StoreKit 2 + Xcode StoreKit Testing的组合提供了完整的开发和测试环境。
关键要点
- StoreKit 2基于async/await,代码更简洁可靠
- Transaction的JWS自动验证消除了收据验证的复杂性
- Transaction.updates AsyncSequence替代了delegate回调
- Product.SubscriptionInfo提供完整的订阅状态管理
- 黑苹果上使用StoreKit Configuration可以完整测试所有购买场景
如果你在黑苹果上进行StoreKit 2开发时遇到任何问题,欢迎在评论区留言讨论!🍎


评论(0)