www.2527.com_澳门新葡8455手机版_新京葡娱乐场网址_
做最好的网站

iOS内购你看本人就够了,及掉单难点的正确姿势

2019-09-11 07:15 来源:未知

在中坑已经说的比非常多了,但只埋了八分之四.今后自己把自家发觉到的坑都埋完.没看过的出远门左转,那俩要同步看..抱歉篇幅难点

内购坑多,开首(提出先去看些入门的)

不久前开荒一个档案的次序涉嫌到内购, 也境遇过一些难题. 这里拿出去分享一下, 制止某个人走弯路.开端先聊一聊这段日子苹果关于前年新的稽核机制和喧嚣的微信和苹果的撕逼

↓防懵逼不可不看

iOS内购你看本人就够了

@import StoreKit;#import "IAPManager.h"@implementation IAPManager  (instancetype)sharedInstance{ static id sharedInstance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedInstance = [[IAPManager alloc] init]; }); return sharedInstance;}单利的入口,传进来一个后台得到的商品ID- payBtnPressed:(NSString *)product_id {//这传进来个商品id,lzc if ([SKPaymentQueue canMakePayments])//是否允许应用内付费 { SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithObject:product_id]]; request.delegate = self; [request start]; }else DLog(@"用户不允许内购");//提示框}// 查询成功后的回调- productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{ //这里菊花消失 DLog; NSArray *products = response.products; if (products.count != 0) { self.product = products[0]; SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:self.product];//lzc 改 payment.applicationUsername = @.stringValue;//充值用户的id,也就是uid. [[SKPaymentQueue defaultQueue] addPayment:payment];//发起购买当然还有监听,这个东西写在程序入口比较好,至于为何下次讲 } if (products.count == 0){ DLog(@"无法获取商品"); }}//查询失败后的回调- request:(SKRequest *)request didFailWithError:(NSError *)error { //菊花消失 DLog(@"请求苹果服务器失败%@",[error localizedDescription]);}

1. 2017新的查处机制:

  • ipv6: 使用国内Ali云的app上架, 大都会遇到ipv6被拒的邮件:应用方案:方案1. 服务端化解: �配置Ali云ECS协理IPv6, 增多AAAA解析方案2. 客商端解决: 手提式有线电话机端配置ipv6处境测量检验, 录像APP内的操作录像, 上传来YouTobe, 将网站发送给检查核对人士就可以通过核实 (ps: 录制时候确定要录像应用程式所在的互连网情况: 设置中->有线网络->DNS: 2003:2:0:aab1: :: 1 ,DNS为这种格式则为ipv6)

  • 内购:说一说那么些类型内购遗闻体:a. 首先做那么些项目标时候, 大家充钱虚构币方案定的是: 后台做贰个按键, app在审查时期走苹果内购, 在上线后, 走微信和支付宝支付, 并向低版本包容. 到达绕过苹果核查的指标. 结果被拒了, 邮件中提到了支付宝, 当时很懵逼, 就留给了丰硕的联系格局和苹果交流, 第二天苹果打来电话: 说内购的同一时候不可以动用第三方支付. 因此看来: 第三方支付的相干相关代码或SDK被围观到了. 遂移除掉, 只使用内购格局b. 核实时期, 苹果发来一封邮件大约意思是问: 你们确定内购的最高价格是你们期望的吗? 回复以后才可以继续审核, 这里自身的通晓是: 大家的内购的最高价格定得非常高149美元的那一档, 所以苹果要承认一下, 经过复原邮件表达了弹指间这个最高价格确定是我们自己定的最高价格, 没有错误, 第二天苹果又恢复生机了核准, 形成了核查中...c. app被拒后, 内购项目改为了需要开发人员操作, 盗图一张:

计算机编程 1供给开垦人士操作

此刻一般只须求步入需要开发人员操作的内购项目中, 修改一下描述, 重新提交就可以, 然后再行提交app. (ps: 一般这里自个儿只是将陈述中丰裕或删除空格, 就足以重复提交了)

d. 关于项目中: app内购商品重回列表为空, 再次回到的都是行不通产品即: [response.products count]始终为0, [response.invalidProductIdentifiers] 有值这些的来头是: 左券、税务和银行业务中必需通过才方可:

计算机编程 2磋商、税务和银行业务

↑防懵逼必须要看

归来看了看貌似已经得以化解全数标题了...额

