//
import StoreKit
@objcMembers public class FastApplePay: NSObject, SKPaymentTransactionObserver, SKProductsRequestDelegate {
public static let shared = FastApplePay()
public typealias FastApplePayBlock = (FastApplePayState,String?,String?)->Void
public enum FastApplePayState :String{
case start = "商品开始购买"
case success = "商品购买成功"
case failed = "商品购买失败"
case productContent = "商品信息列表获取失败"
case payFailed = "商品支付失败"
case cancel = "用户取消交易"
case error = "商品交易失败"
case catchError = "后台验证接口崩溃"
case end = "商品交易完成"
case transactionEnd = "扣款成功"
case guaJiePayURLError = "挂接中心回调地址请求失败"
}
/**
回调
*/
var callBack:FastApplePayBlock?
/**
商品ID
*/
var gamebuyGoodId:String?
/**
服务器回调地址
*/
var gamePayCallbackUrl:String?
/**
订单号
*/
var gameAppPayIdentifier:String?
/**
商品
*/
var product:SKProduct?
/**
服务器验证字典参数
*/
var userPayParame:Dictionary<String,Any>?
var productList:Array<Dictionary<String,Any>> = [] // 商品列表
var isGetProductList = false // 是否是获取商品列表信息
var productListCallBack:FastStrBlock? // 商品列表回调
/**
购买商品
*/
public func buyProduct(param:Dictionary<String,Any>,blcok:@escaping FastApplePayBlock) {
self.callBack = blcok
self.isGetProductList = false
print(param)
if let gamebuyGoodId = param.string("productId"),let gamePayCallbackUrl = param.string("notify_url") {
self.gamePayCallbackUrl = gamePayCallbackUrl
self.gamebuyGoodId = gamebuyGoodId
userPayParame = param.value("data") as? Dictionary<String,Any>
if SKPaymentQueue.canMakePayments(){
let productsRequest = SKProductsRequest(productIdentifiers: [gamebuyGoodId])
productsRequest.start()
productsRequest.delegate = self
callBack(.start, orderid: nil, desc:nil)
}else{
FastLog.error("无法支付-----")
print("无法支付-----")
}
}else{
FastLog.error("参数错误")
print("参数错误")
}
}
/**
支付状态回调
*/
func callBack(_ state:FastApplePayState,orderid:String?,desc:String?){
self.callBack?(state,orderid,desc)
}
/**
成功处理
*/
func completeTransaction(transaction: SKPaymentTransaction){
if let gamePayCallbackUrl = self.gamePayCallbackUrl,let gameAppPayIdentifier = transaction.transactionIdentifier,let url = Bundle.main.appStoreReceiptURL{
if let data = try? Data(contentsOf: url) {
// 凭证
let receipt = data.base64EncodedString(options: .endLineWithLineFeed)
userPayParame?["transaction_id"] = gameAppPayIdentifier
FastNetWorking.shared.post(inUrl: gamePayCallbackUrl, parameDic: userPayParame, receipt) {[weak self] suc in
print("status:\(suc.dic?.value(Int.self,"status"))")
//判断服务器凭证验证状态
if let status = suc.dic?.value(Int.self,"status"){
if status != 1{
self?.callBack(.payFailed, orderid: gameAppPayIdentifier, desc: suc.dic?.string("msg"))
return
}
SKPaymentQueue.default().finishTransaction(transaction)
self?.callBack(.end, orderid: gameAppPayIdentifier, desc: suc)
}else{
self?.callBack(.payFailed, orderid: gameAppPayIdentifier, desc: suc.dic?.string("msg"))
}
} fai: {[weak self] str in
self?.callBack(.payFailed, orderid: gameAppPayIdentifier, desc: str)
}
}else{
callBack(.payFailed, orderid: nil, desc: nil)
}
}else{
callBack(.payFailed, orderid: nil, desc: nil)
}
}
/**
失败处理
*/
func failedTransaction(transaction: SKPaymentTransaction){
if let err:SKError = transaction.error as? SKError {
if err.code == SKError.Code.paymentCancelled{ //交易取消
callBack(.cancel, orderid: nil, desc: nil)
}else{//购买失败
callBack(.failed, orderid: nil, desc: nil)
}
}
SKPaymentQueue.default().finishTransaction(transaction)
}
// MARK: 商品信息回调
public func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
print("商品信息回调 response.products = \(response.products)")
FastLog.debug(response.products)
if response.products.count > 1{
if self.isGetProductList{
self.callBackProductList(list: response.products)
}
}else{
if let product = response.products.first {
print("Product title: \(product.localizedTitle)")
print("Product description: \(product.localizedDescription)")
print("Product price: \(product.price)")
print("Product productIdentifier: \(product.productIdentifier)")
print("Product priceLocale: \(product.priceLocale)")
print("地区编码: \(product.priceLocale.identifier)")
print("货币代码: \(String(describing: product.priceLocale.currencyCode))")
print("货币符号: \(String(describing: product.priceLocale.currencySymbol))")
if #available(iOS 16, *) {
FastLog.log("地区语言: \(product.priceLocale.language)")
} else {
// Fallback on earlier versions
}
self.product = product
// 发起请求
let payment = SKPayment(product: self.product!)
SKPaymentQueue.default().add(payment)
SKPaymentQueue.default().add(self)
}else{
print("商品信息列表获取失败 response.products = \(response.products)")
//商品信息列表获取失败
callBack(.productContent, orderid: nil, desc: nil)
}
}
}
// MARK: 失败执行的方法
public func request(_ request: SKRequest, didFailWithError error: Error) {
callBack(.error, orderid: nil, desc: nil)
print("货币符号error: \(error)")
}
// MARK: 购买完成
public func requestDidFinish(_ request: SKRequest) {
callBack(.success,orderid: gamebuyGoodId,desc: nil)
}
// MARK: - SKPaymentTransactionObserver
public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction in transactions {
FastLog.debug(transaction.transactionState)
switch transaction.transactionState {
case .purchased:
FastLog.debug("purchased")
// 交易完成
completeTransaction(transaction: transaction)
callBack(.transactionEnd, orderid: nil, desc: nil)
break
case .failed:
FastLog.debug("failed")
// 处理购买失败的逻辑
failedTransaction(transaction: transaction)
break
case .restored:
FastLog.debug("restored")
// 处理购买或恢复购买的逻辑
queue.finishTransaction(transaction)
default:
break
}
}
}
/// 查询苹果是否有需要补单的订单
/// - Returns: 结果
public func selectPayDropOrder() -> Bool{
if SKPaymentQueue.default().transactions.count > 0{
return true
}else{
return false
}
}
/// 返回掉单支付所有订单对象
/// - Returns: 结果
public func allTransaction() -> Array<SKPaymentTransaction>{
return SKPaymentQueue.default().transactions
}
public func getLocalInfo(productList:String,callBack:@escaping FastStrBlock){
self.productListCallBack = callBack
self.isGetProductList = true
if let dic = productList.dic{
if let arr = dic.value(Array<Dictionary<String,Any>>.self, "product_list"){
var productIds:Set<String> = []
for product in arr{
if let data:Dictionary<String,Any> = product as? Dictionary<String, Any>{
self.productList.append(data)
if let product_id = data.string("product_id"){
productIds.insert(product_id)
}
}
}
if productIds.count > 0{
let productsRequest = SKProductsRequest(productIdentifiers: productIds)
productsRequest.delegate = self
productsRequest.start()
print("productId start .. ")
}else{
print("productIds 为空 直接返回JSON数据\(productList)")
callBack(productList)
}
}else{
print("productList JSON解析错误 直接返回JSON数据\(productList)")
callBack(productList)
}
}else{
FastLog.debug("productList JSON解析错误 直接返回JSON数据\(productList)")
callBack(productList)
}
}
func callBackProductList(list:Array<SKProduct>){
var newProductList:Array<Dictionary<String,Any>> = []
for product in list{
print("新商品列表 product = \(product)")
for data in self.productList{
print("新商品列表 data = \(data)")
if product.productIdentifier == data.string("product_id"){
var newData = data
newData["local"] = ["price":product.price,
"currencyCode":product.priceLocale.currencyCode ?? "",
"title":product.localizedTitle,
]
newProductList.append(newData)
}
}
}
self.productListCallBack?(["product_list":newProductList].json)
}
}