Music_Player3/relax.offline.mp3.music/MP/Common/Tool(工具封装)/MP_IAPManager.swift
2024-08-03 14:57:17 +08:00

362 lines
14 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 StoreKit
///
class MP_IAPManager: NSObject {
///
static let shared = MP_IAPManager()
///ID
private var productIdentifiers:[String] = ["1weekvip","1yearvip","lifetimevip"]
///(ID)
private var productsRequest:SKProductsRequest?
///
private var availableProducts:[SKProduct] = []
override init() {
super.init()
SKPaymentQueue.default().add(self)
}
deinit {
SKPaymentQueue.default().remove(self)
}
///
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(_ index:Int) {
//
guard availableProducts.indices.contains(index) else {
//广
MP_HUD.onlytext("Try again!", delay: 1.0, completion: nil)
requestProducts()
return
}
let id = productIdentifiers[index]
if let product = availableProducts.first(where: {$0.productIdentifier == id}) {
//
let payment = SKPayment(product: product)
//线
SKPaymentQueue.default().add(payment)
MP_HUD.loading()
}
}
///
func restorePurchases() {
MP_HUD.loading()
//
SKPaymentQueue.default().restoreCompletedTransactions()
}
///
func systemRestorePurchases() {
let receiptURL = Bundle.main.appStoreReceiptURL
guard let receipt = try? Data(contentsOf: receiptURL!) else {
//
print("没有收据,广告默认开")
//AppStore,使广
MP_AdMobManager.shared.setOpenAdStatus(true)
return
}
//
SKPaymentQueue.default().restoreCompletedTransactions()
}
///广
func startLunchStatus() {
//
let receiptURL = Bundle.main.appStoreReceiptURL
guard let receipt = try? Data(contentsOf: receiptURL!) else {
//
print("没有收据,广告默认开")
//AppStore,使广
MP_AdMobManager.shared.setOpenAdStatus(true)
return
}
//
print("有收据,开始检索本地信息")
reloadOpenStatus()
}
///广
func reloadOpenStatus() {
//广
if isProductPurchased(productId: productIdentifiers[0]) || isProductPurchased(productId: productIdentifiers[1]) || isProductPurchased(productId: productIdentifiers[2]){
//广
MP_AdMobManager.shared.setOpenAdStatus(false)
}else {
MP_AdMobManager.shared.setOpenAdStatus(true)
}
}
}
//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_AdMobManager.shared.setOpenAdStatus(true)
}
}
func paymentQueue(_ queue: SKPaymentQueue, restoreCompletedTransactionsFailedWithError error: any Error) {
print("重启交易失败")
MP_HUD.error("The current transaction failed, \(error.localizedDescription)", delay: 1.0, completion: nil)
//VIP
productIdentifiers.forEach { item in
cleanPurchase(productId: item)
}
MP_AdMobManager.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.")
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? {
if error.code != SKError.paymentCancelled.rawValue {
MP_HUD.error("The current transaction failed, \(error.localizedDescription)", delay: 1.0, completion: nil)
} else {
MP_HUD.onlytext("The current transaction has been canceled", 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] {
print("Receipt validation response: \(jsonResponse)")
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)
}
} catch {
completion(false)
}
}
task.resume()
} catch {
completion(false)
}
}
}