|
@@ -8,177 +8,16 @@
|
|
|
import Foundation
|
|
|
import StoreKit
|
|
|
|
|
|
-public enum PremiumPeriod{
|
|
|
- case none
|
|
|
- case month
|
|
|
- case year
|
|
|
- case lifetime
|
|
|
- case week(WeekType)
|
|
|
-
|
|
|
- public enum WeekType:String, CaseIterable{
|
|
|
- case week = "week"
|
|
|
- case week1 = "week1"
|
|
|
- case weekPromotional1 = "weekPromotional1"
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-extension PremiumPeriod:CaseIterable ,RawRepresentable {
|
|
|
- /// 所有可迭代的 case(包括 week 的所有子类型)
|
|
|
- public static var allCases: [PremiumPeriod] {
|
|
|
- var cases: [PremiumPeriod] = [.none, .month, .year, .lifetime]
|
|
|
- cases.append(contentsOf: WeekType.allCases.map { .week($0) })
|
|
|
- return cases
|
|
|
- }
|
|
|
-
|
|
|
- public var rawValue: String{
|
|
|
- switch self {
|
|
|
- case .week(let type):
|
|
|
- type.rawValue
|
|
|
- case .month:
|
|
|
- "Monthly"
|
|
|
- case .year:
|
|
|
- "Yearly"
|
|
|
- case .lifetime:
|
|
|
- "Lifetime"
|
|
|
- default:
|
|
|
- ""
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- public init?(rawValue: String) {
|
|
|
- // 先尝试匹配基础类型
|
|
|
- switch rawValue {
|
|
|
- case "Monthly":
|
|
|
- self = .month
|
|
|
- case "Yearly":
|
|
|
- self = .year
|
|
|
- case "Lifetime":
|
|
|
- self = .lifetime
|
|
|
- default:
|
|
|
- if let weekType = WeekType(rawValue: rawValue){
|
|
|
- self = .week(weekType)
|
|
|
- }else{
|
|
|
- self = .none
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- var isWeekType:Bool {
|
|
|
- switch self {
|
|
|
- case .week(_):
|
|
|
- return true
|
|
|
- default:
|
|
|
- return false
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-extension PremiumPeriod {
|
|
|
-
|
|
|
- /// 对应vip类型,可以每天限额次数
|
|
|
- var dayGeneratedNumber: Int {
|
|
|
- return 50//50
|
|
|
- }
|
|
|
- /// 对应vip类型,可以每天限额视频次数
|
|
|
- var dayGeneratedVideoNumber: Int {
|
|
|
- return 20//20
|
|
|
- }
|
|
|
- /// 对应vip类型,可以一共限额视频次数
|
|
|
- var creatVideoMaxNum:Int {
|
|
|
- switch self {
|
|
|
- case .year:
|
|
|
- return 200//200
|
|
|
- default:
|
|
|
- return 20//20
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- var saveString: String {
|
|
|
- switch self {
|
|
|
- case .none:
|
|
|
- return "80%"//"40%" 增加月付费
|
|
|
- default:
|
|
|
- return "80%"
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /*
|
|
|
- 1. 一年(非闰年)
|
|
|
- 365 天 = 365 × 24 × 60 × 60 × 1000 = 31,536,000,000 毫秒
|
|
|
- (若闰年 366 天 = 31,622,400,000 毫秒)
|
|
|
- 2. 一个月(平均)
|
|
|
- 30.44 天(按 365 天/12 个月计算)≈ 30.44 × 24 × 60 × 60 × 1000 ≈ 2,629,746,000 毫秒
|
|
|
- (实际月份天数不同,如 28/30/31 天需单独计算)
|
|
|
- 3. 一周
|
|
|
- 7 天 = 7 × 24 × 60 × 60 × 1000 = 604,800,000 毫秒
|
|
|
- */
|
|
|
- var milliseconds:Int {
|
|
|
- switch self {
|
|
|
- case .year:
|
|
|
- return 365 * 24 * 60 * 60 * 1000
|
|
|
- case .month:
|
|
|
- return 30 * 24 * 60 * 60 * 1000
|
|
|
- case .week(_):
|
|
|
- return 7 * 24 * 60 * 60 * 1000
|
|
|
- default:
|
|
|
- return 0
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-public enum VipFreeNumType: String, CaseIterable {
|
|
|
- case none = "kNone"
|
|
|
- case generatePic = "kGeneratePicFreeNum"
|
|
|
- case aichat = "kAIChatFreeNum"
|
|
|
- case textGeneratePic = "kTextGeneratePicFreeNum"
|
|
|
- case picToPic = "kPicToPicFreeNum"
|
|
|
- case aiGenerate = "kAIGenerateFreeNum"
|
|
|
- case videoV2 = "kVideoV2FreeNum"
|
|
|
-}
|
|
|
-
|
|
|
-public struct PurchaseProduct {
|
|
|
- public let productId: String
|
|
|
- public let period: PremiumPeriod
|
|
|
-
|
|
|
- public init(productId: String, period: PremiumPeriod) {
|
|
|
- self.productId = productId
|
|
|
- self.period = period
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-public enum PremiumRequestState {
|
|
|
- case none
|
|
|
-
|
|
|
- case loading
|
|
|
- case loadSuccess
|
|
|
- case loadFail
|
|
|
-
|
|
|
- case paying
|
|
|
- case paySuccess
|
|
|
- case payFail
|
|
|
-
|
|
|
- case restoreing
|
|
|
- case restoreSuccess
|
|
|
- case restoreFail
|
|
|
-
|
|
|
- case verifying
|
|
|
- case verifySuccess
|
|
|
- case verifyFail
|
|
|
-}
|
|
|
-
|
|
|
public extension Notification.Name {
|
|
|
static let kPurchasePrepared = Self("kPurchaseProductPrepared")
|
|
|
static let kPurchaseDidChanged = Self("kPurchaseDidChanged")
|
|
|
}
|
|
|
|
|
|
-let kFreeNumKey = "kFreeNumKey"
|
|
|
-let kDayGeneratedNumKey = "kDayGeneratedNumKey"
|
|
|
-let kVipGeneratedNumKey = "kVipGeneratedNumKey"
|
|
|
-
|
|
|
-private let kPremiumExpiredInfoKey = "premiumExpiredInfoKey"
|
|
|
+let kPremiumExpiredInfoKey = "premiumExpiredInfoKey"
|
|
|
|
|
|
typealias PurchaseStateChangeHandler = (_ manager: PurchaseManager, _ state: PremiumRequestState, _ object: Any?) -> Void
|
|
|
|
|
|
+
|
|
|
let kPurchaseDefault = PurchaseManager.default
|
|
|
public class PurchaseManager: NSObject {
|
|
|
@objc public static let `default` = PurchaseManager()
|
|
@@ -814,114 +653,7 @@ public extension PurchaseManager {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-/// 免费生成图片次数
|
|
|
-extension PurchaseManager {
|
|
|
- /// 使用一次免费次数
|
|
|
- func useOnceForFree(type: VipFreeNumType) {
|
|
|
- /// 总使用次数
|
|
|
- if isVip {
|
|
|
- saveForDayGeneratedNum(type: type)
|
|
|
- saveForVipGeneratedNum(type: type)
|
|
|
- }
|
|
|
-
|
|
|
- if isVip {
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
- var freeNum = freeDict[type.rawValue] ?? 0
|
|
|
- if freeNum > 0 {
|
|
|
- freeNum -= 1
|
|
|
- }
|
|
|
-
|
|
|
- if freeNum < 0 {
|
|
|
- freeNum = 0
|
|
|
- }
|
|
|
-
|
|
|
- freeDict[type.rawValue] = freeNum
|
|
|
- saveForFree()
|
|
|
-
|
|
|
- NotificationCenter.default.post(name: .kVipFreeNumChanged, object: nil, userInfo: ["VipFreeNumType": type])
|
|
|
- }
|
|
|
-
|
|
|
- func freeNum(type: VipFreeNumType) -> Int {
|
|
|
- let freeNum = freeDict[type.rawValue] ?? 0
|
|
|
- return freeNum
|
|
|
- }
|
|
|
-
|
|
|
- func saveForFree() {
|
|
|
- UserDefaults.standard.set(freeDict, forKey: kFreeNumKey)
|
|
|
- UserDefaults.standard.synchronize()
|
|
|
-
|
|
|
- //保存到钥匙串
|
|
|
- if let jsonString = freeDict.toJSONString() {
|
|
|
- KeychainManager.save(jsonString, forKey: kFreeNumKey)
|
|
|
- dePrint("从钥匙串存入了jsonString kFreeNumKey=\(jsonString)")
|
|
|
- KeychainManager.save(jsonString, forKey: kFreeNumKey)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- func initializeForFree() {
|
|
|
-
|
|
|
- if let jsonString = KeychainManager.load(forKey: kFreeNumKey, type: String.self),
|
|
|
- let jsonDict = jsonString.jsonDict() as? [String: Int]
|
|
|
- {
|
|
|
- freeDict = jsonDict
|
|
|
- saveForFree()
|
|
|
- dePrint("从钥匙串取出了freeDict=\(freeDict)")
|
|
|
- }else{
|
|
|
- if let dict = UserDefaults.standard.dictionary(forKey: kFreeNumKey) as? [String: Int] {
|
|
|
- freeDict = dict
|
|
|
- } else {
|
|
|
- freeDict = [
|
|
|
- VipFreeNumType.generatePic.rawValue: 1,
|
|
|
- VipFreeNumType.aichat.rawValue: 1,
|
|
|
- VipFreeNumType.textGeneratePic.rawValue: 1,
|
|
|
- VipFreeNumType.picToPic.rawValue: 1,
|
|
|
- VipFreeNumType.aiGenerate.rawValue: 1,
|
|
|
- VipFreeNumType.videoV2.rawValue: 1,
|
|
|
- ]
|
|
|
- saveForFree()
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if let jsonString = KeychainManager.load(forKey: kDayGeneratedNumKey, type: String.self),
|
|
|
- let jsonDict = jsonString.jsonDict() as? [String: [String: Any]]
|
|
|
- {
|
|
|
- UserDefaults.standard.set(jsonDict, forKey: kDayGeneratedNumKey)
|
|
|
- UserDefaults.standard.synchronize()
|
|
|
- dePrint("从钥匙串取出了kDayGeneratedNumKey=\(jsonDict)")
|
|
|
- }
|
|
|
-
|
|
|
- if let jsonString = KeychainManager.load(forKey: kVipGeneratedNumKey, type: String.self),
|
|
|
- let jsonDict = jsonString.jsonDict() as? [String: [String: Any]]
|
|
|
- {
|
|
|
- UserDefaults.standard.set(jsonDict, forKey: kVipGeneratedNumKey)
|
|
|
- UserDefaults.standard.synchronize()
|
|
|
- dePrint("从钥匙串取出了kVipGeneratedNumKey=\(jsonDict)")
|
|
|
- }
|
|
|
|
|
|
- }
|
|
|
-
|
|
|
- /// 免费次数是否可用
|
|
|
- func freeNumAvailable(type: VipFreeNumType) -> Bool {
|
|
|
- if isVip == true {
|
|
|
- return true
|
|
|
- } else {
|
|
|
- if let freeNum = freeDict[type.rawValue], freeNum > 0 {
|
|
|
- return true
|
|
|
- }
|
|
|
- }
|
|
|
- return false
|
|
|
- }
|
|
|
-
|
|
|
- /// 是否展示生成类的会员图标
|
|
|
- func generateVipShow(type: VipFreeNumType) -> Bool {
|
|
|
- if isVip == false, freeNum(type: type) > 0 {
|
|
|
- return false
|
|
|
- }
|
|
|
- return true
|
|
|
- }
|
|
|
-}
|
|
|
|
|
|
|
|
|
extension PurchaseManager {
|
|
@@ -960,119 +692,7 @@ extension PurchaseManager {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-extension PurchaseManager {
|
|
|
- func getDayGeneratedNum(type: VipFreeNumType) -> VipFreeNumType{
|
|
|
- if type != .videoV2 {
|
|
|
- return .aiGenerate
|
|
|
- }
|
|
|
- return .videoV2
|
|
|
- }
|
|
|
-
|
|
|
- //是否超出每天使用的次数
|
|
|
- public func isExceedsDayGeneratedNum(vipFreeNumType:VipFreeNumType) -> Bool {
|
|
|
- if isVip {
|
|
|
-// #if DEBUG
|
|
|
-// return false
|
|
|
-// #endif
|
|
|
- let dayNum = loadDayGeneratedNum(type: vipFreeNumType)
|
|
|
- if vipFreeNumType == .videoV2 {
|
|
|
- return dayNum >= vipType.dayGeneratedVideoNumber
|
|
|
- }
|
|
|
- return dayNum >= vipType.dayGeneratedNumber
|
|
|
- }
|
|
|
- return false
|
|
|
- }
|
|
|
-
|
|
|
- func saveForDayGeneratedNum(type: VipFreeNumType) {
|
|
|
- let type = getDayGeneratedNum(type: type)
|
|
|
-
|
|
|
- var dayNum = loadDayGeneratedNum(type: type)
|
|
|
- dayNum = dayNum + 1
|
|
|
-
|
|
|
- // 保存新的记录
|
|
|
- var dict: [String: [String: Any]] = [:]
|
|
|
- if var saveDict = UserDefaults.standard.dictionary(forKey: kDayGeneratedNumKey) as? [String: [String: Any]]{
|
|
|
- dict = saveDict
|
|
|
- }
|
|
|
- dict[type.rawValue] = ["date": Date().dateDayString, "times": dayNum]
|
|
|
- UserDefaults.standard.set(dict, forKey: kDayGeneratedNumKey)
|
|
|
- UserDefaults.standard.synchronize()
|
|
|
- //保存到钥匙串
|
|
|
- if let jsonString = dict.toJSONString() {
|
|
|
- dePrint("从钥匙串存入了jsonString kDayGeneratedNumKey=\(jsonString)")
|
|
|
- KeychainManager.save(jsonString, forKey: kDayGeneratedNumKey)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- func loadDayGeneratedNum(type: VipFreeNumType)-> Int {
|
|
|
- let type = getDayGeneratedNum(type: type)
|
|
|
- // 当天没记录,设置默认次数
|
|
|
- guard let dict = UserDefaults.standard.dictionary(forKey: kDayGeneratedNumKey),
|
|
|
- let typeDict = dict.safeDictionary(forKey: type.rawValue) as? [String: Any],
|
|
|
- typeDict.safeString(forKey: "date") == Date().dateDayString else {
|
|
|
- return 0
|
|
|
- }
|
|
|
-
|
|
|
- dePrint("从钥匙串 loadDayGeneratedNum=\(dict)")
|
|
|
-
|
|
|
- // 有记录,设置已经使用次数
|
|
|
- return typeDict.safeInt(forKey: "times")
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-extension PurchaseManager {
|
|
|
-
|
|
|
- //是否超出Vip期间使用的次数
|
|
|
- public func isExceedsVipGeneratedNum(vipFreeNumType:VipFreeNumType) -> Bool {
|
|
|
- if isVip {
|
|
|
-// #if DEBUG
|
|
|
-// return false
|
|
|
-// #endif
|
|
|
- let dayNum = loadVipGeneratedNum(type: vipFreeNumType)
|
|
|
- if vipFreeNumType == .videoV2 {
|
|
|
- return dayNum >= vipType.creatVideoMaxNum
|
|
|
- }
|
|
|
- return false
|
|
|
- }
|
|
|
- return false
|
|
|
- }
|
|
|
|
|
|
- func saveForVipGeneratedNum(type: VipFreeNumType) {
|
|
|
- let type = getDayGeneratedNum(type: type)
|
|
|
-
|
|
|
- var dayNum = loadVipGeneratedNum(type: type)
|
|
|
- dayNum = dayNum + 1
|
|
|
- // 保存新的记录
|
|
|
- var dict: [String: [String: Any]] = [:]
|
|
|
- if var saveDict = UserDefaults.standard.dictionary(forKey: kVipGeneratedNumKey) as? [String: [String: Any]]{
|
|
|
- dict = saveDict
|
|
|
- }
|
|
|
- dict[type.rawValue] = ["times": dayNum,"expireTime":expireTime]
|
|
|
- UserDefaults.standard.set(dict, forKey: kVipGeneratedNumKey)
|
|
|
- UserDefaults.standard.synchronize()
|
|
|
- //保存到钥匙串
|
|
|
- if let jsonString = dict.toJSONString() {
|
|
|
- dePrint("从钥匙串存入了jsonString kVipGeneratedNumKey=\(jsonString)")
|
|
|
- KeychainManager.save(jsonString, forKey: kVipGeneratedNumKey)
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- func loadVipGeneratedNum(type: VipFreeNumType)-> Int {
|
|
|
- let type = getDayGeneratedNum(type: type)
|
|
|
- // 当天没记录,设置默认次数
|
|
|
- guard let dict = UserDefaults.standard.dictionary(forKey: kVipGeneratedNumKey),
|
|
|
- let typeDict = dict.safeDictionary(forKey: type.rawValue) as? [String: Any],
|
|
|
- typeDict.safeString(forKey: "expireTime") == expireTime
|
|
|
- else {
|
|
|
- return 0
|
|
|
- }
|
|
|
- dePrint("从钥匙串 loadVipGeneratedNum=\(dict)")
|
|
|
- // 有记录,设置已经使用次数
|
|
|
- return typeDict.safeInt(forKey: "times")
|
|
|
- }
|
|
|
-
|
|
|
-}
|
|
|
/*
|
|
|
|
|
|
首先,创建SKProductsRequest对象并使用init(productIdentifiers:)初始化,传入要查询的产品标识符。
|