1
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// StoreKitConfig.swift
|
||||
// StoreKit2Manager
|
||||
//
|
||||
// Created by xiaopin on 2025/12/6.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// StoreKit 配置模型
|
||||
public struct StoreKitConfig {
|
||||
/// 产品ID数组
|
||||
public let productIds: [String]
|
||||
|
||||
/// 终生会员ID
|
||||
public let lifetimeIds: [String]
|
||||
|
||||
/// 非续订订阅的过期天数(从购买日期开始计算)
|
||||
/// 如果为 nil,则表示非续订订阅永不过期
|
||||
public let nonRenewableExpirationDays: Int?
|
||||
|
||||
/// 是否自动排序产品(按价格从低到高)
|
||||
public let autoSortProducts: Bool
|
||||
|
||||
/// 是否显示日志
|
||||
public let showLog: Bool
|
||||
|
||||
/// 初始化配置
|
||||
/// - Parameters:
|
||||
/// - productIds: 产品ID数组
|
||||
/// - lifetimeIds: 终生会员ID
|
||||
/// - nonRenewableExpirationDays: 非续订订阅过期天数,默认为 365 天
|
||||
/// - autoSortProducts: 是否自动排序产品,默认为 true
|
||||
/// - showLog: 是否显示日志,默认为 false
|
||||
public init(
|
||||
productIds: [String],
|
||||
lifetimeIds: [String],
|
||||
nonRenewableExpirationDays: Int? = 365,
|
||||
autoSortProducts: Bool = true,
|
||||
showLog: Bool = false
|
||||
) {
|
||||
self.productIds = productIds
|
||||
self.lifetimeIds = lifetimeIds
|
||||
self.nonRenewableExpirationDays = nonRenewableExpirationDays
|
||||
self.autoSortProducts = autoSortProducts
|
||||
self.showLog = showLog
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
//
|
||||
// StoreKitError.swift
|
||||
// StoreKit2Manager
|
||||
//
|
||||
// Created by xiaopin on 2025/12/6.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// StoreKit 错误类型
|
||||
public enum StoreKitError: Error, LocalizedError {
|
||||
/// 产品未找到
|
||||
case productNotFound(String)
|
||||
|
||||
/// 购买失败
|
||||
case purchaseFailed(Error)
|
||||
|
||||
/// 交易验证失败
|
||||
case verificationFailed
|
||||
|
||||
/// 配置缺失
|
||||
case configurationMissing
|
||||
|
||||
/// 服务未启动
|
||||
case serviceNotStarted
|
||||
|
||||
/// 购买正在进行中
|
||||
case purchaseInProgress
|
||||
|
||||
/// 取消订阅失败
|
||||
case cancelSubscriptionFailed(Error)
|
||||
|
||||
/// 恢复购买失败
|
||||
case restorePurchasesFailed(Error)
|
||||
|
||||
/// 未知错误
|
||||
case unknownError
|
||||
|
||||
public var errorDescription: String? {
|
||||
switch self {
|
||||
case .productNotFound(let id):
|
||||
return "产品未找到: \(id)"
|
||||
case .purchaseFailed(let error):
|
||||
return "购买失败: \(error.localizedDescription)"
|
||||
case .verificationFailed:
|
||||
return "交易验证失败,可能是设备已越狱或交易数据被篡改"
|
||||
case .configurationMissing:
|
||||
return "配置缺失,请先调用 configure 方法进行配置"
|
||||
case .serviceNotStarted:
|
||||
return "服务未启动,请先调用 configure 方法"
|
||||
case .purchaseInProgress:
|
||||
return "购买正在进行中,请等待当前购买完成"
|
||||
case .cancelSubscriptionFailed(let error):
|
||||
return "取消订阅失败: \(error.localizedDescription)"
|
||||
case .restorePurchasesFailed(let error):
|
||||
return "恢复购买失败: \(error.localizedDescription)"
|
||||
case .unknownError:
|
||||
return "未知错误"
|
||||
}
|
||||
}
|
||||
|
||||
public var failureReason: String? {
|
||||
switch self {
|
||||
case .productNotFound(let id):
|
||||
return "请检查产品ID是否正确,并确保在 App Store Connect 中已配置该产品"
|
||||
case .purchaseFailed(let error):
|
||||
return error.localizedDescription
|
||||
case .verificationFailed:
|
||||
return "交易数据无法通过 Apple 的验证,这可能是由于设备已越狱或交易数据被篡改"
|
||||
case .configurationMissing:
|
||||
return "在调用其他方法之前,必须先调用 configure(with:delegate:) 或 configure(with:onStateChanged:) 方法"
|
||||
case .serviceNotStarted:
|
||||
return "StoreKit2Manager 尚未初始化,请先调用 configure 方法"
|
||||
case .purchaseInProgress:
|
||||
return "当前有购买正在进行,请等待完成后再试"
|
||||
case .cancelSubscriptionFailed(let error):
|
||||
return (error as? LocalizedError)?.failureReason ?? error.localizedDescription
|
||||
case .restorePurchasesFailed(let error):
|
||||
return (error as? LocalizedError)?.failureReason ?? error.localizedDescription
|
||||
case .unknownError:
|
||||
return "发生了未预期的错误"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
//
|
||||
// StoreKitState.swift
|
||||
// StoreKit2Manager
|
||||
//
|
||||
// Created by xiaopin on 2025/12/6.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import StoreKit
|
||||
|
||||
/// StoreKit 状态枚举
|
||||
public enum StoreKitState: Equatable {
|
||||
/// 空闲状态
|
||||
case idle
|
||||
|
||||
/// 正在加载产品
|
||||
case loadingProducts
|
||||
|
||||
/// 正在加载已购买产品
|
||||
case loadingPurchases
|
||||
|
||||
/// 已购买产品加载完成
|
||||
case purchasesLoaded
|
||||
|
||||
/// 正在购买指定产品
|
||||
case purchasing(String) // 产品ID
|
||||
|
||||
/// 购买成功
|
||||
case purchaseSuccess(String) // 产品ID
|
||||
|
||||
/// 购买待处理(需要用户操作)
|
||||
case purchasePending(String) // 产品ID
|
||||
|
||||
/// 用户取消购买
|
||||
case purchaseCancelled(String) // 产品ID
|
||||
|
||||
/// 购买失败
|
||||
case purchaseFailed(String, Error) // 产品ID, 错误
|
||||
|
||||
/// 正在恢复购买
|
||||
case restoringPurchases
|
||||
|
||||
/// 恢复购买成功
|
||||
case restorePurchasesSuccess
|
||||
|
||||
/// 恢复购买失败
|
||||
case restorePurchasesFailed(Error)
|
||||
|
||||
/// 购买已退款
|
||||
case purchaseRefunded(String) // 产品ID
|
||||
|
||||
/// 购买已撤销
|
||||
case purchaseRevoked(String) // 产品ID
|
||||
|
||||
/// 订阅已取消
|
||||
/// - Parameters:
|
||||
/// - productId: 产品ID
|
||||
/// - isFreeTrialCancelled: 是否在免费试用期取消(true 表示在免费试用期内取消,false 表示在付费订阅期内取消)
|
||||
case subscriptionCancelled(String, isFreeTrialCancelled: Bool)
|
||||
|
||||
/// 发生错误
|
||||
case error(Error)
|
||||
|
||||
public static func == (lhs: StoreKitState, rhs: StoreKitState) -> Bool {
|
||||
switch (lhs, rhs) {
|
||||
case (.idle, .idle),
|
||||
(.loadingProducts, .loadingProducts),
|
||||
(.loadingPurchases, .loadingPurchases),
|
||||
(.purchasesLoaded, .purchasesLoaded):
|
||||
return true
|
||||
case (.purchasing(let lhsId), .purchasing(let rhsId)),
|
||||
(.purchaseSuccess(let lhsId), .purchaseSuccess(let rhsId)),
|
||||
(.purchasePending(let lhsId), .purchasePending(let rhsId)),
|
||||
(.purchaseCancelled(let lhsId), .purchaseCancelled(let rhsId)):
|
||||
return lhsId == rhsId
|
||||
case (.purchaseFailed(let lhsId, _), .purchaseFailed(let rhsId, _)):
|
||||
return lhsId == rhsId
|
||||
case (.restoringPurchases, .restoringPurchases),
|
||||
(.restorePurchasesSuccess, .restorePurchasesSuccess):
|
||||
return true
|
||||
case (.restorePurchasesFailed(let lhsError), .restorePurchasesFailed(let rhsError)):
|
||||
return lhsError.localizedDescription == rhsError.localizedDescription
|
||||
case (.purchaseRefunded(let lhsId), .purchaseRefunded(let rhsId)),
|
||||
(.purchaseRevoked(let lhsId), .purchaseRevoked(let rhsId)):
|
||||
return lhsId == rhsId
|
||||
case (.subscriptionCancelled(let lhsId, let lhsIsFreeTrial), .subscriptionCancelled(let rhsId, let rhsIsFreeTrial)):
|
||||
return lhsId == rhsId && lhsIsFreeTrial == rhsIsFreeTrial
|
||||
case (.error(let lhsError), .error(let rhsError)):
|
||||
return lhsError.localizedDescription == rhsError.localizedDescription
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
//
|
||||
// TransactionHistory.swift
|
||||
// StoreKit2Manager
|
||||
//
|
||||
// Created by xiaopin on 2025/12/6.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import StoreKit
|
||||
|
||||
/// 交易历史记录
|
||||
public struct TransactionHistory {
|
||||
/// 产品ID
|
||||
public let productId: String
|
||||
|
||||
/// 产品对象
|
||||
public let product: Product?
|
||||
|
||||
/// 交易对象
|
||||
public let transaction: StoreKit.Transaction
|
||||
|
||||
/// 购买日期
|
||||
public let purchaseDate: Date
|
||||
|
||||
/// 过期日期(如果是订阅)
|
||||
public let expirationDate: Date?
|
||||
|
||||
/// 是否已退款
|
||||
public let isRefunded: Bool
|
||||
|
||||
/// 是否已撤销
|
||||
public let isRevoked: Bool
|
||||
|
||||
/// 所有权类型
|
||||
public let ownershipType: StoreKit.Transaction.OwnershipType
|
||||
|
||||
/// 交易ID
|
||||
public let transactionId: UInt64
|
||||
|
||||
public init(
|
||||
productId: String,
|
||||
product: Product?,
|
||||
transaction: StoreKit.Transaction,
|
||||
purchaseDate: Date,
|
||||
expirationDate: Date? = nil,
|
||||
isRefunded: Bool = false,
|
||||
isRevoked: Bool = false,
|
||||
ownershipType: StoreKit.Transaction.OwnershipType,
|
||||
transactionId: UInt64
|
||||
) {
|
||||
self.productId = productId
|
||||
self.product = product
|
||||
self.transaction = transaction
|
||||
self.purchaseDate = purchaseDate
|
||||
self.expirationDate = expirationDate
|
||||
self.isRefunded = isRefunded
|
||||
self.isRevoked = isRevoked
|
||||
self.ownershipType = ownershipType
|
||||
self.transactionId = transactionId
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - 从 Transaction 创建
|
||||
extension TransactionHistory {
|
||||
/// 从 Transaction 创建交易历史
|
||||
public static func from(_ transaction: StoreKit.Transaction, product: Product? = nil) -> TransactionHistory {
|
||||
return TransactionHistory(
|
||||
productId: transaction.productID,
|
||||
product: product,
|
||||
transaction: transaction,
|
||||
purchaseDate: transaction.purchaseDate,
|
||||
expirationDate: transaction.expirationDate,
|
||||
isRefunded: transaction.revocationDate != nil,
|
||||
isRevoked: transaction.revocationDate != nil,
|
||||
ownershipType: transaction.ownershipType,
|
||||
transactionId: transaction.id
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user