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
This commit is contained in:
227
YuMi/Tools/PIIAPTool/PIIAPRegulate.swift
Normal file
227
YuMi/Tools/PIIAPTool/PIIAPRegulate.swift
Normal file
@@ -0,0 +1,227 @@
|
||||
|
||||
|
||||
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] = [:]// 用于完成Id的缓存map
|
||||
|
||||
@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()
|
||||
}
|
||||
|
||||
|
||||
}
|
Reference in New Issue
Block a user