293 lines
11 KiB
Swift
293 lines
11 KiB
Swift
//
|
|
// NRIapManager.swift
|
|
// ReaderHive
|
|
//
|
|
// Created by 澜声世纪 on 2025/12/10.
|
|
//
|
|
|
|
import UIKit
|
|
internal import StoreKit
|
|
import JXIAPManager
|
|
|
|
class NRIapManager {
|
|
typealias CompletionHandler = ((_ finish: Bool) -> Void)
|
|
///内购模版前缀
|
|
static let IAPPrefix = "readerhive"
|
|
|
|
|
|
static let manager = NRIapManager()
|
|
|
|
///成功回调
|
|
private var completionHandler: CompletionHandler?
|
|
|
|
private var shortPlayId: String?
|
|
private var videoId: String?
|
|
|
|
private lazy var iapManager: JXIAPManager = {
|
|
let manager = JXIAPManager.manager
|
|
manager.delegate = self
|
|
return manager
|
|
}()
|
|
|
|
private var orderCode: String?
|
|
private var payId: String?
|
|
|
|
///预加载数据
|
|
private var payRequest: NRPayDataRequest?
|
|
var payDateModel: NRPayDateModel?
|
|
|
|
|
|
///恢复购买使用
|
|
///等待恢复的数据
|
|
private var waitRestoreModel: NRWaitRestoreModel? = UserDefaults.nr_object(forKey: kNRWaitRestoreIAPDefaultsKey, as: NRWaitRestoreModel.self)
|
|
|
|
|
|
///开始内购
|
|
func start(model: NRPayItem, shortPlayId: String? = nil, videoId: String? = nil, hudShowView: UIView? = nil, handler: CompletionHandler? = nil) {
|
|
|
|
if let _ = self.waitRestoreModel {
|
|
NRToast.show(text: "pay_error_2".localized)
|
|
handler?(false)
|
|
return
|
|
}
|
|
|
|
guard let payId = model.id else {
|
|
handler?(false)
|
|
return
|
|
}
|
|
self.shortPlayId = shortPlayId
|
|
self.videoId = videoId
|
|
self.completionHandler = handler
|
|
self.waitRestoreModel = NRWaitRestoreModel()
|
|
self.waitRestoreModel?.buyType = model.buy_type
|
|
let productId = getProductId(templateId: model.ios_template_id) ?? ""
|
|
var isDiscount = false
|
|
var identifierDiscount: String? = nil
|
|
if model.discount_type == 1, let _ = model.introductionaryOffer {
|
|
isDiscount = true
|
|
} else if model.discount_type == 2, let discount = model.promotionalOffers?.first {
|
|
isDiscount = true
|
|
identifierDiscount = discount.identifier
|
|
}
|
|
|
|
NRHud.show(containerView: hudShowView)
|
|
|
|
NRStoreAPI.requestCreateOrder(payId: payId, shortPlayId: shortPlayId ?? "0", videoId: videoId ?? "0", isDiscount: isDiscount, identifierDiscount: identifierDiscount) { orderModel in
|
|
guard let orderModel = orderModel else {
|
|
NRHud.dismiss()
|
|
self.waitRestoreModel = nil
|
|
self.completionHandler?(false)
|
|
self.clean()
|
|
return
|
|
}
|
|
self.orderCode = orderModel.order_code
|
|
self.payId = payId
|
|
self.waitRestoreModel?.payId = payId
|
|
self.waitRestoreModel?.orderCode = orderModel.order_code
|
|
|
|
var discount: SKPaymentDiscount? = nil
|
|
|
|
if let identifierDiscount = identifierDiscount,
|
|
let signData = orderModel.discount?.sign_data,
|
|
let keyIdentifier = signData.keyIdentifier,
|
|
let nonce = UUID(uuidString: signData.nonce ?? ""),
|
|
let signature = signData.signature,
|
|
let timestamp = signData.timestamp
|
|
{
|
|
discount = SKPaymentDiscount(identifier: identifierDiscount,
|
|
keyIdentifier: keyIdentifier,
|
|
nonce: nonce,
|
|
signature: signature,
|
|
timestamp: NSNumber(value: timestamp))
|
|
}
|
|
|
|
self.iapManager.start(productId: productId, orderId: self.orderCode ?? "", applicationUsername: orderModel.discount?.sign_data?.applicationUsername, discount: discount)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
func restore(isLoding: Bool = true, shortPlayId: String? = nil, videoId: String? = nil, completer: ((_ isFinish: Bool, _ buyType: NRStoreAPI.BuyType?) -> Void)?) {
|
|
let buyType = self.waitRestoreModel?.buyType
|
|
|
|
guard let waitRestoreModel = self.waitRestoreModel,
|
|
let orderCode = waitRestoreModel.orderCode,
|
|
let payId = waitRestoreModel.payId,
|
|
let receipt = waitRestoreModel.receipt,
|
|
let transactionId = waitRestoreModel.transactionId
|
|
else {
|
|
if isLoding {
|
|
NRToast.show(text: "pay_error_3".localized)
|
|
}
|
|
completer?(false, buyType)
|
|
return
|
|
}
|
|
|
|
if isLoding {
|
|
NRHud.show()
|
|
}
|
|
|
|
let verifyData = self.getVerifyOrderParameters(orderCode: orderCode, payId: payId, transactionId: transactionId, purchaseToken: receipt)
|
|
|
|
let statParamenters: [String : Any] = [
|
|
"type" : isLoding ? "manual" : "auto",
|
|
"pay_data" : verifyData.toJsonString() ?? ""
|
|
]
|
|
NRStatAPI.nr_requestEventStat(orderCode: orderCode, shortPlayId: shortPlayId, videoId: videoId, eventKey: .payRestore, errorMsg: "restore", otherParamenters: statParamenters)
|
|
|
|
NRStoreAPI.requestVerifyOrder(parameters: verifyData) { response in
|
|
if isLoding {
|
|
NRHud.dismiss()
|
|
}
|
|
|
|
guard let model = response.data else {
|
|
completer?(false, buyType)
|
|
return
|
|
}
|
|
self.waitRestoreModel = nil
|
|
UserDefaults.nr_setObject(nil, forKey: kNRWaitRestoreIAPDefaultsKey)
|
|
|
|
if model.status == "success" {
|
|
if buyType == .subVip {
|
|
NRLoginManager.manager.userInfo?.is_vip = true
|
|
}
|
|
|
|
if isLoding {
|
|
NRToast.show(text: "reader_success".localized)
|
|
}
|
|
completer?(true, buyType)
|
|
if buyType == .subVip {
|
|
NotificationCenter.default.post(name: NRIapManager.buyVipFinishNotification, object: nil)
|
|
}
|
|
} else {
|
|
completer?(false, buyType)
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
func getProductId(templateId: String?) -> String? {
|
|
guard let templateId = templateId else { return nil }
|
|
return NRIapManager.IAPPrefix + "." + templateId
|
|
}
|
|
|
|
func clean() {
|
|
self.orderCode = nil
|
|
self.payId = nil
|
|
self.shortPlayId = nil
|
|
self.videoId = nil
|
|
self.completionHandler = nil
|
|
}
|
|
}
|
|
|
|
//MARK: JXIAPManagerDelegate
|
|
extension NRIapManager: JXIAPManagerDelegate {
|
|
|
|
func jx_iapPaySuccess(productId: String, receipt: String, transactionIdentifier: String) {
|
|
guard let orderCode = self.orderCode, let payId = self.payId else {
|
|
self.waitRestoreModel = nil
|
|
self.completionHandler?(false)
|
|
self.clean()
|
|
NRHud.dismiss()
|
|
return
|
|
}
|
|
|
|
self.waitRestoreModel?.productId = productId
|
|
self.waitRestoreModel?.receipt = receipt
|
|
self.waitRestoreModel?.transactionId = transactionIdentifier
|
|
|
|
UserDefaults.nr_setObject(self.waitRestoreModel, forKey: kNRWaitRestoreIAPDefaultsKey)
|
|
|
|
#if DEBUG
|
|
let verifyData = self.getVerifyOrderParameters(orderCode: orderCode, payId: payId, transactionId: transactionIdentifier, purchaseToken: receipt)
|
|
#else
|
|
let verifyData = self.getVerifyOrderParameters(orderCode: orderCode, payId: payId, transactionId: transactionIdentifier, purchaseToken: receipt)
|
|
#endif
|
|
|
|
NRStoreAPI.requestVerifyOrder(parameters: verifyData) { response in
|
|
NRHud.dismiss()
|
|
|
|
guard let model = response.data else {
|
|
NRStatAPI.nr_requestEventStat(orderCode: self.orderCode, shortPlayId: self.shortPlayId, videoId: self.videoId, eventKey: .payCallback, errorMsg: verifyData.toJsonString())
|
|
self.completionHandler?(false)
|
|
self.clean()
|
|
return
|
|
}
|
|
|
|
let buyType = self.waitRestoreModel?.buyType
|
|
self.waitRestoreModel = nil
|
|
UserDefaults.nr_setObject(nil, forKey: kNRWaitRestoreIAPDefaultsKey)
|
|
|
|
if model.status == "success" {
|
|
if buyType == .subVip {
|
|
NRLoginManager.manager.userInfo?.is_vip = true
|
|
}
|
|
|
|
NRToast.show(text: "reader_success".localized)
|
|
self.completionHandler?(true)
|
|
if buyType == .subVip {
|
|
NotificationCenter.default.post(name: NRIapManager.buyVipFinishNotification, object: nil)
|
|
}
|
|
} else {
|
|
NRToast.show(text: "pay_error_4".localized)
|
|
NRStatAPI.nr_requestEventStat(orderCode: self.orderCode, shortPlayId: self.shortPlayId, videoId: self.videoId, eventKey: .payCallback, errorMsg: verifyData.toJsonString())
|
|
self.completionHandler?(false)
|
|
}
|
|
self.clean()
|
|
}
|
|
|
|
}
|
|
|
|
func jx_iapPayFailed(productId: String, code: JXIAPManager.ErrorCode, msg: String?) {
|
|
NRHud.dismiss()
|
|
|
|
if code == .noProduct {
|
|
NRToast.show(text: "pay_error_5".localized)
|
|
} else if code == .cancelled {
|
|
NRToast.show(text: "pay_error_6".localized)
|
|
}
|
|
|
|
if code == .cancelled {
|
|
NRStatAPI.nr_requestEventStat(orderCode: self.orderCode, shortPlayId: self.shortPlayId, videoId: self.videoId, eventKey: .payCancel, errorMsg: "user cancel")
|
|
} else {
|
|
NRStatAPI.nr_requestEventStat(orderCode: self.orderCode, shortPlayId: self.shortPlayId, videoId: self.videoId, eventKey: .payError, errorMsg: msg)
|
|
}
|
|
|
|
self.completionHandler?(false)
|
|
|
|
self.waitRestoreModel = nil
|
|
self.clean()
|
|
}
|
|
}
|
|
|
|
extension NRIapManager {
|
|
|
|
func getVerifyOrderParameters(orderCode: String, payId: String, transactionId: String, purchaseToken: String) -> [String : Any] {
|
|
let parameters: [String : Any] = [
|
|
"order_code" : orderCode,
|
|
"pay_setting_id" : payId,
|
|
"pkg_name" : kNRAPPBundleIdentifier,
|
|
"transaction_id": transactionId,
|
|
"purchases_token" : purchaseToken
|
|
]
|
|
return parameters
|
|
}
|
|
|
|
///预加载支付项
|
|
func preloadingProducts() {
|
|
JXIAPManager.manager.fetchReceipt { _ in
|
|
self.payRequest = NRPayDataRequest()
|
|
self.payRequest?.requestProducts(isLoding: false, isToast: false) { _ in
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
extension NRIapManager {
|
|
///成功购买会员
|
|
@objc static let buyVipFinishNotification = Notification.Name(rawValue: "NRIapManager.buyVipFinishNotification")
|
|
}
|