Files
yingmeng-ios-switf/yinmeng-ios/Modules/Pay/UserPayViewManager.swift
2024-03-28 19:56:25 +08:00

258 lines
8.2 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.

//
// UserPayViewManager.swift
// yinmeng-ios
//
// Created by yinmeng on 2024/3/1.
//
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 StoreError: 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 UserPayViewManager: 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
var _name: String = "" //
@objc public static let shared = {
let instance = UserPayViewManager()
return instance
}()
private override init() { // private
_transactionMap = [:] //
super.init()
Task {
_updateListenerTask = _listenForTransactions()
}
}
//
@objc public func _demandCommodityThing(productId:String, uuid: String) async throws {
if(_ConditionBlock != nil ){
_ConditionBlock(StoreConditionResult.Start,nil)
}
do {
let list:[String] = [productId]
let storeProducts = try await Product.products(for: Set.init(list))
if storeProducts.count > 0 {
try await _purchase(storeProducts[0],uuid)
}else {
print("iap: no found product")
if(_ConditionBlock != nil ){
_ConditionBlock(StoreConditionResult.NoProduct,nil)
}
throw StoreError.NoProduct //
}
} catch {
print("Failed product request from the App Store server: \(error)")
if(_ConditionBlock != nil ){
_ConditionBlock(StoreConditionResult.NoProduct,nil)
}
throw StoreError.NoProduct //
}
}
//
private func _purchase(_ product: Product, _ uuid: String) async throws -> Transaction? {
if(_ConditionBlock != nil ){
_ConditionBlock(StoreConditionResult.Pay,nil)
}
guard let curUUID = UUID.init(uuidString: uuid) else{
if(_ConditionBlock != nil ){
_ConditionBlock(StoreConditionResult.FailedVerification,nil)
}
return nil
}
let getUUID = Product.PurchaseOption.appAccountToken(curUUID)
let result = try await product.purchase(options: [getUUID])
switch result {
case .success(let verification): //
let transaction = try await _verifiedAndAccomplish(verification)
return transaction
case .userCancelled: //
if(_ConditionBlock != nil ){
_ConditionBlock(StoreConditionResult.UserCancelled,nil)
}
return nil
case .pending: //
if(_ConditionBlock != nil ){
_ConditionBlock(StoreConditionResult.Pending,nil)
}
return nil
default:
if(_ConditionBlock != nil ){
_ConditionBlock(StoreConditionResult.Unowned,nil)
}
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.
if(_ConditionBlock != nil ){
_ConditionBlock(StoreConditionResult.FailedVerification,nil)
}
throw StoreError.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: transactionId)
//
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)")
}
}
//Transaction.latest(for: "pid")
}
//
@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)
}
}
@MainActor
func _uploadServer(for transactionId:UInt64) async {
let dic :Dictionary<String,Any> = ["transactionId":transactionId]
if(_ConditionBlock != nil ){
_ConditionBlock(StoreConditionResult.VerifiedServer,dic)
}
}
// 退
@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")
}
}
//
func _listenForTransactions() -> Task<Void, Error> {
return Task.detached {
//Iterate through any transactions that don't come from a direct call to `purchase()`.
// update unfinished?
for await result in Transaction.updates { //
do {
print("iap: updates")
print("result:\(result)")
try await self._verifiedAndAccomplish(result)
} catch {
//StoreKit has a transaction that fails verification. Don't deliver content to the user.
print("Transaction failed verification")
}
}
}
}
//
deinit {
_updateListenerTask?.cancel()
}
}