为此要把起先监听写在程序入口,在程序挂起时移除监听.若是有未成功的订单他就能一贯走

- paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions

一般情形下都是购置成功后并没有终止订单的坑,所以她会走验证方法

if (self.cash != nil)这句话在这就起作用了,所以他会走[self checkUnTestReceipt];//从本地取凭证验证去 }

因为您大概出现持续多个未评释的订单,擦,非常的惨

- checkUnTestReceipt{ NSArray *payAry =从本地去取存凭证的数组,我就不告诉你怎么取,咬我 if (!payAry || payAry.count == 0) { return; } for (NSDictionary *dic in payAry) { [self untestReceiptByTime:dic];//把本地的dic都去验证了 }}

untestReceiptByTime:这办法只是多了个停业后的回传,和注明措施有少数例外

- untestReceiptByTime:(NSDictionary *)dic//第一次访问服务器失败了又一次请求,多了个定时的请求{ WEAKSELF; [[YLBNetWorkManager sharedInstance]postJsonData:dic url: e successBlock:^(id responseBody) { [weakSelf removeDicFromPayAry:dic];//移除,不懂得看一 } failureBlock:^(NSString *error) { DLog(@"%@和自己服务器失败22",error); [self untestReceiptByTime:dic];//最好一段时间后再验证,用GCD}

程序入口监听开始[[SKPaymentQueue defaultQueue] addTransactionObserver:self];DLog(@"我一进来就看看本地漏单"); [[IAPManager sharedInstance]checkUnTestReceipt];//一进程序看看有没有漏单双管齐下,你慢慢漏吧当然还得在程序入口新建本地数组,用NSUserDefaults ,删除添加都是他,这我就不写了,你全部都复制粘贴也没啥成就感.. 程序出口移除监听[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];

.

为了不往本地存本人是没少费力,不过小编太年轻,还是错了.纵然只要不甘休订单凭证就不会消亡,可是验证时还亟需任何(顾客Aid,不然你怎么驾驭何人买的.只怕下一次登入换客户B了,你只用了本地的凭证和B,擦,充错人)所以必得存本地.突然有个难题,那一个证据是怎么和订单一一对应的?何人知道说下,作者没事也研究下

一般不大概缓慢解决顾客购买后未表达不过换另一边手机了的丢单难题,额,你找客服吧.好烦..技巧有限,以往再说吧.稳重思忖好像还不是很完美.往后再补偿吧.也请大神们多指教吧最后自个儿要感激全数帮衬作者的人感谢您们五四的孝敬

...写到这里,算是把内购给写的略微通晓点了.笔者明天也就会驾驭到那些水平了,以往有啥会再补充.记得刚得到职责一脸懵逼头大.貌似大部分内购作品里都有本身浏览的身材吧.小编针扎本人在想要不要加密本地的事物,还会有唐巧说要禁止越狱的客商内购(他们难题太多,举例被红客威吓)

先去苹果的标准服务器验证,再次回到21007的话再去测量试验验证.因为苹果测量试验用的是测验服务器

上边正是监听结果了

2. 谈一谈微信和苹果的撕逼

新的查处左券将打赏列为了内购作者的见地和那些仁兄同样

//监听购买结果,每个状态下都要结束订单,否则就坑爹了- paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions {//当用户购买的操作有结果时,就会触发下面的回调函数, //菊花消失 DLog(@"来监听购买结果吧"); for (SKPaymentTransaction *transaction in transactions) { switch (transaction.transactionState) { case SKPaymentTransactionStatePurchased://交易成功 [self completeTransaction:transaction];//验证 //如果用户在这中间退出,咋办??不知道的看下一篇,坑坑坑都是坑 DLog; [[SKPaymentQueue defaultQueue] finishTransaction:transaction];//验证成功与否,咱们都注销交易,否则会出现虚假凭证信息一直验证不通过..每次进程序都得输入苹果账号的情况 break; case SKPaymentTransactionStateFailed: [self failedTransaction:transaction];//交易失败方法 break; case SKPaymentTransactionStateRestored://已经购买过该商品 DLog; [[SKPaymentQueue defaultQueue] finishTransaction:transaction];//消耗型不支持恢复,所以我就不写了 break; case SKPaymentTransactionStatePurchasing: DLog(@"已经在商品列表中");//菊花 break; case SKPaymentTransactionStateDeferred: DLog(@"最终状态未确定 "); break; default: break; } }}- failedTransaction:(SKPaymentTransaction *)transaction{ if(transaction.error.code != SKErrorPaymentCancelled) { NSLog; } else { NSLog(@"用户取消交易"); } [[SKPaymentQueue defaultQueue] finishTransaction: transaction];}

3. 摆龙门阵扯完了, 看一下如何做内购并处理掉单难题:

苹果官方提供的内购的不错姿势苹果这一文中证实两点:a. 在appdelegate中加上观望者, 在采办成功后交由给自身的服务器, 由自个儿服务器交由证据到苹果服务器验证精确后, 重回给客商端之后, 那笔交易才到位, 这时候再queue.finishTransaction(transaction), 假设这里面苹果的服务器还没回去结果 或者购买成功了,我们提交证据给本身服务器的时候网断掉了(钱空了, 不过设想货品未有到账, 丢单了), 则那笔交易都并未有完成, 方法queue.finishTransaction(transaction)都不曾调用, 所以再一次展开app的时候, 因为appdelegate中增多了观看者, 就能够再度调用func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction])方法

