Music_Player3/relax.offline.mp3.music/MP/Common/Tool(工具封装)/MP_IAPManager.swift
2024-12-13 18:47:39 +08:00

533 lines
22 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// MP_IAPManager.swift
// relax.offline.mp3.music
//
// Created by Mr.Zhou on 2024/7/29.
//
import UIKit
import SwiftyStoreKit
import StoreKit
///
class MP_IAPManager: NSObject {
///
static let shared = MP_IAPManager()
///ID
var productIdentifiers:[String] = ["1weekvip","1yearvip","lifetimevip"]
///
private var transactions:[PaymentTransaction] = []
///(ID)
private var productsRequest:SKProductsRequest?
///
private var availableProducts:Set<SKProduct> = []
///
private var showHUD:Bool = false
///VIP
var isVIP:Bool {
//VIP
guard let lastDate = UserDefaults.standard.object(forKey: "PurchaseVIPDate") as? Date, lastDate.timeIntervalSince(Date()) > 0 else { return false }
return true
}
override init() {
super.init()
}
deinit {
}
///
func observeVIPStoreKit() {
//
transactions.removeAll()
//
SwiftyStoreKit.completeTransactions { [weak self] purchases in
guard let self = self else { return }
var productId = ""
//
purchases.forEach { item in
switch item.transaction.transactionState {
case .purchased, .restored:///
//
if item.needsFinishTransaction {
SwiftyStoreKit.finishTransaction(item.transaction)
}
//ID
productId = item.productId
//
self.transactions.append(item.transaction)
case .failed://
//
if item.needsFinishTransaction {
SwiftyStoreKit.finishTransaction(item.transaction)
}
default:
break
}
}
//
if !productId.isEmpty {
//
print("当前用户有VIP记录,进行验证")
self.showHUD = false
self.purchaseVerificationProductVIP(with: productId)
}else {
//
print("当前用户非VIP")
}
}
}
///
func getVIPAllProducts() {
let productIds:Set<String> = Set(productIdentifiers)
SwiftyStoreKit.retrieveProductsInfo(productIds) { [weak self] results in
guard let self = self else { return }
//
if !results.retrievedProducts.isEmpty {
self.availableProducts = results.retrievedProducts
}
}
}
// ///
// func requestProducts() {
// guard MP_NetWorkManager.shared.netWorkStatu == .reachable else {
// //
// print("")
// return
// }
// //
// availableProducts = []
// //IDSet
// let setStrings:Set<String> = Set(productIdentifiers)
// //
// productsRequest = SKProductsRequest(productIdentifiers: setStrings)
// //
// productsRequest?.delegate = self
// //
// productsRequest?.start()
// }
///
func purchaseProduct(with productId:String) {
showHUD = true
MP_HUD.loading()
//
if SwiftyStoreKit.canMakePayments {
//
MP_AnalyticsManager.shared.VIP_continue_clickAction(productId)
self.pruchaseProductVIP(with: productId)
}else {
//
MP_HUD.error("Sorry. Subscription purchase failed", delay: 1.0, completion: nil)
MP_AnalyticsManager.shared.VIP_buy_failureAction(productId, error: "The current project status is not supported")
}
}
///
func restorePurchases() {
MP_HUD.loading()
showHUD = true
self.transactions.removeAll()
SwiftyStoreKit.restorePurchases(atomically: false) { [weak self] results in
guard let self = self else { return }
if !results.restoredPurchases.isEmpty, let productId = results.restoredPurchases.first?.productId {
//restore
self.transactions.append(contentsOf: results.restoredPurchases.map({$0.transaction}))
self.purchaseVerificationProductVIP(with: productId)
return
}
if !results.restoreFailedPurchases.isEmpty {
MP_HUD.error("Restore purchase failed", delay: 1.0, completion: nil)
return
}
MP_HUD.error("No purchase can be restored", delay: 1.0, completion: nil)
}
}
// ///广
// func startLunchStatus() {
// //
// let receiptURL = Bundle.main.appStoreReceiptURL
// guard let receipt = try? Data(contentsOf: receiptURL!) else {
// //
// print("广")
// //AppStore,使广
// MP_ADSimpleManager.shared.setOpenAdStatus(true)
// return
// }
// //
// print("")
// reloadOpenStatus()
// }
//
// ///广
// func reloadOpenStatus() {
// //广
// if isProductPurchased(productId: productIdentifiers[0]) || isProductPurchased(productId: productIdentifiers[1]) || isProductPurchased(productId: productIdentifiers[2]){
// //广
// MP_ADSimpleManager.shared.setOpenAdStatus(false)
// }else {
// MP_ADSimpleManager.shared.setOpenAdStatus(true)
// }
// }
}
//MARK: -
extension MP_IAPManager {
///
private func pruchaseProductVIP(with productId:String) {
//
self.transactions.removeAll()
SwiftyStoreKit.purchaseProduct(productId, quantity: 1, atomically: false) { [weak self] (result) in
guard let self = self else { return }
switch result {
case .success(let product)://
print("VIP Purchase Successfully.")
//
self.transactions.append(product.transaction)
//
self.purchaseVerificationProductVIP(with: product.productId)
case .error(let error)://
MP_HUD.error("Sorry. Subscription purchase failed", delay: 1.0, completion: nil)
MP_AnalyticsManager.shared.VIP_buy_failureAction(productId, error: "The current project status is not supported")
print("VIP: \(error.localizedDescription)")
}
}
}
///
private func purchaseVerificationProductVIP(with productId:String) {
//
let receiptValidator = AppleReceiptValidator(service: checkIsSandbox() ? .sandbox:.production, sharedSecret: "d29627e4f78b4b50a0ce5166acd8aa9f")
SwiftyStoreKit.verifyReceipt(using: receiptValidator, forceRefresh: true) { [weak self] result in
guard let self = self else { return }
switch result {
case .success(let receipt)://
print("VIP Receipt: \(receipt)")
//
if productId == "lifetimevip" {
//
let purchaseResult = SwiftyStoreKit.verifyPurchase(productId: productId, inReceipt: receipt)
switch purchaseResult {
case .purchased(let item):
print("VIP LifeTime verifyPurchase successed current item: \(item)")
//
let lifeDate = Date().addingTimeInterval(60 * 60 * 24 * 365 * 100)
//VIP
UserDefaults.standard.set(lifeDate, forKey: "PurchaseVIPDate")
UserDefaults.standard.synchronize()
//
self.showSuccessfullyHUD()
self.finishedAllTransactionsVIP()
case .notPurchased://
self.finishedAllTransactionsVIP()
self.showFailedHUD()
}
}else {
//
let purchaseResult = SwiftyStoreKit.verifySubscription(ofType: .autoRenewable, productId: productId, inReceipt: receipt)
switch purchaseResult {
case .purchased(let expiryDate, let items):
print("VIP \(productId) valid until \(expiryDate)")
UserDefaults.standard.set(expiryDate, forKey: "PurchaseVIPDate")
UserDefaults.standard.synchronize()
//
self.showSuccessfullyHUD()
self.finishedAllTransactionsVIP()
case .expired(let expiryDate, let items)://
self.finishedAllTransactionsVIP()
self.showFailedHUD()
case .notPurchased://
self.finishedAllTransactionsVIP()
self.showFailedHUD()
}
}
case .error(let error)://
self.finishedAllTransactionsVIP()
self.showFailedHUD()
}
}
}
//
private func showSuccessfullyHUD() {
if showHUD == true {
MP_HUD.success("Successfully purchased", delay: 1.5, completion: nil)
}
}
//
private func showFailedHUD() {
if showHUD == true {
MP_HUD.error("Failed purchased", delay: 1.5, completion: nil)
}
}
//
private func finishedAllTransactionsVIP() {
self.transactions.forEach { item in
SwiftyStoreKit.finishTransaction(item)
}
self.transactions.removeAll()
}
private func checkIsSandbox() -> Bool {
var isSandbox = false
#if DEBUG
isSandbox = true
#endif
return isSandbox
}
}
//MARK: - SKProductsRequestDelegate
//extension MP_IAPManager: SKProductsRequestDelegate, SKPaymentTransactionObserver {
// ///
// func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
// //
// availableProducts = response.products
// //
// guard availableProducts.isEmpty == false else {
// //
// print("")
// return
// }
// //,
// for (index, item) in availableProducts.enumerated() {
// print("\(index)--\(item)")
// }
// }
// ///
// func request(_ request: SKRequest, didFailWithError error: any Error) {
// print("Failed to fetch products: \(error.localizedDescription)")
// }
// ///
// func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
// //
// transactions.forEach { item in
// //
// switch item.transactionState {
// case .purchasing://
// break
// case .purchased://
// complete(transaction: item)
// case .failed://
// fail(transaction: item)
// case .restored://
// restore(transaction: item)
// case .deferred://
// break
// @unknown default:
// break
// }
// }
// }
// ///
// func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
// print("")
// MP_HUD.hideNow()
// //
// if queue.transactions.isEmpty {
// //ID
// print("/")
// productIdentifiers.forEach { item in
// cleanPurchase(productId: item)
// }
// MP_ADSimpleManager.shared.setOpenAdStatus(true)
// }
// }
// func paymentQueue(_ queue: SKPaymentQueue, restoreCompletedTransactionsFailedWithError error: any Error) {
// print("")
// MP_HUD.error("The current transaction failed".localizableString(), delay: 1.0, completion: nil)
// //VIP
// productIdentifiers.forEach { item in
// cleanPurchase(productId: item)
// }
// MP_ADSimpleManager.shared.setOpenAdStatus(true)
// }
// //
// private func storePurchase(productId: String) {
// if isProductPurchased(productId: productId) == false {
// var purchasedProducts = UserDefaults.standard.array(forKey: "PurchasedProducts") as? [String] ?? []
// purchasedProducts.append(productId)
// UserDefaults.standard.set(purchasedProducts, forKey: "PurchasedProducts")
// }
// }
// //VIP
// private func isProductPurchased(productId: String) -> Bool {
// let purchasedProducts = UserDefaults.standard.array(forKey: "PurchasedProducts") as? [String] ?? []
// return purchasedProducts.contains(productId)
// }
// //广ID
// private func cleanPurchase(productId: String) {
// if isProductPurchased(productId: productId) {
// var purchasedProducts = UserDefaults.standard.array(forKey: "PurchasedProducts") as? [String] ?? []
// purchasedProducts.removeAll(where: {$0 == productId})
// UserDefaults.standard.set(purchasedProducts, forKey: "PurchasedProducts")
// }
// }
// ///
// private func complete(transaction: SKPaymentTransaction) {
// MP_HUD.success("Successfully purchased", delay: 1.0, completion: nil)
// print("Transaction completed successfully.")
// MP_AnalyticsManager.shared.VIP_buy_successAction(transaction.payment.productIdentifier)
// 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.")
// 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) {
// //
// if let error = transaction.error as NSError? {
// MP_AnalyticsManager.shared.VIP_buy_failureAction(transaction.payment.productIdentifier, error: error.localizedDescription)
// if error.code != SKError.paymentCancelled.rawValue {
// MP_HUD.error("The current transaction failed".localizableString(), delay: 1.0, completion: nil)
// } else {
// MP_HUD.onlytext("The current transaction has been canceled".localizableString(), delay: 1.0, completion: nil)
// }
// }
// }
// //
// func fetchReceipt() -> String? {
// guard let receiptURL = Bundle.main.appStoreReceiptURL else { return nil }
// guard let receiptData = try? Data(contentsOf: receiptURL) else { return nil }
// return receiptData.base64EncodedString(options: [])
// }
// //
// func validateReceipt(completion: @escaping (Bool) -> Void) {
// guard MP_NetWorkManager.shared.netWorkStatu == .reachable else {
// completion(false)
// return
// }
// //
// guard let receiptString = fetchReceipt() else {
// completion(false)
// return
// }
// //
// let requestDictionary = ["receipt-data": receiptString,
// "password": "d29627e4f78b4b50a0ce5166acd8aa9f" ]
// guard JSONSerialization.isValidJSONObject(requestDictionary) else {
// completion(false)
// return
// }
//
// do {
// let requestData = try JSONSerialization.data(withJSONObject: requestDictionary)
// #if DEBUG
// let validationURLString = "https://sandbox.itunes.apple.com/verifyReceipt"
// #else
// let validationURLString = "https://buy.itunes.apple.com/verifyReceipt"
// #endif
// 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] {
// 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] {
// if let status = jsonResponse["status"] as? Int, status == 0 {
// completion(true)
// } else {
// completion(false)
// }
// } else {
// completion(false)
// }
// } catch {
// completion(false)
// }
// }
// task.resume()
// } catch {
// completion(false)
// }
// }
//}