258 lines
8.2 KiB
Swift
258 lines
8.2 KiB
Swift
//
|
||
// 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] // 用于完成Id的缓存map
|
||
|
||
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()
|
||
}
|
||
|
||
|
||
}
|