b. 苹果推荐步入内购项目表单页面包车型地铁时候先乞请appstore,依照重回的可出卖物品来开展呈现(可是洋洋app的做法皆以调用本身的接口取得物品价位列表举办展示, 但是我们无法明确大家团结的服务器再次来到的和苹果重返的两样), 这里极度抱歉的辨证一下: 大家的app也是根据自身服务器的api再次来到的数据突显的货色价位列表, 哈哈哈

c. 关于内购和服务端的接口参数, 大家设置为:

  1. 本次交易的客户的独一标示符(accountID):
  2. 贸易成功的凭据
  3. 本次交易的订单号
  4. 服务端也要管理重复诉求该接口的情景(不要老是诉求成功都给顾客加钱..)

注明: 客户的并世无两标示符的功效: 若是顾客购买成功, 但是将凭证给自身服务端的时候断掉了, 然后本身切换了账号, 下一次开荒app的时等候检查查测量检验, 大家须求以此象征符知道何人买的..不要将设想货币充错客户

计算机编程,ios7 苹果扩充了贰个属性applicationusername,SKMutablepayment的习性,所以客户在发起支付的时候能够钦赐顾客的username及本人生成的订单,那样客商再后一次获得回调的时候就领悟,此交易是哪个订单发起的了跟着产生交易。回调中获得username。

上代码:

