内购
This commit is contained in:
parent
fa6978eb4e
commit
318a4d9b7e
@ -209,6 +209,12 @@
|
||||
F39855942E379D9600E2D28D /* BRVideoDetailRecommendCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F39855932E379D9600E2D28D /* BRVideoDetailRecommendCell.swift */; };
|
||||
F39855962E38A27500E2D28D /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = F39855952E38A27500E2D28D /* GoogleService-Info.plist */; };
|
||||
F39855982E38BB3500E2D28D /* BRVideoDetailRecommendTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F39855972E38BB3500E2D28D /* BRVideoDetailRecommendTransformer.swift */; };
|
||||
F398559C2E38CF9700E2D28D /* JXIAPManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F398559A2E38CF9700E2D28D /* JXIAPManager.swift */; };
|
||||
F398559F2E38D1FF00E2D28D /* BRIAPVerifyModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F398559E2E38D1FF00E2D28D /* BRIAPVerifyModel.swift */; };
|
||||
F39855A12E38D22000E2D28D /* BRIAPOrderModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F39855A02E38D22000E2D28D /* BRIAPOrderModel.swift */; };
|
||||
F39855A32E38D25900E2D28D /* BRWaitRestoreModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F39855A22E38D25900E2D28D /* BRWaitRestoreModel.swift */; };
|
||||
F39855A52E38D2A800E2D28D /* BRIAP.swift in Sources */ = {isa = PBXBuildFile; fileRef = F39855A42E38D2A800E2D28D /* BRIAP.swift */; };
|
||||
F39855A72E38EE9800E2D28D /* BRRechargeRecordModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F39855A62E38EE9800E2D28D /* BRRechargeRecordModel.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
@ -429,6 +435,12 @@
|
||||
F39855952E38A27500E2D28D /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
|
||||
F39855972E38BB3500E2D28D /* BRVideoDetailRecommendTransformer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRVideoDetailRecommendTransformer.swift; sourceTree = "<group>"; };
|
||||
F39855992E38CBE800E2D28D /* BeeReel.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = BeeReel.entitlements; sourceTree = "<group>"; };
|
||||
F398559A2E38CF9700E2D28D /* JXIAPManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JXIAPManager.swift; sourceTree = "<group>"; };
|
||||
F398559E2E38D1FF00E2D28D /* BRIAPVerifyModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRIAPVerifyModel.swift; sourceTree = "<group>"; };
|
||||
F39855A02E38D22000E2D28D /* BRIAPOrderModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRIAPOrderModel.swift; sourceTree = "<group>"; };
|
||||
F39855A22E38D25900E2D28D /* BRWaitRestoreModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRWaitRestoreModel.swift; sourceTree = "<group>"; };
|
||||
F39855A42E38D2A800E2D28D /* BRIAP.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRIAP.swift; sourceTree = "<group>"; };
|
||||
F39855A62E38EE9800E2D28D /* BRRechargeRecordModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BRRechargeRecordModel.swift; sourceTree = "<group>"; };
|
||||
F70FA1F4169364C4C53534CE /* Pods-BeeReel.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BeeReel.release.xcconfig"; path = "Target Support Files/Pods-BeeReel/Pods-BeeReel.release.xcconfig"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
@ -723,6 +735,7 @@
|
||||
BF692AF62E0A480000A5C2DA /* Lib */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F398559D2E38D1DC00E2D28D /* IAP */,
|
||||
F39855252E32294C00E2D28D /* Alert */,
|
||||
BF3A56862E30E0C2009E5CF9 /* Empty */,
|
||||
BF692B452E0A9B5800A5C2DA /* Player */,
|
||||
@ -738,6 +751,7 @@
|
||||
BF692AF72E0A480E00A5C2DA /* Thirdparty */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F398559B2E38CF9700E2D28D /* JXIAPManager */,
|
||||
BF02B8372E30B2F800172177 /* AlignedCollectionViewFlowLayout */,
|
||||
BFC676A62E12AF04006659E5 /* FlowLayout */,
|
||||
BFC676612E0E2C8E006659E5 /* WMZBanner */,
|
||||
@ -1185,6 +1199,7 @@
|
||||
children = (
|
||||
F398553D2E336D3000E2D28D /* BRPayDateModel.swift */,
|
||||
F39855532E34A49500E2D28D /* BRPayDataRequest.swift */,
|
||||
F39855A62E38EE9800E2D28D /* BRRechargeRecordModel.swift */,
|
||||
);
|
||||
path = Model;
|
||||
sourceTree = "<group>";
|
||||
@ -1197,6 +1212,25 @@
|
||||
path = Guide;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F398559B2E38CF9700E2D28D /* JXIAPManager */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F398559A2E38CF9700E2D28D /* JXIAPManager.swift */,
|
||||
);
|
||||
path = JXIAPManager;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F398559D2E38D1DC00E2D28D /* IAP */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F39855A42E38D2A800E2D28D /* BRIAP.swift */,
|
||||
F398559E2E38D1FF00E2D28D /* BRIAPVerifyModel.swift */,
|
||||
F39855A02E38D22000E2D28D /* BRIAPOrderModel.swift */,
|
||||
F39855A22E38D25900E2D28D /* BRWaitRestoreModel.swift */,
|
||||
);
|
||||
path = IAP;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
@ -1358,6 +1392,8 @@
|
||||
BF02B8202E2F9B1000172177 /* BRWebViewController.swift in Sources */,
|
||||
BF692B242E0A825B00A5C2DA /* BRCryptorService.swift in Sources */,
|
||||
BF02B8242E2FAEB500172177 /* BRAboutUsCell.swift in Sources */,
|
||||
F39855A72E38EE9800E2D28D /* BRRechargeRecordModel.swift in Sources */,
|
||||
F398559F2E38D1FF00E2D28D /* BRIAPVerifyModel.swift in Sources */,
|
||||
BF692B342E0A87C800A5C2DA /* UIDevice+BRAdd.swift in Sources */,
|
||||
BF692B3E2E0A8D2300A5C2DA /* BRTabBarController.swift in Sources */,
|
||||
BF02B7E12E2DE64200172177 /* BRVideoProgressView.swift in Sources */,
|
||||
@ -1380,6 +1416,7 @@
|
||||
BF02B7ED2E2E390500172177 /* BRScrollView.swift in Sources */,
|
||||
F39855562E34BD6000E2D28D /* BRWalletViewController.swift in Sources */,
|
||||
BF3A56812E30C08F009E5CF9 /* BRHotSearchTagCell.swift in Sources */,
|
||||
F39855A12E38D22000E2D28D /* BRIAPOrderModel.swift in Sources */,
|
||||
BF02B8132E2F83C200172177 /* BRMineViewController.swift in Sources */,
|
||||
BF02B8222E2FAB1600172177 /* BRAboutUsViewController.swift in Sources */,
|
||||
BFC6766F2E0E3B5C006659E5 /* UIImageView+BRAdd.swift in Sources */,
|
||||
@ -1410,6 +1447,7 @@
|
||||
BF692AEC2E0A475D00A5C2DA /* SceneDelegate.swift in Sources */,
|
||||
BF692B492E0A9D0E00A5C2DA /* UIView+BRAdd.swift in Sources */,
|
||||
BF02B7F82E2F211A00172177 /* BRHomeCategoriesMainCell.swift in Sources */,
|
||||
F398559C2E38CF9700E2D28D /* JXIAPManager.swift in Sources */,
|
||||
BF02B8192E2F8B1100172177 /* BRMineCell.swift in Sources */,
|
||||
BFC676812E122733006659E5 /* BRPlayerProtocol.swift in Sources */,
|
||||
BFC676BC2E138ABB006659E5 /* BRNewReleasesViewController.swift in Sources */,
|
||||
@ -1459,6 +1497,7 @@
|
||||
F39855892E37732600E2D28D /* BRGuideViewController.swift in Sources */,
|
||||
F39855782E375E7000E2D28D /* Dictionary+BRAdd.swift in Sources */,
|
||||
BFC676832E122CC5006659E5 /* BRPlayerViewModel.swift in Sources */,
|
||||
F39855A52E38D2A800E2D28D /* BRIAP.swift in Sources */,
|
||||
BF02B7EF2E2E4BFD00172177 /* BRRateModel.swift in Sources */,
|
||||
BF692B672E0BC6C700A5C2DA /* AppDelegate+BRConfig.swift in Sources */,
|
||||
BF02B8022E2F39FE00172177 /* BRCategorieShortViewController.swift in Sources */,
|
||||
@ -1514,6 +1553,7 @@
|
||||
BF692B2A2E0A84F700A5C2DA /* JXUUID.m in Sources */,
|
||||
BF692B2B2E0A84F700A5C2DA /* PDKeyChain.m in Sources */,
|
||||
BF3A56852E30CA78009E5CF9 /* BRSearchResultCell.swift in Sources */,
|
||||
F39855A32E38D25900E2D28D /* BRWaitRestoreModel.swift in Sources */,
|
||||
BF02B7FA2E2F225D00172177 /* BRHomeCategoryModel.swift in Sources */,
|
||||
BFC6766B2E0E395F006659E5 /* BRHomeHeaderBannerCell.swift in Sources */,
|
||||
F398557E2E3766A300E2D28D /* BRStatAPI.swift in Sources */,
|
||||
|
@ -19,3 +19,5 @@ let kBRVideoRevolutionDefaultsKey = "kBRVideoRevolutionDefaultsKey"
|
||||
let kBRHasBeenOpenedAPPDefaultsKey = "kBRHasBeenOpenedAPPDefaultsKey"
|
||||
|
||||
let kBRApnsAlertDefaultsKey = "kBRApnsAlertDefaultsKey"
|
||||
|
||||
let kBRWaitRestoreIAPDefaultsKey = "kBRWaitRestoreIAPDefaultsKey"
|
||||
|
@ -26,4 +26,68 @@ class BRStoreAPI {
|
||||
completer?(response.data)
|
||||
}
|
||||
}
|
||||
|
||||
///创建内购订单
|
||||
static func requestCreateOrder(payId: String, shortPlayId: String, videoId: String, isDiscount: Bool = false, completer: ((_ orderModel: BRIAPOrderModel?) -> Void)?) {
|
||||
var param = BRNetworkParameters(path: "/createOrder")
|
||||
param.isToast = false
|
||||
param.parameters = [
|
||||
"payment_channel" : "apple",
|
||||
"short_play_id" : shortPlayId,
|
||||
"video_id" : videoId,
|
||||
"pay_setting_id" : payId,
|
||||
"is_discount" : isDiscount ? 1 : 0
|
||||
]
|
||||
|
||||
BRNetwork.request(parameters: param) { (response: BRNetworkResponse<BRIAPOrderModel>) in
|
||||
guard let data = response.data else {
|
||||
BRToast.show(text: "beereel_network".localized)
|
||||
completer?(nil)
|
||||
return
|
||||
}
|
||||
|
||||
if let message = data.message, message.count > 0 {
|
||||
if response.data?.code == 30007 {
|
||||
BRToast.show(text: "beereel_vip_error_1".localized)
|
||||
} else {
|
||||
BRToast.show(text: message)
|
||||
}
|
||||
|
||||
completer?(nil)
|
||||
} else {
|
||||
completer?(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///校验内购
|
||||
static func requestVerifyOrder(orderCode: String, payId: String, productId: String, purchaseToken: String, completer: ((_ model: BRIAPVerifyModel?) -> Void)?) {
|
||||
var param = BRNetworkParameters(path: "/applePaid")
|
||||
param.parameters = [
|
||||
"order_code" : orderCode,
|
||||
"pay_setting_id" : payId,
|
||||
"pkg_name" : kBRAPPBundleIdentifier,
|
||||
"transaction_id" : productId,
|
||||
"purchases_token" : purchaseToken
|
||||
]
|
||||
|
||||
BRNetwork.request(parameters: param) { (response: BRNetworkResponse<BRIAPVerifyModel>) in
|
||||
completer?(response.data)
|
||||
}
|
||||
}
|
||||
|
||||
///充值记录
|
||||
static func requestRechargeRecord(buyType: BuyType, page: Int, completer: ((_ listModel: BRListModel<BRRechargeRecordModel>?) -> Void)?) {
|
||||
var param = BRNetworkParameters(path: "/getCustomerOrder")
|
||||
param.method = .get
|
||||
param.parameters = [
|
||||
"page_size" : 20,
|
||||
"current_page" : page,
|
||||
"buy_type" : buyType == .subVip ? "vip" : buyType.rawValue
|
||||
]
|
||||
|
||||
BRNetwork.request(parameters: param) { (response: BRNetworkResponse<BRListModel<BRRechargeRecordModel>>) in
|
||||
completer?(response.data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,10 @@ import UIKit
|
||||
|
||||
class BRCoinOrderRecordViewController: BRViewController, WMZPageProtocol {
|
||||
|
||||
|
||||
private lazy var listArr: [BRRechargeRecordModel] = []
|
||||
private lazy var page = 1
|
||||
|
||||
private lazy var collectionViewLayout: UICollectionViewFlowLayout = {
|
||||
let layout = UICollectionViewFlowLayout()
|
||||
layout.itemSize = .init(width: UIScreen.width - 30, height: 80)
|
||||
@ -34,6 +38,8 @@ class BRCoinOrderRecordViewController: BRViewController, WMZPageProtocol {
|
||||
self.view.backgroundColor = .colorFFFFFF()
|
||||
|
||||
br_setupUI()
|
||||
|
||||
requestDataList(page: 1, completer: nil)
|
||||
}
|
||||
|
||||
func getMyScrollView() -> UIScrollView {
|
||||
@ -41,11 +47,15 @@ class BRCoinOrderRecordViewController: BRViewController, WMZPageProtocol {
|
||||
}
|
||||
|
||||
override func handleHeaderRefresh(_ completer: (() -> Void)?) {
|
||||
completer?()
|
||||
self.requestDataList(page: 1) {
|
||||
completer?()
|
||||
}
|
||||
}
|
||||
|
||||
override func handleFooterRefresh(_ completer: (() -> Void)?) {
|
||||
self.collectionView.br_endFooterRefreshing()
|
||||
self.requestDataList(page: self.page + 1) { [weak self] in
|
||||
self?.collectionView.br_endFooterRefreshing()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -70,11 +80,32 @@ extension BRCoinOrderRecordViewController: UICollectionViewDelegate, UICollectio
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! BRCoinOrderRecordCell
|
||||
cell.model = self.listArr[indexPath.row]
|
||||
return cell
|
||||
}
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||
return 10
|
||||
return self.listArr.count
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension BRCoinOrderRecordViewController {
|
||||
|
||||
private func requestDataList(page: Int, completer: (() -> Void)?) {
|
||||
BRStoreAPI.requestRechargeRecord(buyType: .coins, page: page) { [weak self] listModel in
|
||||
guard let self = self else { return }
|
||||
|
||||
if let list = listModel?.list {
|
||||
if page == 1 {
|
||||
self.listArr.removeAll()
|
||||
}
|
||||
self.listArr += list
|
||||
self.page = page
|
||||
self.collectionView.reloadData()
|
||||
}
|
||||
completer?()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -59,6 +59,18 @@ class BRStoreViewController: BRViewController {
|
||||
self.statusBarStyle = .lightContent
|
||||
self.view.backgroundColor = .color1C1C1C()
|
||||
|
||||
|
||||
|
||||
|
||||
let barButtonItem = UIBarButtonItem(title: "Restore".localized, style: .plain, target: self, action: #selector(handleRestore))
|
||||
var titleTextAttributes = barButtonItem.titleTextAttributes(for: .normal) ?? [:]
|
||||
titleTextAttributes[.foregroundColor] = UIColor.white
|
||||
titleTextAttributes[.font] = UIFont.fontRegular(ofSize: 14)
|
||||
barButtonItem.setTitleTextAttributes(titleTextAttributes, for: .normal)
|
||||
self.navigationItem.rightBarButtonItem = barButtonItem
|
||||
|
||||
|
||||
|
||||
configNavigationBack("nav_back_icon_03")
|
||||
|
||||
br_setupUI()
|
||||
@ -71,7 +83,14 @@ class BRStoreViewController: BRViewController {
|
||||
self.navigationController?.setNavigationBarHidden(false, animated: true)
|
||||
br_setNavigation(style: .dark)
|
||||
}
|
||||
|
||||
|
||||
@objc private func handleRestore() {
|
||||
BRIAP.manager.restore { isFinish in
|
||||
guard isFinish else { return }
|
||||
BRLoginManager.manager.updateUserInfo(completer: nil)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
extension BRStoreViewController {
|
||||
|
15
BeeReel/Class/Store/Model/BRRechargeRecordModel.swift
Normal file
15
BeeReel/Class/Store/Model/BRRechargeRecordModel.swift
Normal file
@ -0,0 +1,15 @@
|
||||
//
|
||||
// BRRechargeRecordModel.swift
|
||||
// BeeReel
|
||||
//
|
||||
// Created by 长沙鸿瑶 on 2025/7/29.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SmartCodable
|
||||
|
||||
class BRRechargeRecordModel: BRModel, SmartCodable {
|
||||
var type: String?
|
||||
var created_at: String?
|
||||
var value: String?
|
||||
}
|
@ -9,6 +9,12 @@ import UIKit
|
||||
|
||||
class BRCoinOrderRecordCell: BRCollectionViewCell {
|
||||
|
||||
var model: BRRechargeRecordModel? {
|
||||
didSet {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private lazy var titleLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = .fontRegular(ofSize: 14)
|
||||
|
@ -135,7 +135,16 @@ extension BRStoreCoinView: UICollectionViewDelegate, UICollectionViewDataSource
|
||||
return cell
|
||||
}
|
||||
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||
let item = self.newList[indexPath.section][indexPath.row]
|
||||
|
||||
BRIAP.manager.start(model: item, shortPlayId: shortPlayId, videoId: videoId) { [weak self] finish in
|
||||
if finish {
|
||||
BRLoginManager.manager.updateUserInfo(completer: nil)
|
||||
self?.buyFinishBlock?()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -104,5 +104,15 @@ extension BRStoreVipView: UICollectionViewDelegate, UICollectionViewDataSource {
|
||||
return cell
|
||||
}
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||
let item = list[indexPath.row]
|
||||
|
||||
BRIAP.manager.start(model: item, shortPlayId: shortPlayId, videoId: videoId) { [weak self] finish in
|
||||
if finish {
|
||||
BRLoginManager.manager.updateUserInfo(completer: nil)
|
||||
self?.buyFinishBlock?()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -25,6 +25,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
|
||||
requestAPNS()
|
||||
|
||||
BRIAP.manager.restore(isLoding: false) { isFinish in
|
||||
guard isFinish else { return }
|
||||
BRLoginManager.manager.updateUserInfo(completer: nil)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
202
BeeReel/Lib/IAP/BRIAP.swift
Normal file
202
BeeReel/Lib/IAP/BRIAP.swift
Normal file
@ -0,0 +1,202 @@
|
||||
//
|
||||
// BRIAP.swift
|
||||
// BeeReel
|
||||
//
|
||||
// Created by 长沙鸿瑶 on 2025/7/29.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class BRIAP {
|
||||
typealias CompletionHandler = ((_ finish: Bool) -> Void)
|
||||
///内购模版前缀
|
||||
static let IAPPrefix = "veloria."
|
||||
|
||||
|
||||
static let manager = BRIAP()
|
||||
|
||||
///成功回调
|
||||
private var completionHandler: CompletionHandler?
|
||||
|
||||
private lazy var iapManager: JXIAPManager = {
|
||||
let manager = JXIAPManager()
|
||||
manager.delegate = self
|
||||
return manager
|
||||
}()
|
||||
|
||||
private var orderCode: String?
|
||||
private var payId: String?
|
||||
|
||||
///恢复购买使用
|
||||
///等待恢复的数据
|
||||
private var waitRestoreModel: BRWaitRestoreModel? = UserDefaults.br_object(forKey: kBRWaitRestoreIAPDefaultsKey, as: BRWaitRestoreModel.self)
|
||||
|
||||
|
||||
|
||||
///开始内购
|
||||
func start(model: BRPayItem, shortPlayId: String? = nil, videoId: String? = nil, isDiscount: Bool = false, hudShowView: UIView? = nil, handler: CompletionHandler? = nil) {
|
||||
|
||||
if let _ = self.waitRestoreModel {
|
||||
BRToast.show(text: "beereel_pay_error_1".localized)
|
||||
handler?(false)
|
||||
return
|
||||
}
|
||||
|
||||
guard let payId = model.id else {
|
||||
handler?(false)
|
||||
return
|
||||
}
|
||||
self.completionHandler = handler
|
||||
self.waitRestoreModel = BRWaitRestoreModel()
|
||||
self.waitRestoreModel?.buyType = model.buy_type
|
||||
|
||||
let productId = BRIAP.IAPPrefix + (model.ios_template_id ?? "")
|
||||
|
||||
BRHUD.show(containerView: hudShowView)
|
||||
|
||||
BRStoreAPI.requestCreateOrder(payId: payId, shortPlayId: shortPlayId ?? "0", videoId: videoId ?? "0", isDiscount: isDiscount) { orderModel in
|
||||
guard let orderModel = orderModel else {
|
||||
BRHUD.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 ?? "", discount: nil)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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 {
|
||||
BRToast.show(text: "beereel_pay_error_3".localized)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if isLoding {
|
||||
BRHUD.show()
|
||||
}
|
||||
BRStoreAPI.requestVerifyOrder(orderCode: orderCode, payId: payId, productId: productId, purchaseToken: receipt) { model in
|
||||
if isLoding {
|
||||
BRHUD.dismiss()
|
||||
}
|
||||
|
||||
guard let model = model else {
|
||||
completer?(false)
|
||||
return
|
||||
}
|
||||
let buyType = self.waitRestoreModel?.buyType
|
||||
self.waitRestoreModel = nil
|
||||
UserDefaults.br_setObject(self.waitRestoreModel, forKey: kBRWaitRestoreIAPDefaultsKey)
|
||||
|
||||
if model.status == "success" {
|
||||
if buyType == .subVip {
|
||||
BRLoginManager.manager.userInfo?.is_vip = true
|
||||
}
|
||||
|
||||
if isLoding {
|
||||
BRToast.show(text: "beereel_succeed".localized)
|
||||
}
|
||||
completer?(true)
|
||||
if buyType == .subVip {
|
||||
NotificationCenter.default.post(name: BRIAP.buyVipFinishNotification, object: nil)
|
||||
}
|
||||
} else {
|
||||
completer?(false)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
func getProductId(templateId: String?) -> String? {
|
||||
guard let templateId = templateId else { return nil }
|
||||
return BRIAP.IAPPrefix + templateId
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: -------------- JXIAPManagerDelegate --------------
|
||||
extension BRIAP: JXIAPManagerDelegate {
|
||||
|
||||
func jx_iapPaySuccess(productId: String, receipt: String, transactionIdentifier: String?) {
|
||||
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.br_setObject(self.waitRestoreModel, forKey: kBRWaitRestoreIAPDefaultsKey)
|
||||
|
||||
BRStoreAPI.requestVerifyOrder(orderCode: orderCode, payId: payId, productId: productId, purchaseToken: receipt) { model in
|
||||
BRHUD.dismiss()
|
||||
|
||||
self.orderCode = nil
|
||||
self.payId = nil
|
||||
|
||||
guard let model = model else {
|
||||
self.completionHandler?(false)
|
||||
return
|
||||
}
|
||||
|
||||
let buyType = self.waitRestoreModel?.buyType
|
||||
self.waitRestoreModel = nil
|
||||
UserDefaults.br_setObject(self.waitRestoreModel, forKey: kBRWaitRestoreIAPDefaultsKey)
|
||||
|
||||
if model.status == "success" {
|
||||
if buyType == .subVip {
|
||||
BRLoginManager.manager.userInfo?.is_vip = true
|
||||
}
|
||||
|
||||
BRToast.show(text: "beereel_succeed".localized)
|
||||
self.completionHandler?(true)
|
||||
if buyType == .subVip {
|
||||
NotificationCenter.default.post(name: BRIAP.buyVipFinishNotification, object: nil)
|
||||
}
|
||||
} else {
|
||||
self.completionHandler?(false)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func jx_iapPayFailed(productId: String, code: JXIAPManagerCode) {
|
||||
self.orderCode = nil
|
||||
self.payId = nil
|
||||
self.waitRestoreModel = nil
|
||||
|
||||
BRHUD.dismiss()
|
||||
|
||||
if code == .noProduct {
|
||||
BRToast.show(text: "beereel_pay_error_2".localized)
|
||||
} else if code == .cancelled {
|
||||
BRToast.show(text: "beereel_pay_error_4".localized)
|
||||
}
|
||||
self.completionHandler?(false)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
extension BRIAP {
|
||||
///成功购买会员
|
||||
@objc static let buyVipFinishNotification = NSNotification.Name(rawValue: "BRIAP.buyVipFinishNotification")
|
||||
|
||||
}
|
34
BeeReel/Lib/IAP/BRIAPOrderModel.swift
Normal file
34
BeeReel/Lib/IAP/BRIAPOrderModel.swift
Normal file
@ -0,0 +1,34 @@
|
||||
//
|
||||
// BRIAPOrderModel.swift
|
||||
// BeeReel
|
||||
//
|
||||
// Created by 长沙鸿瑶 on 2025/7/29.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SmartCodable
|
||||
|
||||
class BRIAPOrderModel: BRModel, SmartCodable {
|
||||
|
||||
var code: Int?
|
||||
var message: String?
|
||||
var money: String?
|
||||
var order_code: String?
|
||||
var is_backhaul: String?
|
||||
var discount: BRIAPOrderDiscountModel?
|
||||
}
|
||||
|
||||
class BRIAPOrderDiscountModel: BRModel, SmartCodable {
|
||||
|
||||
var is_discount: Bool?
|
||||
var discount_code: String?
|
||||
var sign_data: BRIAPOrderDiscountSign?
|
||||
}
|
||||
|
||||
class BRIAPOrderDiscountSign: BRModel, SmartCodable {
|
||||
|
||||
var keyIdentifier: String?
|
||||
var nonce: String?
|
||||
var timestamp: TimeInterval?
|
||||
var signature: String?
|
||||
}
|
17
BeeReel/Lib/IAP/BRIAPVerifyModel.swift
Normal file
17
BeeReel/Lib/IAP/BRIAPVerifyModel.swift
Normal file
@ -0,0 +1,17 @@
|
||||
//
|
||||
// BRIAPVerifyModel.swift
|
||||
// BeeReel
|
||||
//
|
||||
// Created by 长沙鸿瑶 on 2025/7/29.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SmartCodable
|
||||
|
||||
class BRIAPVerifyModel: BRModel, SmartCodable {
|
||||
|
||||
var code: String?
|
||||
var status: String?
|
||||
var money: String?
|
||||
var is_backhaul: String?
|
||||
}
|
45
BeeReel/Lib/IAP/BRWaitRestoreModel.swift
Normal file
45
BeeReel/Lib/IAP/BRWaitRestoreModel.swift
Normal file
@ -0,0 +1,45 @@
|
||||
//
|
||||
// BRWaitRestoreModel.swift
|
||||
// BeeReel
|
||||
//
|
||||
// Created by 长沙鸿瑶 on 2025/7/29.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class BRWaitRestoreModel: BRModel, NSSecureCoding {
|
||||
|
||||
var orderCode: String?
|
||||
var payId: String?
|
||||
var productId: String?
|
||||
var receipt: String?
|
||||
var buyType: BRStoreAPI.BuyType?
|
||||
|
||||
|
||||
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")
|
||||
coder.encode(buyType?.rawValue, forKey: "buyType")
|
||||
}
|
||||
|
||||
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
|
||||
if let type = coder.decodeObject(of: NSString.self, forKey: "buyType") as? String {
|
||||
buyType = BRStoreAPI.BuyType(rawValue: type)
|
||||
}
|
||||
}
|
||||
}
|
@ -102,6 +102,63 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"beereel_pay_error_1" : {
|
||||
"comment" : "还有未完成购买",
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "You have unfinished in-app purchases, please restore them first."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"beereel_pay_error_2" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Invalid in-app purchase"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"beereel_pay_error_3" : {
|
||||
"comment" : "没有可恢复购买",
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "No in-app purchases can be restored"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"beereel_pay_error_4" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Payment has been cancelled"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"beereel_succeed" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Success"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"beereel_video_lock_tip_text" : {
|
||||
"comment" : "请解锁上一集",
|
||||
"extractionState" : "manual",
|
||||
@ -114,6 +171,18 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"beereel_vip_error_1" : {
|
||||
"comment" : "已是会员",
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "You are already a member!"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Browse Genres" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
@ -631,6 +700,17 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Restore" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Restore"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Reward Coins" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
|
185
BeeReel/Thirdparty/JXIAPManager/JXIAPManager.swift
vendored
Normal file
185
BeeReel/Thirdparty/JXIAPManager/JXIAPManager.swift
vendored
Normal file
@ -0,0 +1,185 @@
|
||||
//
|
||||
// JXIAPManager.swift
|
||||
// BoJia
|
||||
//
|
||||
// Created by 火山传媒 on 2024/6/3.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import StoreKit
|
||||
|
||||
@objc protocol JXIAPManagerDelegate {
|
||||
/// 购买成功
|
||||
@objc optional func jx_iapPaySuccess(productId: String, receipt: String, transactionIdentifier: String?)
|
||||
/// 购买失败
|
||||
@objc optional func jx_iapPayFailed(productId: String, code: JXIAPManagerCode)
|
||||
/// 恢复商品(仅限永久有效商品)
|
||||
@objc optional func iapPayRestore(productIds: [String], transactionIds: [String])
|
||||
// /// 加载
|
||||
// @objc optional func iapPayShowHud()
|
||||
// /// 系统错误
|
||||
// @objc optional func iapSysWrong()
|
||||
// /// 验证成功
|
||||
// @objc optional func verifySuccess()
|
||||
// /// 验证失败
|
||||
// @objc optional func verifyFailed()
|
||||
}
|
||||
|
||||
@objc enum JXIAPManagerCode: Int {
|
||||
///未知错误
|
||||
case unknown
|
||||
///取消交易
|
||||
case cancelled
|
||||
///没有商品
|
||||
case noProduct
|
||||
}
|
||||
|
||||
class JXIAPManager: NSObject {
|
||||
|
||||
|
||||
static let manager: JXIAPManager = JXIAPManager()
|
||||
|
||||
weak var delegate: JXIAPManagerDelegate?
|
||||
|
||||
private var payment: SKPayment?
|
||||
|
||||
private var product: SKProduct?
|
||||
private var productId: String?
|
||||
|
||||
private var discount: SKPaymentDiscount?
|
||||
private var orderId: String?
|
||||
private var applicationUsername: String? {
|
||||
get {
|
||||
return orderId
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
SKPaymentQueue.default().add(self)
|
||||
}
|
||||
|
||||
|
||||
func start(productId: String, orderId: String, discount: SKPaymentDiscount? = nil) {
|
||||
self.product = nil
|
||||
self.productId = productId
|
||||
self.orderId = orderId
|
||||
self.discount = discount
|
||||
|
||||
let set = Set([productId])
|
||||
let productsRequest = SKProductsRequest(productIdentifiers: set)
|
||||
productsRequest.delegate = self
|
||||
productsRequest.start()
|
||||
}
|
||||
|
||||
/// 购买商品
|
||||
private func buyProduct() {
|
||||
guard let product = self.product else { return }
|
||||
|
||||
// 要购买商品,开个小票
|
||||
let payment = SKMutablePayment(product: product)
|
||||
payment.applicationUsername = applicationUsername
|
||||
if let discount = self.discount {
|
||||
payment.paymentDiscount = discount
|
||||
self.discount = nil
|
||||
}
|
||||
|
||||
self.payment = payment
|
||||
|
||||
// 去收银台排队,准备购买
|
||||
SKPaymentQueue.default().add(payment)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//MARK: -------------- SKProductsRequestDelegate --------------
|
||||
extension JXIAPManager: SKProductsRequestDelegate {
|
||||
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
|
||||
guard let product = response.products.first else {
|
||||
DispatchQueue.main.async {
|
||||
if let productId = self.productId {
|
||||
self.productId = nil
|
||||
self.delegate?.jx_iapPayFailed?(productId: productId, code: .noProduct)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
self.product = product
|
||||
|
||||
self.buyProduct()
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: -------------- SKPaymentTransactionObserver --------------
|
||||
extension JXIAPManager: SKPaymentTransactionObserver {
|
||||
///购买回调
|
||||
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
|
||||
|
||||
for transaction in transactions {
|
||||
switch transaction.transactionState {
|
||||
case .purchased:
|
||||
DispatchQueue.main.async {
|
||||
self.completeTransaction(transaction: transaction)
|
||||
}
|
||||
SKPaymentQueue.default().finishTransaction(transaction)
|
||||
|
||||
case .failed:
|
||||
DispatchQueue.main.async {
|
||||
self.failedTransaction(transaction: transaction)
|
||||
}
|
||||
SKPaymentQueue.default().finishTransaction(transaction)
|
||||
// case .restored:
|
||||
// self.restoreTransaction(transaction: transaction)
|
||||
|
||||
case .purchasing:
|
||||
break
|
||||
default:
|
||||
SKPaymentQueue.default().finishTransaction(transaction)
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// func paymentQueue(_ queue: SKPaymentQueue, shouldAddStorePayment payment: SKPayment, for product: SKProduct) -> Bool {
|
||||
// return true
|
||||
// }
|
||||
|
||||
/// 恢复购买回调
|
||||
func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension JXIAPManager {
|
||||
private func completeTransaction(transaction: SKPaymentTransaction) {
|
||||
guard let receiptURL = Bundle.main.appStoreReceiptURL else { return }
|
||||
let receiptData = NSData(contentsOf: receiptURL)
|
||||
guard let encodeStr = receiptData?.base64EncodedString(options: .endLineWithLineFeed) else { return }
|
||||
guard let transactionIdentifier = transaction.transactionIdentifier else { return }
|
||||
|
||||
|
||||
guard let productId = self.productId, productId == transaction.payment.productIdentifier else { return }
|
||||
self.productId = nil
|
||||
self.delegate?.jx_iapPaySuccess?(productId: productId, receipt: encodeStr, transactionIdentifier: transactionIdentifier)
|
||||
|
||||
}
|
||||
|
||||
private func failedTransaction(transaction: SKPaymentTransaction) {
|
||||
let error = transaction.error as? SKError
|
||||
guard let productId = self.productId else { return }
|
||||
self.productId = nil
|
||||
|
||||
switch error?.code {
|
||||
case SKError.paymentCancelled:
|
||||
self.delegate?.jx_iapPayFailed?(productId: productId, code: .cancelled)
|
||||
default:
|
||||
self.delegate?.jx_iapPayFailed?(productId: productId, code: .unknown)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user