diff --git a/relax.offline.mp3.music.xcodeproj/project.pbxproj b/relax.offline.mp3.music.xcodeproj/project.pbxproj index 06c6f6d..640a340 100644 --- a/relax.offline.mp3.music.xcodeproj/project.pbxproj +++ b/relax.offline.mp3.music.xcodeproj/project.pbxproj @@ -27,6 +27,7 @@ CB2CAAD72C5A1AC500EF691D /* MP_IAPViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = CB2CAAD62C5A1AC500EF691D /* MP_IAPViewController.xib */; }; CB2CAAD82C5A1AC500EF691D /* MP_IAPViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB2CAAD52C5A1AC500EF691D /* MP_IAPViewController.swift */; }; CB4FA4D92C5CDC4A0027C949 /* relax.offline.mp3.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = CB4FA4D72C5CDC4A0027C949 /* relax.offline.mp3.xcdatamodeld */; }; + CB6EEB8E2C5DFE6100AEC414 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CB6EEB8D2C5DFE6100AEC414 /* StoreKit.framework */; }; CB7FC5422C2AA01F00292A43 /* FacebookAEM in Frameworks */ = {isa = PBXBuildFile; productRef = CB7FC5412C2AA01F00292A43 /* FacebookAEM */; }; CB7FC5442C2AA01F00292A43 /* FacebookBasics in Frameworks */ = {isa = PBXBuildFile; productRef = CB7FC5432C2AA01F00292A43 /* FacebookBasics */; }; CB7FC5462C2AA01F00292A43 /* FacebookCore in Frameworks */ = {isa = PBXBuildFile; productRef = CB7FC5452C2AA01F00292A43 /* FacebookCore */; }; @@ -281,6 +282,7 @@ CB2CAAD52C5A1AC500EF691D /* MP_IAPViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MP_IAPViewController.swift; sourceTree = ""; }; CB2CAAD62C5A1AC500EF691D /* MP_IAPViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MP_IAPViewController.xib; sourceTree = ""; }; CB4FA4D82C5CDC4A0027C949 /* MusicPlayer.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MusicPlayer.xcdatamodel; sourceTree = ""; }; + CB6EEB8D2C5DFE6100AEC414 /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; }; CB7FC5472C2AC25C00292A43 /* MPPositive_CenterListSearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPPositive_CenterListSearchView.swift; sourceTree = ""; }; CBAFC9F22C0A10500054500E /* MP_BaseViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MP_BaseViewController.swift; sourceTree = ""; }; CBAFC9F32C0A10500054500E /* MP_LunchViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MP_LunchViewController.swift; sourceTree = ""; }; @@ -520,6 +522,7 @@ CBAFCBAB2C0A10DA0054500E /* FirebaseAnalytics in Frameworks */, CB000F442C12AD2300B0FC0A /* GoogleMobileAds in Frameworks */, 46C4D5B84AF3CA6292152C70 /* Pods_relax_offline_mp3_music.framework in Frameworks */, + CB6EEB8E2C5DFE6100AEC414 /* StoreKit.framework in Frameworks */, CBAFCBAF2C0A10DA0054500E /* FirebaseRemoteConfig in Frameworks */, CB7FC5462C2AA01F00292A43 /* FacebookCore in Frameworks */, ); @@ -531,6 +534,7 @@ 2286F0649FAEEC8EC5583D68 /* Frameworks */ = { isa = PBXGroup; children = ( + CB6EEB8D2C5DFE6100AEC414 /* StoreKit.framework */, CBD4570C2C2EC38400CE766D /* AppTrackingTransparency.framework */, 8DB1E9160CFEC3A0E35F11BF /* Pods_relax_offline_mp3_music.framework */, ); @@ -1744,7 +1748,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1.1.5.1; + CURRENT_PROJECT_VERSION = 1.1.5.2; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = RAQJ4FNZUH; @@ -1790,7 +1794,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1.1.5.1; + CURRENT_PROJECT_VERSION = 1.1.5.2; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = RAQJ4FNZUH; diff --git a/relax.offline.mp3.music/MP/Common/Tool(工具封装)/MP_IAPManager.swift b/relax.offline.mp3.music/MP/Common/Tool(工具封装)/MP_IAPManager.swift index 59e76fe..abeb877 100644 --- a/relax.offline.mp3.music/MP/Common/Tool(工具封装)/MP_IAPManager.swift +++ b/relax.offline.mp3.music/MP/Common/Tool(工具封装)/MP_IAPManager.swift @@ -197,20 +197,30 @@ extension MP_IAPManager: SKProductsRequestDelegate, SKPaymentTransactionObserver private func complete(transaction: SKPaymentTransaction) { MP_HUD.success("Successfully purchased", delay: 1.0, completion: nil) print("Transaction completed successfully.") - // 存储购买信息 - self.storePurchase(productId: transaction.payment.productIdentifier) - SKPaymentQueue.default().finishTransaction(transaction) - //更新广告开关状态 - reloadOpenStatus() + validateReceipt { [weak self] status in + guard let self = self else {return} + if status { + // 存储购买信息 + self.storePurchase(productId: transaction.payment.productIdentifier) + SKPaymentQueue.default().finishTransaction(transaction) + //更新广告开关状态 + reloadOpenStatus() + } + } } //重启交易 private func restore(transaction: SKPaymentTransaction) { print("Transaction restored.") - // 存储购买信息 - self.storePurchase(productId: transaction.payment.productIdentifier) - SKPaymentQueue.default().finishTransaction(transaction) - //更新广告开关状态 - reloadOpenStatus() + validateReceipt { [weak self] status in + guard let self = self else {return} + if status { + // 存储购买信息 + self.storePurchase(productId: transaction.payment.productIdentifier) + SKPaymentQueue.default().finishTransaction(transaction) + //更新广告开关状态 + reloadOpenStatus() + } + } } ///交易失败 private func fail(transaction: SKPaymentTransaction) { @@ -276,8 +286,66 @@ extension MP_IAPManager: SKProductsRequestDelegate, SKPaymentTransactionObserver do { if let jsonResponse = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any] { print("Receipt validation response: \(jsonResponse)") - // 根据苹果的验证响应处理购买信息 - completion(true) + if let status = jsonResponse["status"] as? Int { + if status == 0 { + completion(true) + } else if status == 21007 { + self.validateReceiptInSandbox(receiptString: receiptString, completion: completion) + } else { + print("Receipt validation failed with status: \(status)") + completion(false) + } + } else { + completion(false) + } + } else { + completion(false) + } + } catch { + completion(false) + } + } + task.resume() + } catch { + completion(false) + } + } + func validateReceiptInSandbox(receiptString: String, completion: @escaping (Bool) -> Void) { + let requestDictionary = ["receipt-data": receiptString, + "password": "d29627e4f78b4b50a0ce5166acd8aa9f"] + guard JSONSerialization.isValidJSONObject(requestDictionary) else { + completion(false) + return + } + + do { + let requestData = try JSONSerialization.data(withJSONObject: requestDictionary) + let validationURLString = "https://sandbox.itunes.apple.com/verifyReceipt" + guard let validationURL = URL(string: validationURLString) else { + completion(false) + return + } + + var request = URLRequest(url: validationURL) + request.httpMethod = "POST" + request.cachePolicy = .reloadIgnoringCacheData + request.httpBody = requestData + + let session = URLSession.shared + let task = session.dataTask(with: request) { data, response, error in + guard error == nil, let data = data else { + completion(false) + return + } + do { + if let jsonResponse = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any] { + print("Sandbox receipt validation response: \(jsonResponse)") + + if let status = jsonResponse["status"] as? Int, status == 0 { + completion(true) + } else { + completion(false) + } } else { completion(false) } diff --git a/relax.offline.mp3.music/MP/MPPositive/Views/Home/MPPositive_ArtistShowHeaderView.swift b/relax.offline.mp3.music/MP/MPPositive/Views/Home/MPPositive_ArtistShowHeaderView.swift index d3f6e77..0c492ed 100644 --- a/relax.offline.mp3.music/MP/MPPositive/Views/Home/MPPositive_ArtistShowHeaderView.swift +++ b/relax.offline.mp3.music/MP/MPPositive/Views/Home/MPPositive_ArtistShowHeaderView.swift @@ -106,14 +106,16 @@ class MPPositive_ArtistShowHeaderView: UIView { MPPositive_LoadCoreModel.shared.reloadCollectionArtistViewModels(nil) } }else{ - self.collectionBtn.isSelected = true - let item = MPPositive_CollectionArtistModel.create() - item.title = artist.header.title - item.coverImage = URL(string: artist.header.thumbnails?.last ?? "") - item.subtitle = artist.header.subscriptionedText - item.artistId = self.artistid - MPPositive_CollectionArtistModel.save() - MPPositive_LoadCoreModel.shared.reloadCollectionArtistViewModels(nil) + if let artist = self.artist, let header = artist.header { + self.collectionBtn.isSelected = true + let item = MPPositive_CollectionArtistModel.create() + item.title = header.title + item.coverImage = URL(string: header.thumbnails?.last ?? "") + item.subtitle = header.subscriptionedText + item.artistId = self.artistid + MPPositive_CollectionArtistModel.save() + MPPositive_LoadCoreModel.shared.reloadCollectionArtistViewModels(nil) + } } }