import Foundationimport StoreKitenum InpurchaseError: Error { /// 没有内购许可 case noPermission /// 不存在该商品: 商品未在appstore中商品已经下架 case noExist /// 交易结果未成功 case failTransactions /// 交易成功但未找到成功的凭证 case noReceipt}typealias Order = (productIdentifiers: String, applicationUsername: String)class Inpurchase: NSObject, SKPaymentTransactionObserver, SKProductsRequestDelegate { static let `default` = Inpurchase() /// 掉单/未完成的订单回调 (凭证, 交易, 交易队列) var unFinishedTransaction: ((String, SKPaymentTransaction, SKPaymentQueue) -> ? private var sandBoxURLString = "https://sandbox.itunes.apple.com/verifyReceipt" private var buyURLString = "https://buy.itunes.apple.com/verifyReceipt" private var isComplete: Bool = true private var products: [SKProduct] = [] private var failBlock: ((InpurchaseError) -> ? /// 交易完成的回调 (凭证, 交易, 交易队列) private var receiptBlock: ((String, SKPaymentTransaction, SKPaymentQueue) -> ? private var successBlock:  -> Order)? private override init() { super.init() SKPaymentQueue.default().add } deinit { SKPaymentQueue.default().remove } /// 开始向Apple Store请求产品列表数据,并购买指定的产品,得到Apple Store的Receipt,失败回调 /// /// - Parameters: /// - productIdentifiers: 请求指定产品 /// - successBlock: 请求产品成功回调,这个时候可以返回需要购买的产品ID和用户的唯一标识,默认为不购买 /// - receiptBlock: 得到Apple Store的Receipt和transactionIdentifier,这个时候可以将数据传回后台或者自己去post到Apple Store /// - failBlock: 失败回调 func start(productIdentifiers: Set<String>, successBlock:  -> Order)? = nil, receiptBlock: ((String, SKPaymentTransaction, SKPaymentQueue) -> ? = nil, failBlock: ((InpurchaseError) -> ? = nil) { guard isComplete else { return } defer { isComplete = false } let request = SKProductsRequest(productIdentifiers: productIdentifiers) request.delegate = self request.start() self.successBlock = successBlock self.receiptBlock = receiptBlock self.failBlock = failBlock } //MARK: - SKProductsRequestDelegate func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) { products = response.products guard let order = successBlock?() else { return } buy } /// 购买给定的order的产品 private func buy(_ order: Order) { let p = products.first { $0.productIdentifier == order.productIdentifiers } guard let product = p else { failBlock?; return } guard SKPaymentQueue.canMakePayments() else { failBlock?(.noPermission); return } let payment = SKMutablePayment(product: product) /// 发起支付时候指定用户的username, 在掉单时候验证防止切换账号导致充值错误 payment.applicationUsername = order.applicationUsername SKPaymentQueue.default().add } //MARK: - SKPaymentTransactionObserver func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) { for transaction in transactions { switch transaction.transactionState { case .purchased: // appStoreReceiptURL iOS7.0增加的,购买交易完成后,会将凭据存放在该地址 guard let receiptUrl = Bundle.main.appStoreReceiptURL, let receiptData = NSData(contentsOf: receiptUrl) else { failBlock?(.noReceipt);return } let receiptString = receiptData.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0)) if let receiptBlock = receiptBlock { receiptBlock(receiptString, transaction, queue) }else{ // app启动时恢复购买记录 unFinishedTransaction?(receiptString, transaction, queue) } isComplete = true case .failed: failBlock?(.failTransactions) queue.finishTransaction(transaction) isComplete = true case .restored: // 购买过 对于购买过的商品, 回复购买的逻辑 queue.finishTransaction(transaction) isComplete = true default: break } } }}

appdelegate中的监听使用形式:

appdelegate中: func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { Inpurchase.default.unFinishedTransaction = {(receipt, transaction, queue) in // 如果存在掉单情况就会走这里 let data = InpurchaseAPIData(accountID: transaction.payment.applicationUsername, //用户唯一标示 transactionID: transaction.transactionIdentifier, //交易流水 receiptData: receipt)// 凭证 LPNetworkManager.request(Router.verifyReceipt.showToast().loading(in: self.view).success {[weak self] in showToast // 记住一定要请求自己的服务器成功之后, 再移除此次交易 queue.finishTransaction(transaction) }.fail { print("向服务器发送凭证失败") } } return true }

点击购买的代码:

 // 点击购买 let productIdentifiers: Set<String> = ["a", "b", "c"] Inpurchase.default.start(productIdentifiers: productIdentifiers, successBlock: { () -> Order in return (productIdentifiers: "a", applicationUsername: "该用户的id或改用户的唯一标识符") }, receiptBlock: { (receipt, transaction, queue) in //交易成功返回了凭证 let data = InpurchaseAPIData(accountID: transaction.payment.applicationUsername, transactionID: transaction.transactionIdentifier, receiptData: receipt) LPNetworkManager.request(Router.verifyReceipt.showToast().loading(in: self.view).success {[weak self] in showToast // 记住一定要请求自己的服务器成功之后, 再移除此次交易 queue.finishTransaction(transaction) }.fail { print("向服务器发送凭证失败") } }, failBlock: {  in print

demo地址 能点个star也是极好的, 打不打赏无所谓, 能帮到你就好

还恐怕有一种实践措施, 个人并不引入, 因为太烦琐了:思路: 购买成功后在本地将订单的顾客, 凭证等音讯存款和储蓄到当地(UserDefaults, 数据库,keyChain等), 将凭证发送给本身服务器成功之后再移除此条交易记录, 每一回张开app的时候, 在地点扫描是或不是有未到位的订单, 循环境与发展送给自个儿的服务器实行二次验证

补充:

  1. 有关上线:错误做法: 上线调查的时候利用沙箱测验地方, 核查通过后, 手动发表上线, 上线后让服务器切换成苹果的标准测量检验地方

表明: 这种做法第三回上架能够应用, 不过到第四回迭代核实的时候, 苹果测量试验员使用的是沙盒情形, 可是大家服务器是标准情况, 会导致报错误码: 21007没有错的做法: 判定苹果正式验证服务器的归来code,若是是21007 表示环境不对,则再贰回三番五次测验服务器进行验证就可以.. (这一步骤即: 先决断苹果的情况, 依照苹果碰着切换沙盒地址依旧职业地址)

  1. 有关苹果一回证实重回的参数:服务端顾客端对苹果发送央求实行表达有时会回去多少个交易记录

表明: 苹果验证会再次回到: 一个未到位交易的数组(一般独有三个, 正是当前操作购买的那个), 要是有多个为成功的贸易,就能够回去多少个(这种情况相似是代码写的不对变成的), 服务端依照transactionIdentifier找到当前购买发售的贸易照旧取最终四个也是日前购入的交易来做剖断和验证....经过测量试验开采只要在脚出手提式无线电话机央求发掘并发多个未成功的交易, 则换别的一部无绳电话机和账号等, 依旧会回到那么些未产生的交易, 看来每一趟对货品进行购买, 苹果会把具备未到位的交易都回去(不管这些商品是别的顾客的依然任何手提式无线电话机的)

demo地址 能点个star也是极好的, 打不打赏无所谓, 能帮到你就好

下边正是和服务器验证凭证了,珍视坑在此间

- completeTransaction:(SKPaymentTransaction *)transaction{ if (self.cash != nil) { //self.cash这玩意我是点击商品后传进来的,所以通过它判断是不是漏单的,有他的话就走正常流程 DLog(@"购买成功验证订单"); NSData *data = [NSData dataWithContentsOfFile:[[[NSBundle mainBundle] appStoreReceiptURL] path]]; NSString *a = [data base64EncodedStringWithOptions:0]; NSLog(@"base64JSONString =---%@---",a);//得到凭证 NSDictionary * dic = @{@"uid":用户id, @"receipt":a, 还有后台要的其他东西,起码凭证和用户id是必须的吧}; DLog(@"验证信息%@",dic); //存起来 [self addDicToPayAry:dic];//先把这个信息存起到本地,这是你埋坑的第一步,有空再解释 [self testForServer:dic];//和后台去二次验证,这个很必要 }else { DLog(@"漏单流程从本地取凭证去验证"); [self checkUnTestReceipt];//从本地取凭证验证去,下篇分解 }//不是点击cell 进来的,也就是说上次订单没结束,日狗去吧}

服务器交互,只要服务器有反馈咱就把地面包车型地铁新闻移除,因为证实成功删了不荒谬,验证不成事验证凭证有假(当然也只怕苹果出题目,那我管不了了)删,咱那边服务器出毛病(大家后台说那么些场合是后台验证过了但是未有发商品,客户找客服),删.独有没和团结后台交互上的时候,大家就在地头找村过的凭据,有的话表明有没表达过的,咱继续验证.没的话(第1回也不会没,因为存过二遍不过就没删过)表明都证实过了.同理可得那个进度不管怎么都要截止订单.

- testForServer:(NSDictionary *)dic{ WEAKSELF; [[NetWorkManager sharedInstance]postJsonData:dic url:url successBlock:^(id responseBody) { DLog(@"支付成功%@",responseBody); if ([responseBody[@"code"]isEqualToNumber:@ { DLog(@"1验证成功完成交易OKOKOKOK"); if (weakSelf.paySuccessBlock){ weakSelf.paySuccessBlock();//告诉外边UI做处理 } [weakSelf removeDicFromPayAry:dic];//从本地移除凭证等信息dic } if (验证receipt-data失败) { DLog(@"1验证receipt-data失败"); [weakSelf removeDicFromPayAry:dic];//移除 } if  { [weakSelf removeDicFromPayAry:dic]; DLog(@"1你去找客服吧我们服务器有毛病"); } // DLog(@"我结束订单了-----"); } failureBlock:^(NSString *error) { DLog(@"%@和自己服务器交互失败11",error); [weakSelf checkUnTestReceipt];//和自己后台没联系上,所以要检查本地有没有存过的凭证,有的话继续验证.下篇详聊 }];}

字数难点,存凭证删凭证和重新证实凭证下一次说.还应该有订单没告竣的坑(客商买成功了苹果服务器不给力,客商急本性关应用了,订单还没甘休)等等,下回分解.

8.25更,来埋坑了,点进去↓iOS内购第二篇

倍感依然不太完善啊,先这么啊

TAG标签:
版权声明:本文由澳门新葡8455手机版发布于计算机编程,转载请注明出处:iOS内购你看本人就够了,及掉单难点的正确姿势