diff --git a/yinmeng-ios/yinmeng-ios.xcodeproj/project.pbxproj b/yinmeng-ios/yinmeng-ios.xcodeproj/project.pbxproj index 5a51985..370210b 100644 --- a/yinmeng-ios/yinmeng-ios.xcodeproj/project.pbxproj +++ b/yinmeng-ios/yinmeng-ios.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ 23270C292B0E037300B9303B /* MessageConentAudioView.m in Sources */ = {isa = PBXBuildFile; fileRef = 23270C272B0E037300B9303B /* MessageConentAudioView.m */; }; 23270C2C2B0E041300B9303B /* MessageAudioCenter.m in Sources */ = {isa = PBXBuildFile; fileRef = 23270C2B2B0E041300B9303B /* MessageAudioCenter.m */; }; + 23270C302B0E071B00B9303B /* MewPaymentAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23270C2F2B0E071B00B9303B /* MewPaymentAction.swift */; }; 233757562B0CB577001D0B7F /* MessagePresenter.m in Sources */ = {isa = PBXBuildFile; fileRef = 233757262B0CB577001D0B7F /* MessagePresenter.m */; }; 233757572B0CB577001D0B7F /* MessageMenuModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 2337572C2B0CB577001D0B7F /* MessageMenuModel.m */; }; 233757582B0CB577001D0B7F /* ChatLimitModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 2337572D2B0CB577001D0B7F /* ChatLimitModel.m */; }; @@ -197,6 +198,8 @@ 23270C282B0E037300B9303B /* MessageConentAudioView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MessageConentAudioView.h; sourceTree = ""; }; 23270C2A2B0E041300B9303B /* MessageAudioCenter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MessageAudioCenter.h; sourceTree = ""; }; 23270C2B2B0E041300B9303B /* MessageAudioCenter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MessageAudioCenter.m; sourceTree = ""; }; + 23270C2E2B0E071B00B9303B /* yinmeng-ios-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "yinmeng-ios-Bridging-Header.h"; sourceTree = ""; }; + 23270C2F2B0E071B00B9303B /* MewPaymentAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MewPaymentAction.swift; sourceTree = ""; }; 233757262B0CB577001D0B7F /* MessagePresenter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MessagePresenter.m; sourceTree = ""; }; 233757272B0CB577001D0B7F /* MessagePresenter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MessagePresenter.h; sourceTree = ""; }; 233757292B0CB577001D0B7F /* MessageProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MessageProtocol.h; sourceTree = ""; }; @@ -599,6 +602,16 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 23270C2D2B0E06F700B9303B /* MewIAP */ = { + isa = PBXGroup; + children = ( + 23270C2F2B0E071B00B9303B /* MewPaymentAction.swift */, + 23270C2E2B0E071B00B9303B /* yinmeng-ios-Bridging-Header.h */, + ); + name = MewIAP; + path = "yinmeng-ios/Base/MewIAP"; + sourceTree = SOURCE_ROOT; + }; 233757242B0CB577001D0B7F /* Message */ = { isa = PBXGroup; children = ( @@ -1749,6 +1762,7 @@ 8C9C83C12B0C697A00A601BC /* Mew */ = { isa = PBXGroup; children = ( + 23270C2D2B0E06F700B9303B /* MewIAP */, 8C9C83C22B0C697A00A601BC /* Home */, 8C9C83E62B0C697A00A601BC /* Room */, 8C9C83FA2B0C697A00A601BC /* Party */, @@ -2157,6 +2171,7 @@ TargetAttributes = { 8C4D534B2AFD4CF600238AE6 = { CreatedOnToolsVersion = 14.0; + LastSwiftMigration = 1500; }; }; }; @@ -2357,6 +2372,7 @@ 8C9C845D2B0C697A00A601BC /* MewHomePresenter.m in Sources */, 2337575A2B0CB577001D0B7F /* UITableView+NIMScrollToBottom.m in Sources */, 8C9C84252B0C697A00A601BC /* YMMessageInfoModel.m in Sources */, + 23270C302B0E071B00B9303B /* MewPaymentAction.swift in Sources */, 8C9C82D12B0C695600A601BC /* RechargeStorage.m in Sources */, 8C9C84792B0C697A00A601BC /* MewLoginNumberViewController.m in Sources */, 8C9C841D2B0C697A00A601BC /* ThemeColor+Room.m in Sources */, @@ -2571,6 +2587,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = "yinmeng-ios/yinmeng-ios.entitlements"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; @@ -2597,6 +2614,9 @@ SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OBJC_BRIDGING_HEADER = "yinmeng-ios/Base/MewIAP/yinmeng-ios-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 1; }; name = Debug; @@ -2607,6 +2627,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = "yinmeng-ios/yinmeng-ios.entitlements"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; @@ -2633,6 +2654,8 @@ SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OBJC_BRIDGING_HEADER = "yinmeng-ios/Base/MewIAP/yinmeng-ios-Bridging-Header.h"; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 1; }; name = Release; diff --git a/yinmeng-ios/yinmeng-ios/Base/MewIAP/MewPaymentAction.swift b/yinmeng-ios/yinmeng-ios/Base/MewIAP/MewPaymentAction.swift new file mode 100644 index 0000000..def22bf --- /dev/null +++ b/yinmeng-ios/yinmeng-ios/Base/MewIAP/MewPaymentAction.swift @@ -0,0 +1,264 @@ +// +// MewPaymentAction.swift +// yinmeng-ios +// +// Created by duoban on 2023/11/22. +// + +import UIKit +import StoreKit + +@available(iOS 15.0, *) +typealias MewTransaction = StoreKit.Transaction +@available(iOS 15.0, *) +typealias MewRenewalInfo = StoreKit.Product.SubscriptionInfo.RenewalInfo +@available(iOS 15.0, *) +typealias MewRenewalState = StoreKit.Product.SubscriptionInfo.RenewalState + + + +enum MewStoreError: Error { + + + // 错误回调枚举 + case MewFailedVerification + case MewNoProduct + +} + +@objc public enum MewStoreConditionResult: Int64 { // 支付状态 + case MewStart // 开始 + case MewPay // 进行苹果支付 + case MewVerifiedServer // 服务器校验 + case MewUserCancelled // 用户取消 + case MewPending // 等待(家庭用户才有的状态) + case MewUnowned + case MewNoProduct //没有商品 + case MewFailedVerification //验证失败 +} + + + +@available(iOS 15.0, *) +public class MewPaymentAction: NSObject { + public typealias Mew_KConditionBlock = (_ state :MewStoreConditionResult,_ param:Dictionary?) ->() + @objc public var Mew_ConditionBlock: Mew_KConditionBlock! // 状态回调 + + var Mew_updateListenerTask: Task? = nil // 支付事件监听 + + var Mew_transactionMap :[String:Transaction] // 用于完成Id的缓存map + + var Mew_name: String = "Mew_iosStore" // 单例的写法 + + @objc public static let shared = { + let instance = MewPaymentAction() + return instance + }() + + private override init() { // 单例需要保证private的私有性质 + + Mew_transactionMap = [:] // 初始化 + super.init() + Task { + Mew_updateListenerTask = Mew_listenForTransactions() + } + + + } + + + // 退订 + @objc public func Mew_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") + } + } + + + // 购买某个产品 + @objc public func Mew_demandCommodityThing(productId:String, uuid: String) async throws { + if(Mew_ConditionBlock != nil ){ + Mew_ConditionBlock(MewStoreConditionResult.MewStart,nil) + } + do { + let list:[String] = [productId] + let storeProducts = try await Product.products(for: Set.init(list)) + + + + + if storeProducts.count > 0 { + try await Mew_purchase(storeProducts[0],uuid) + }else { + print("iap: no found product") + if(Mew_ConditionBlock != nil ){ + Mew_ConditionBlock(MewStoreConditionResult.MewNoProduct,nil) + } + throw MewStoreError.MewNoProduct // 没有该产品 + } + } catch { + print("Failed product request from the App Store server: \(error)") + if(Mew_ConditionBlock != nil ){ + Mew_ConditionBlock(MewStoreConditionResult.MewNoProduct,nil) + } + throw MewStoreError.MewNoProduct // 没有该产品 + } + } + + // 购买 + private func Mew_purchase(_ product: Product, _ uuid: String) async throws -> Transaction? { + if(Mew_ConditionBlock != nil ){ + Mew_ConditionBlock(MewStoreConditionResult.MewPay,nil) + } + + guard let curUUID = UUID.init(uuidString: uuid) else{ + + if(Mew_ConditionBlock != nil ){ + Mew_ConditionBlock(MewStoreConditionResult.MewFailedVerification,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 Mew_verifiedAndAccomplish(verification) + return transaction + case .userCancelled: // 用户取消 + if(Mew_ConditionBlock != nil ){ + Mew_ConditionBlock(MewStoreConditionResult.MewUserCancelled,nil) + } + return nil + case .pending: // 此次购买被挂起 + if(Mew_ConditionBlock != nil ){ + Mew_ConditionBlock(MewStoreConditionResult.MewPending,nil) + } + return nil + default: + if(Mew_ConditionBlock != nil ){ + Mew_ConditionBlock(MewStoreConditionResult.MewUnowned,nil) + } + return nil + } + } + + // 校验 + func Mew_checkVerified(_ result: VerificationResult) throws -> T { + //Check whether the JWS passes StoreKit verification. + switch result { + case .unverified: + //StoreKit parses the JWS, but it fails verification. + if(Mew_ConditionBlock != nil ){ + Mew_ConditionBlock(MewStoreConditionResult.MewFailedVerification,nil) + } + throw MewStoreError.MewFailedVerification + case .verified(let safe): + //The result is verified. Return the unwrapped value. + print("iap: verified success") + return safe + } + } + + // 校验&完成后传给服务器 + func Mew_verifiedAndAccomplish(_ verification:VerificationResult) async throws -> Transaction?{ + //Check whether the transaction is verified. If it isn't, + //this function rethrows the verification error. + let transaction = try Mew_checkVerified(verification) + // 这里将订单提交给服务器进行验证 ~~~ + let transactionId = try verification.payloadValue.id + + // 添加进入待完成map + let key = String(transactionId) + Mew_transactionMap[key] = transaction + await Mew_uploadServer(for: transactionId) + + // 这里不触发完成,等服务器验证再触发完成逻辑 + await transaction.finish() + + print("iap: finish") + return transaction + } + /*All transactions:全部的购买交易订单 + Latest transactions:最新的购买交易订单。(分为订阅品项和除订阅品项外的所有类型二种) + Current entitlements:当前用户有购买的权限。(全部的订阅品项、和非消耗品项) + */ + func Mew_getAllBusiness(transactionId:String) async { + + let transactionIntId = UInt64(transactionId) + for await result in Transaction.all { + do { + let tran = try Mew_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 Mew_verifyBusinessAccomplish(transaction:String) async{ + if(Mew_transactionMap[transaction] != nil){ + await Mew_transactionMap[transaction]!.finish() + Mew_transactionMap.removeValue(forKey: transaction) + print("verifyBusinessFinish end") + }else { + await Mew_getAllBusiness(transactionId: transaction) + } + } + + @MainActor + func Mew_uploadServer(for transactionId:UInt64) async { + let dic :Dictionary = ["transactionId":transactionId] + if(Mew_ConditionBlock != nil ){ + Mew_ConditionBlock(MewStoreConditionResult.MewVerifiedServer,dic) + } + } + + // 支付监听事件 + func Mew_listenForTransactions() -> Task { + 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.Mew_verifiedAndAccomplish(result) + } catch { + //StoreKit has a transaction that fails verification. Don't deliver content to the user. + print("Transaction failed verification") + } + } + } + } + //获取推广内购商品 + func Mew_Promotion() async -> [SKProduct]?{ + let promotion = SKProductStorePromotionController() + + let prodicts = try? await promotion.promotionOrder() + return prodicts + } + // 销毁调用 + deinit { + Mew_updateListenerTask?.cancel() + } + + +} + diff --git a/yinmeng-ios/yinmeng-ios/Base/MewIAP/yinmeng-ios-Bridging-Header.h b/yinmeng-ios/yinmeng-ios/Base/MewIAP/yinmeng-ios-Bridging-Header.h new file mode 100644 index 0000000..1b2cb5d --- /dev/null +++ b/yinmeng-ios/yinmeng-ios/Base/MewIAP/yinmeng-ios-Bridging-Header.h @@ -0,0 +1,4 @@ +// +// Use this file to import your target's public headers that you would like to expose to Swift. +// + diff --git a/yinmeng-ios/yinmeng-ios/Base/Tool/IAPHelper/YMIAPHelper.h b/yinmeng-ios/yinmeng-ios/Base/Tool/IAPHelper/YMIAPHelper.h index 533ee5a..a8f0c67 100644 --- a/yinmeng-ios/yinmeng-ios/Base/Tool/IAPHelper/YMIAPHelper.h +++ b/yinmeng-ios/yinmeng-ios/Base/Tool/IAPHelper/YMIAPHelper.h @@ -17,7 +17,7 @@ typedef NS_ENUM(NSInteger ,PaymentStatus) { }; @protocol YMIAPHelperDelegate - +@optional ///当前充值的状态 - (void)rechargeProcessStatus:(PaymentStatus)status; diff --git a/yinmeng-ios/yinmeng-ios/Main/Mew/Home/View/MewHomeViewController.m b/yinmeng-ios/yinmeng-ios/Main/Mew/Home/View/MewHomeViewController.m index 1744473..c5f2d13 100644 --- a/yinmeng-ios/yinmeng-ios/Main/Mew/Home/View/MewHomeViewController.m +++ b/yinmeng-ios/yinmeng-ios/Main/Mew/Home/View/MewHomeViewController.m @@ -18,7 +18,8 @@ #import "YMMineSettingViewController.h" #import "MewMineCollectionViewController.h" #import "MewRechargeViewController.h" - +#import "SessionViewController.h" +#import /// Tool #import "YMMacro.h" #import "ThemeColor.h" @@ -238,7 +239,9 @@ /// optionType: 0 - 取消关注 1 - 关注 2-聊天 - (void)userIntrodctionViewWithUserOption:(NSInteger)optionType { if (optionType == 2) { //聊天 - + NIMSession *session = [NIMSession session:self.targetUid type:NIMSessionTypeP2P]; + SessionViewController *vc = [[SessionViewController alloc] initWithSession:session]; + [self.navigationController pushViewController:vc animated:YES]; } else { [self.presenter attentionUser:self.targetUid state:optionType == 1]; } diff --git a/yinmeng-ios/yinmeng-ios/Main/Mew/Mine/View/MewRechargeViewController.m b/yinmeng-ios/yinmeng-ios/Main/Mew/Mine/View/MewRechargeViewController.m index 56a4090..751e131 100644 --- a/yinmeng-ios/yinmeng-ios/Main/Mew/Mine/View/MewRechargeViewController.m +++ b/yinmeng-ios/yinmeng-ios/Main/Mew/Mine/View/MewRechargeViewController.m @@ -12,10 +12,13 @@ #import #import "YMMacro.h" #import "YMIAPHelper.h" +#import "AccountInfoStorage.h" /// P #import "MineRechargeProtocol.h" #import "MineRechargePresenter.h" - +#import "YMHUDTool.h" +#import "yinmeng_ios-Swift.h" +#import "RechargeStorage.h" @interface MewRechargeViewController () @property (nonatomic, strong) UIImageView *rechargeBgImageView; @property (nonatomic, strong) UIButton *backButton; @@ -65,16 +68,141 @@ self.rechageView.rechargeModels = list; } -- (void)requestIAPRechargeOrderSuccess:(NSString *)orderId chargeProdId:(NSString *)chargeProdId { +- (void)requestIAPRechargeOrderSuccess:(NSString *)orderId chargeProdId:(NSString *)chargeProdId uuid:(nonnull NSString *)uuid{ if (orderId.length > 0) { - self.orderId = orderId; + + self.orderId = orderId; + if (@available(iOS 15.0, *)) { + + MewPaymentAction *iap = [MewPaymentAction shared]; + [iap Mew_demandCommodityThingWithProductId:chargeProdId uuid:uuid completionHandler:^(NSError * _Nullable error) { + + }]; + @kWeakify(self); + iap.Mew_ConditionBlock = ^(enum MewStoreConditionResult state, NSDictionary * _Nullable result) { + @kStrongify(self); + + [self rechargeNewProcessStatus:state]; + switch (state) { + case MewStoreConditionResultMewVerifiedServer: + { + NSString *transactionId = result[@"transactionId"]; + + [self rechargeSuccess:transactionId]; + + } + break; + + default: + { + + + } + break; + } + }; +// + } else { + [YMHUDTool hideHUDInView:kWindow]; + [self showErrorToast:@"充值失败。当前仅支持运行iOS15及以上系统的手机进行充值,请升级系统版本后重试。"]; + } + + + + }else{ + + [YMHUDTool hideHUDInView:kWindow]; } } +///当前充值的状态 +- (void)rechargeNewProcessStatus:(MewStoreConditionResult)status { + + + if (status == MewStoreConditionResultMewPay || status == MewStoreConditionResultMewStart || status == MewStoreConditionResultMewVerifiedServer) { + + }else if (status == MewStoreConditionResultMewUnowned) { + [YMHUDTool hideHUDInView:kWindow]; + [self showErrorToast:@"出现未知错误,请重新尝试"]; + }else{ + [YMHUDTool hideHUDInView:kWindow]; + [self showErrorToast:@"购买失败"]; + } +} +///充值成功回调id +- (void)rechargeSuccess:(NSString *)transactionIdentifier { + ///保存唯一凭证 + [self saveRechageReciptWithTransactionIdentifier:transactionIdentifier]; + ///二次验证 + [self.presenter checkReceiptWithOrderId:self.orderId transcationId:transactionIdentifier errorToast:YES]; +} +///二次校验成功 +- (void)checkReceiptSuccess:(NSString *)transcationId { + [YMHUDTool hideHUDInView:kWindow]; + if (@available(iOS 15.0, *)) { + MewPaymentAction *iap = [MewPaymentAction shared]; + [iap Mew_verifyBusinessAccomplishWithTransaction:[NSString stringWithFormat:@"%@",transcationId] completionHandler:^{ + + }]; + } + [self deleteRechageReciptWithTransactionIdentifier:transcationId]; + self.orderId = nil; + //5秒之后如果没有收到开通成功的云信消息,则请求一次用户信息 + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + + ///刷新一下用户的信息 + [self.presenter getUserWalletInfo]; + + }); +} +///二次校验失败 +- (void)checkReceiptFailWithCode:(NSInteger)code transcationId:(NSString *)transcationId{ + [YMHUDTool hideHUDInView:kWindow]; + if(code == 1444){ + if (@available(iOS 15.0, *)) { + MewPaymentAction *iap = [MewPaymentAction shared]; + [iap Mew_verifyBusinessAccomplishWithTransaction:[NSString stringWithFormat:@"%@",transcationId] completionHandler:^{ + + }]; + } + [self deleteRechageReciptWithTransactionIdentifier:transcationId]; + } +} +///删除本地保存的 +- (void)deleteRechageReciptWithTransactionIdentifier:(NSString *)transactionIdentifier { + NSString * uid = [AccountInfoStorage instance].getUid; + BOOL deleteSuccess = [RechargeStorage delegateTranscationId:transactionIdentifier uid:uid]; + if (deleteSuccess) { +#warning to do 保存失败 需要埋点 + } +} +///充值成功之后保存订单到钥匙串 +- (void)saveRechageReciptWithTransactionIdentifier:(NSString *)transactionIdentifier { + NSData *receipt = [NSData dataWithContentsOfURL:[[NSBundle mainBundle] appStoreReceiptURL]]; + NSString *encodeStr = [receipt base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed]; + NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; + if(transactionIdentifier != nil){ + [dictionary setObject:transactionIdentifier forKey:@"transcationId"]; + } + if(encodeStr != nil){ + [dictionary setObject:encodeStr forKey:@"recipt"]; + } + if(self.orderId != nil){ + [dictionary setObject:self.orderId forKey:@"orderId"]; + } + if(dictionary.allKeys.count == 0)return; + NSString *reciptJson = [dictionary toJSONString]; + NSString * uid = [AccountInfoStorage instance].getUid; + BOOL saveSuccess = [RechargeStorage saveTranscationId:transactionIdentifier recipt:reciptJson uid:uid]; + if (!saveSuccess) { +#warning to do 保存失败 需要埋点 + } +} #pragma mark - MewRechargeSubViewDelegate - (void)didSelectMewRechargeChargeProdId:(NSString *)chargeProdId { + [YMHUDTool showLoadingInView:kWindow]; [self.presenter requestIAPRechargeOrderWithChargeProdId:chargeProdId]; } diff --git a/yinmeng-ios/yinmeng-ios/Main/YinMeng/Mine/Api/Api+Mine.h b/yinmeng-ios/yinmeng-ios/Main/YinMeng/Mine/Api/Api+Mine.h index e32735d..2a9c406 100644 --- a/yinmeng-ios/yinmeng-ios/Main/YinMeng/Mine/Api/Api+Mine.h +++ b/yinmeng-ios/yinmeng-ios/Main/YinMeng/Mine/Api/Api+Mine.h @@ -88,6 +88,14 @@ NS_ASSUME_NONNULL_BEGIN transcationId:(NSString *)transcationId uid:(NSString *)uid ticket:(NSString *)ticket; +/// 验证凭据 +/// @param completion 完成 +/// @param chooseEnv @"true" +/// @param chargeRecordId 服务端生成的订单编号 +/// @param transcationId 内购的唯一标识符 +/// @param uid 用户uid +/// @param ticket ticket ++ (void)checkReceipt:(HttpRequestHelperCompletion)completion chooseEnv:(NSString *)chooseEnv chargeRecordId:(NSString *)chargeRecordId transcationId:(NSString *)transcationId uid:(NSString *)uid ticket:(NSString *)ticket; @end NS_ASSUME_NONNULL_END diff --git a/yinmeng-ios/yinmeng-ios/Main/YinMeng/Mine/Api/Api+Mine.m b/yinmeng-ios/yinmeng-ios/Main/YinMeng/Mine/Api/Api+Mine.m index 956bf6a..d4299ae 100644 --- a/yinmeng-ios/yinmeng-ios/Main/YinMeng/Mine/Api/Api+Mine.m +++ b/yinmeng-ios/yinmeng-ios/Main/YinMeng/Mine/Api/Api+Mine.m @@ -81,7 +81,7 @@ /// @param deviceInfo uuid /// @param clientIp ip地址 + (void)requestIAPRecharge:(HttpRequestHelperCompletion)completion chargeProdId:(NSString *)chargeProdId uid:(NSString *)uid ticket:(NSString *)ticket deviceInfo:(NSString *)deviceInfo clientIp:(NSString *)clientIp { - [self makeRequest:@"order/placeV2" method:HttpRequestHelperMethodPOST completion:completion, __FUNCTION__,chargeProdId, uid, ticket, deviceInfo, clientIp, nil]; + [self makeRequest:[NSString stringWithFormat:@"%@/%@",@"storeKitV2",@"placeOrder"] method:HttpRequestHelperMethodPOST completion:completion, __FUNCTION__,chargeProdId, uid, ticket, deviceInfo, clientIp, nil]; } @@ -96,5 +96,15 @@ + (void)checkReceipt:(HttpRequestHelperCompletion)completion receipt:(NSString *)receipt chooseEnv:(NSString *)chooseEnv chargeRecordId:(NSString *)chargeRecordId transcationId:(NSString *)transcationId uid:(NSString *)uid ticket:(NSString *)ticket { [self makeRequest:@"verify/setiap" method:HttpRequestHelperMethodPOST completion:completion, __FUNCTION__,receipt, chooseEnv, chargeRecordId, transcationId, uid, ticket, nil]; } +/// 验证凭据 +/// @param completion 完成 +/// @param chooseEnv @"true" +/// @param chargeRecordId 服务端生成的订单编号 +/// @param transcationId 内购的唯一标识符 +/// @param uid 用户uid +/// @param ticket ticket ++ (void)checkReceipt:(HttpRequestHelperCompletion)completion chooseEnv:(NSString *)chooseEnv chargeRecordId:(NSString *)chargeRecordId transcationId:(NSString *)transcationId uid:(NSString *)uid ticket:(NSString *)ticket { + [self makeRequest:[NSString stringWithFormat:@"%@/%@",@"storeKitV2",@"verifyOrder"] method:HttpRequestHelperMethodPOST completion:completion, __FUNCTION__, chooseEnv, chargeRecordId, transcationId, uid, ticket, nil]; +} @end diff --git a/yinmeng-ios/yinmeng-ios/Main/YinMeng/Mine/Presenter/MineRechargePresenter.h b/yinmeng-ios/yinmeng-ios/Main/YinMeng/Mine/Presenter/MineRechargePresenter.h index f3af315..89bccc5 100644 --- a/yinmeng-ios/yinmeng-ios/Main/YinMeng/Mine/Presenter/MineRechargePresenter.h +++ b/yinmeng-ios/yinmeng-ios/Main/YinMeng/Mine/Presenter/MineRechargePresenter.h @@ -30,7 +30,10 @@ NS_ASSUME_NONNULL_BEGIN /// 批量验证内购掉单 /// @param transcations 凭据的数组 - (void)checkTranscationIds:(NSArray *)transcations; - +/// 充值成功二次验证 +/// @param orderId 订单编号 +/// @param transcationId 商品id +- (void)checkReceiptWithOrderId:(NSString *)orderId transcationId:(NSString *)transcationId errorToast:(BOOL)errorToast; @end NS_ASSUME_NONNULL_END diff --git a/yinmeng-ios/yinmeng-ios/Main/YinMeng/Mine/Presenter/MineRechargePresenter.m b/yinmeng-ios/yinmeng-ios/Main/YinMeng/Mine/Presenter/MineRechargePresenter.m index 1778d75..e49ba34 100644 --- a/yinmeng-ios/yinmeng-ios/Main/YinMeng/Mine/Presenter/MineRechargePresenter.m +++ b/yinmeng-ios/yinmeng-ios/Main/YinMeng/Mine/Presenter/MineRechargePresenter.m @@ -48,12 +48,28 @@ NSString * clientIp= [YYUtility ipAddress]; [Api requestIAPRecharge:[self createHttpCompletion:^(BaseModel * _Nonnull data) { NSString *orderId = (NSString *)data.data[@"recordId"]; - [[self getView] requestIAPRechargeOrderSuccess:orderId chargeProdId:chargeProdId]; + NSString *mew_uuid = (NSString *)data.data[@"appAccountToken"]; + [[self getView] requestIAPRechargeOrderSuccess:orderId chargeProdId:chargeProdId uuid:mew_uuid]; } fail:^(NSInteger code, NSString * _Nullable msg) { + if(code != 50000){ + [[self getView]showErrorToast:msg]; + } [[self getView] requestIAPRechargeOrderFail]; }] chargeProdId:chargeProdId uid:uid ticket:ticket deviceInfo:deviceInfo clientIp:clientIp]; } +/// 充值成功二次验证 +/// @param orderId 订单编号 +/// @param transcationId 商品id +- (void)checkReceiptWithOrderId:(NSString *)orderId transcationId:(NSString *)transcationId errorToast:(BOOL)errorToast{ + NSString * uid = [AccountInfoStorage instance].getUid; + NSString * ticket = [AccountInfoStorage instance].getTicket; + [Api checkReceipt:[self createHttpCompletion:^(BaseModel * _Nonnull data) { + [[self getView] checkReceiptSuccess:transcationId]; + }fail:^(NSInteger code, NSString * _Nullable msg) { + [[self getView]checkReceiptFailWithCode:code transcationId:transcationId]; + } showLoading:NO errorToast:errorToast] chooseEnv:@"true" chargeRecordId:orderId transcationId:transcationId uid:uid ticket:ticket]; +} /// 充值成功二次验证 /// @param receipt 凭据 diff --git a/yinmeng-ios/yinmeng-ios/Main/YinMeng/Mine/Protocol/MineRechargeProtocol.h b/yinmeng-ios/yinmeng-ios/Main/YinMeng/Mine/Protocol/MineRechargeProtocol.h index c03ef8a..cedaad3 100644 --- a/yinmeng-ios/yinmeng-ios/Main/YinMeng/Mine/Protocol/MineRechargeProtocol.h +++ b/yinmeng-ios/yinmeng-ios/Main/YinMeng/Mine/Protocol/MineRechargeProtocol.h @@ -10,18 +10,21 @@ NS_ASSUME_NONNULL_BEGIN @protocol MineRechargeProtocol +@optional ///请求钱包余额信息 - (void)getUserWalletInfo:(WalletInfoModel *)balanceInfo; ///请求充值列表成功 - (void)requestRechargeListSucccess:(NSArray *)list; ///请求充值id的状态成功 -- (void)requestIAPRechargeOrderSuccess:(NSString *)orderId chargeProdId:(NSString *)chargeProdId; +- (void)requestIAPRechargeOrderSuccess:(NSString *)orderId chargeProdId:(NSString *)chargeProdId uuid:(NSString *)uuid; ///请求充值账单失败 - (void)requestIAPRechargeOrderFail; ///二次校验成功 - (void)checkReceiptSuccess:(NSString *)transcationId; ///批量验证凭据成功 - (void)checkTranscationIdsSuccess; +///二次校验失败 +- (void)checkReceiptFailWithCode:(NSInteger)code transcationId:(NSString *)transcationId; @end NS_ASSUME_NONNULL_END