Files
real-e-party-iOS/YuMi/Tools/PIIAPTool/PIIAPRegulate.swift
edwinQQQ a35a711be6 chore: Initial clean commit
- Removed YuMi/Library/ (138 MB, not tracked)
- Removed YuMi/Resources/ (23 MB, not tracked)
- Removed old version assets (566 files, not tracked)
- Excluded Pods/, xcuserdata/ and other build artifacts
- Clean repository optimized for company server deployment
2025-10-09 16:19:14 +08:00

228 lines
7.6 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.

import UIKit
import StoreKit
@available(iOS 15.0, *)
typealias Transaction = StoreKit.Transaction
@available(iOS 15.0, *)
typealias RenewalInfo = StoreKit.Product.SubscriptionInfo.RenewalInfo
@available(iOS 15.0, *)
typealias RenewalState = StoreKit.Product.SubscriptionInfo.RenewalState
enum PIStoreError: Error {
//
case failedVerification
case noProduct
}
@objc public enum StoreConditionResult: Int64 { //
case start //
case pay //
case verifiedServer //
case userCancelled //
case pending //
case unowned
case noProduct //
case failedVerification //
}
@available(iOS 15.0, *)
public class PIIAPRegulate: NSObject {
public typealias KConditionBlock = (_ state :StoreConditionResult,_ param:Dictionary<String,Any>?) ->()
@objc public var ConditionBlock: KConditionBlock! //
var updateListenerTask: Task<Void, Error>? = nil //
var transactionMap :[String:Transaction] = [:]// Idmap
@objc public static let shared = PIIAPRegulate()
private override init() { // private
super.init()
self.updateListenerTask = listenForTransactions()
}
func triggerConditionBlock(_ state: StoreConditionResult, _ param: [String: Any]? = nil) {
if let ConditionBlock = ConditionBlock {
ConditionBlock(state, param)
}
}
// 退
@objc public func refunRequest(view: UIView,transactionId:UInt64) async {
do {
if let scene = await view.window?.windowScene{
_ = try await Transaction.beginRefundRequest(for:transactionId , in: scene)
}
}catch{
print("iap error")
}
}
//
@objc public func demandCommodityThing(productId:String, uuid: String) async throws {
triggerConditionBlock(.start)
do {
let list = [productId]
let storeProducts = try await Product.products(for: Set(list))
guard let product = storeProducts.first else {
// triggerConditionBlock(.noProduct)
throw PIStoreError.noProduct
}
_ = try await purchase(product, uuid)
} catch {
triggerConditionBlock(.noProduct)
throw PIStoreError.noProduct
}
}
//
private func purchase(_ product: Product, _ uuid: String) async throws -> Transaction? {
triggerConditionBlock(.pay)
guard let curUUID = UUID.init(uuidString: uuid) else{
triggerConditionBlock(.failedVerification)
return nil
}
let result = try await product.purchase(options: [.appAccountToken(curUUID)])
switch result {
case .success(let verification): //
return try await verifiedAndAccomplish(verification)
case .userCancelled: //
triggerConditionBlock(.userCancelled)
case .pending: //
triggerConditionBlock(.pending)
default:
triggerConditionBlock(.unowned)
}
return nil
}
//
func checkVerified<T>(_ result: VerificationResult<T>) throws -> T {
//Check whether the JWS passes StoreKit verification.
switch result {
case .unverified:
//StoreKit parses the JWS, but it fails verification.
triggerConditionBlock(.failedVerification)
throw PIStoreError.failedVerification
case .verified(let safe):
//The result is verified. Return the unwrapped value.
print("iap: verified success")
return safe
}
}
// &
func verifiedAndAccomplish(_ verification:VerificationResult<Transaction>) async throws -> Transaction?{
//Check whether the transaction is verified. If it isn't,
//this function rethrows the verification error.
let transaction = try checkVerified(verification)
// ~~~
let transactionId = try verification.payloadValue.id
// map
let key = String(transactionId)
transactionMap[key] = transaction
await uploadServer(for: key)
//
await transaction.finish()
print("iap: finish")
return transaction
}
/*All transactions
Latest transactions
Current entitlements
*/
func getAllBusiness(transactionId:String) async {
let transactionIntId = UInt64(transactionId)
for await result in Transaction.all {
do {
let tran = try checkVerified(result)
let resultId = try result.payloadValue.id
if transactionIntId == resultId {
await tran.finish()
break
}
} catch let error {
print("error:----\(error)")
}
}
}
//
// @objc public func verifyBusinessAccomplish(transaction:String) async {
// if(transactionMap[transaction] != nil){
// await transactionMap[transaction]!.finish()
// transactionMap.removeValue(forKey: transaction)
// print("verifyBusinessFinish end")
// }else {
// await getAllBusiness(transactionId: transaction)
// }
// }
@objc public func verifyBusinessAccomplish(transactionID: String, completionHandler: @escaping (Bool, Error?) -> Void) {
if let transaction = transactionMap[transactionID] {
Task {
await transaction.finish()
transactionMap.removeValue(forKey: transactionID)
completionHandler(true, nil) //
}
} else {
Task {
await getAllBusiness(transactionId: transactionID)
completionHandler(false, nil) //
}
}
}
@MainActor
func uploadServer(for transactionId:String) async {
let dic :Dictionary<String,String> = ["transactionId":transactionId]
triggerConditionBlock(.verifiedServer, dic)
}
//
func listenForTransactions() -> Task<Void, Error> {
return Task.detached {
for await result in Transaction.updates {
do {
let resultId = try result.payloadValue.id
if !self.transactionMap.keys.contains(String(resultId)) {
_ = try await self.verifiedAndAccomplish(result)
}
} catch {
//
print("Error: \(error)")
}
}
}
}
//广
func Promotion() async -> [SKProduct]?{
let promotion = SKProductStorePromotionController()
let prodicts = try? await promotion.promotionOrder()
return prodicts
}
//
deinit {
updateListenerTask?.cancel()
}
}