diff --git a/MoviaBox/Base/Define/SPUserDefaultsKey.swift b/MoviaBox/Base/Define/SPUserDefaultsKey.swift index c06ac19..ce47d3d 100644 --- a/MoviaBox/Base/Define/SPUserDefaultsKey.swift +++ b/MoviaBox/Base/Define/SPUserDefaultsKey.swift @@ -14,3 +14,6 @@ let kSPLoginUserInfoDefaultsKey = "kSPLoginUserInfoDefaultsKey" ///首页搜索记录 let kSPHomeSearchHistoryDefaultsKey = "kSPHomeSearchHistoryDefaultsKey" + +///待恢复数据 +let kSPWaitRestoreIAPDefaultsKey = "kSPWaitRestoreIAPDefaultsKey" diff --git a/MoviaBox/Class/Player/Controller/SPPlayerDetailViewController.swift b/MoviaBox/Class/Player/Controller/SPPlayerDetailViewController.swift index 0b7fef4..d86f708 100644 --- a/MoviaBox/Class/Player/Controller/SPPlayerDetailViewController.swift +++ b/MoviaBox/Class/Player/Controller/SPPlayerDetailViewController.swift @@ -86,7 +86,7 @@ class SPPlayerDetailViewController: SPPlayerListViewController { //解锁视频需要的金币 let videoCoin = videoInfo.coins ?? 0 - if myCoin < videoCoin { + if myCoin < videoCoin {//金币不够时打开充值页面 self.onPlayBuy() } return diff --git a/MoviaBox/Class/Player/View/SPPlayBuyView.swift b/MoviaBox/Class/Player/View/SPPlayBuyView.swift index 041b7cf..49b9c93 100644 --- a/MoviaBox/Class/Player/View/SPPlayBuyView.swift +++ b/MoviaBox/Class/Player/View/SPPlayBuyView.swift @@ -49,6 +49,7 @@ class SPPlayBuyView: HWPanModalContentView { button.setTitle("Restore".localized, for: .normal) button.setTitleColor(.colorFFFFFF(alpha: 0.5), for: .normal) button.titleLabel?.font = .fontRegular(ofSize: 14) + button.addTarget(self, action: #selector(handleRestoreButton), for: .touchUpInside) return button }() @@ -121,7 +122,13 @@ extension SPPlayBuyView { guard let self = self else { return } self.rechargeView.userInfo = SPLoginManager.manager.userInfo } - + } + + @objc private func handleRestoreButton() { + SPIAPManager.manager.restore { [weak self] isFinish in + guard let self = self else { return } + self.handleBuyFinish() + } } } diff --git a/MoviaBox/Class/Wallet/Controller/SPStoreViewController.swift b/MoviaBox/Class/Wallet/Controller/SPStoreViewController.swift index 19311b4..da14f1c 100644 --- a/MoviaBox/Class/Wallet/Controller/SPStoreViewController.swift +++ b/MoviaBox/Class/Wallet/Controller/SPStoreViewController.swift @@ -84,7 +84,10 @@ class SPStoreViewController: SPViewController { extension SPStoreViewController { @objc private func handelRightBarButton() { - + SPIAPManager.manager.restore { [weak self] isFinish in + guard let self = self else { return } + self.buyFinish() + } } @objc private func buyFinish() { diff --git a/MoviaBox/Class/Wallet/View/SPCoinRechargeView.swift b/MoviaBox/Class/Wallet/View/SPCoinRechargeView.swift index 5c46d1a..0531030 100644 --- a/MoviaBox/Class/Wallet/View/SPCoinRechargeView.swift +++ b/MoviaBox/Class/Wallet/View/SPCoinRechargeView.swift @@ -141,7 +141,6 @@ extension SPCoinRechargeView: UICollectionViewDelegate, UICollectionViewDataSour SPIAPManager.manager.startRecharge(model: model, shortPlayId: shortPlayId, videoId: videoId) { [weak self] finish in if finish { - SPToast.show(text: "success".localized) self?.rechargeFinishHandle?() } } diff --git a/MoviaBox/Class/Wallet/View/SPMemberRechargeView.swift b/MoviaBox/Class/Wallet/View/SPMemberRechargeView.swift index b993528..6f27aac 100644 --- a/MoviaBox/Class/Wallet/View/SPMemberRechargeView.swift +++ b/MoviaBox/Class/Wallet/View/SPMemberRechargeView.swift @@ -106,7 +106,6 @@ extension SPMemberRechargeView: UICollectionViewDelegate, UICollectionViewDataSo SPIAPManager.manager.startRecharge(model: model, shortPlayId: shortPlayId, videoId: videoId) { [weak self] finish in if finish { - SPToast.show(text: "success".localized) self?.buyFinishHandle?() } } diff --git a/MoviaBox/Libs/SPIAPManager/SPIAPManager.swift b/MoviaBox/Libs/SPIAPManager/SPIAPManager.swift index 623e4c0..a1f6ea6 100644 --- a/MoviaBox/Libs/SPIAPManager/SPIAPManager.swift +++ b/MoviaBox/Libs/SPIAPManager/SPIAPManager.swift @@ -27,13 +27,25 @@ class SPIAPManager: NSObject { private var orderCode: String? private var payId: String? + ///恢复购买使用 + ///等待恢复的数据 + private var waitRestoreModel: SPWaitRestoreModel? = UserDefaults.jx_object(forKey: kSPWaitRestoreIAPDefaultsKey, class: SPWaitRestoreModel.self) as? SPWaitRestoreModel + ///开始内购 func startRecharge(model: SPPayTemplateItem, shortPlayId: String? = nil, videoId: String? = nil, handler: CompletionHandler? = nil) { + + if let waitRestoreModel = self.waitRestoreModel { + SPToast.show(text: "kToastMessage_02".localized) + handler?(false) + return + } + guard let payId = model.id else { handler?(false) return } self.completionHandler = handler + self.waitRestoreModel = SPWaitRestoreModel() let productId = SPIAPManager.IAPPrefix + (model.ios_template_id ?? "") @@ -42,11 +54,14 @@ class SPIAPManager: NSObject { SPWalletAPI.requestCreateOrder(payId: payId, shortPlayId: shortPlayId ?? "0", videoId: videoId ?? "0") { orderModel in guard let orderModel = orderModel else { SPHUD.dismiss() + self.waitRestoreModel = nil self.completionHandler?(false) return } self.orderCode = orderModel.order_code self.payId = payId + self.waitRestoreModel?.payId = payId + self.waitRestoreModel?.orderCode = orderModel.order_code self.iapManager.start(productId: productId, orderId: self.orderCode ?? "") @@ -54,6 +69,42 @@ class SPIAPManager: NSObject { } + func restore(isLoding: Bool = true, completer: ((_ isFinish: Bool) -> Void)?) { + guard let waitRestoreModel = self.waitRestoreModel, + let orderCode = waitRestoreModel.orderCode, + let payId = waitRestoreModel.payId, + let productId = waitRestoreModel.productId, + let receipt = waitRestoreModel.receipt + else { + if isLoding { + SPToast.show(text: "kToastMessage_01".localized) + } + return + } + + if isLoding { + SPHUD.show() + } + SPWalletAPI.requestVerifyOrder(orderCode: orderCode, payId: payId, productId: productId, purchaseToken: receipt) { model in + if model?.status == "success" { + self.waitRestoreModel = nil + UserDefaults.jx_setObject(self.waitRestoreModel, forKey: kSPWaitRestoreIAPDefaultsKey) + if isLoding { + SPToast.show(text: "success".localized) + } + completer?(true) + } else { +// SPToast.show(text: "failure".localized) + completer?(false) + } + + if isLoding { + SPHUD.dismiss() + } + } + + } + } @@ -61,15 +112,29 @@ class SPIAPManager: NSObject { extension SPIAPManager: JXIAPManagerDelegate { func jx_iapPaySuccess(productId: String, receipt: String, transactionIdentifier: String?) { - guard let orderCode = self.orderCode, let payId = self.payId else { return } + guard let orderCode = self.orderCode, let payId = self.payId else { + self.orderCode = nil + self.payId = nil + self.waitRestoreModel = nil + self.completionHandler?(false) + return + } + + self.waitRestoreModel?.productId = productId + self.waitRestoreModel?.receipt = receipt + + UserDefaults.jx_setObject(self.waitRestoreModel, forKey: kSPWaitRestoreIAPDefaultsKey) SPWalletAPI.requestVerifyOrder(orderCode: orderCode, payId: payId, productId: productId, purchaseToken: receipt) { model in SPHUD.dismiss() + self.orderCode = nil + self.payId = nil + if model?.status == "success" { - self.orderCode = nil - self.payId = nil - + self.waitRestoreModel = nil + UserDefaults.jx_setObject(self.waitRestoreModel, forKey: kSPWaitRestoreIAPDefaultsKey) + SPToast.show(text: "success".localized) self.completionHandler?(true) } else { self.completionHandler?(false) @@ -81,6 +146,8 @@ extension SPIAPManager: JXIAPManagerDelegate { func jx_iapPayFailed(productId: String, code: JXIAPManagerCode) { self.orderCode = nil self.payId = nil + self.waitRestoreModel = nil + SPHUD.dismiss() if code == .noProduct { diff --git a/MoviaBox/Libs/SPIAPManager/SPWaitRestoreModel.swift b/MoviaBox/Libs/SPIAPManager/SPWaitRestoreModel.swift new file mode 100644 index 0000000..b79039e --- /dev/null +++ b/MoviaBox/Libs/SPIAPManager/SPWaitRestoreModel.swift @@ -0,0 +1,44 @@ +// +// SPWaitRestoreModel.swift +// MoviaBox +// +// Created by 佳尔 on 2025/5/7. +// + +import UIKit + + +class SPWaitRestoreModel: SPModel, NSSecureCoding { + + + + var orderCode: String? + var payId: String? + var productId: String? + var receipt: String? + + + required init() { } + + static var supportsSecureCoding: Bool { + get { + return true + } + } + + func encode(with coder: NSCoder) { + coder.encode(orderCode, forKey: "orderCode") + coder.encode(payId, forKey: "payId") + coder.encode(productId, forKey: "productId") + coder.encode(receipt, forKey: "receipt") + } + + required init?(coder: NSCoder) { + super.init() + orderCode = coder.decodeObject(of: NSString.self, forKey: "orderCode") as? String + payId = coder.decodeObject(of: NSString.self, forKey: "payId") as? String + productId = coder.decodeObject(of: NSString.self, forKey: "productId") as? String + receipt = coder.decodeObject(of: NSString.self, forKey: "receipt") as? String + } + +} diff --git a/MoviaBox/Source/en.lproj/Localizable.strings b/MoviaBox/Source/en.lproj/Localizable.strings index ed68531..9913001 100644 --- a/MoviaBox/Source/en.lproj/Localizable.strings +++ b/MoviaBox/Source/en.lproj/Localizable.strings @@ -8,6 +8,7 @@ "Home" = "Home"; "success" = "success"; +"failure" = "failure"; "For You" = "For You"; "Error" = "Error"; "Profile" = "Profile"; @@ -91,11 +92,17 @@ "Unlock the previous episode" = "Unlock the previous episode"; "Purchase Single Episode" = "Purchase Single Episode"; +///没有可恢复购买 +"kToastMessage_01" = "There are no recoverable in-app purchases."; +///还有未完成的内购 +"kToastMessage_02" = "You still have unfinished in-app purchases. Please restore them first."; + ///请购买上一集提示 "kAlertMessage_01" = "The previous episode of this series has not been unlocked yet. Please unlock the previous episode first."; ///没有找到视频提示 "kAlertMessage_02" = "Purchase failed, please try again later!"; + "kLoginAgreementText" = "By continuing, you agree to the User Agreement and Privacy Policy"; "kBuyMemberTipText" = "Auto renew · Cancel anytime"; "kStoreTipTitle" = "Related terms and conditions:";