在iOS中,如果用户尝试购买IAP,并且我的服务器验证收据失败,那么正确的行为是什么?在沙箱中测试时,我只是收到大量弹出窗口要求输入密码。如果我打电话给finishTransaction,它会停止询问我的密码,但我相信这会导致用户在没有收到产品的情况下收费。
答案 0 :(得分:2)
我认为在进行应用内购买时的错误处理是较少谈论进行应用内购买的难度之一。这里有几个问题。例如:1)如果您正在验证设备上的购买收据(即> = iOS7样式的解密样式验证),并且验证失败,您应该怎么做? 2)如果您正在使用基于Web的服务器进行验证,但是失败了,您应该怎么做? 3)如果您将收据存储到您自己的基于Web的服务器,并且失败了,您应该怎么做?我会从我自己的应用程序中提出一些想法。很高兴看到其他人正在对这种错误处理做些什么。
1)如果您正在验证设备上的购买收据(即> = iOS7解密样式验证),并且验证失败,您应该怎么做?
Apple建议您在设备上进行初始验证失败时进行收据刷新(SKReceiptRefreshRequest)。但是如果刷新后的验证失败怎么办?您可以解释这种情况,因为存在严重错误(例如,用户以某种方式入侵您的应用程序以故意给您收到错误的收据),告诉用户购买失败且收据验证失败,并完成交易(SKPaymentQueue) finishTransaction :),永久失败。但是,我不喜欢这样的决定。也许这是我的自信,但是我的编程可能会出错。我想给用户更多机会。因此,我的解决方案是:1)告诉用户验证失败,2)将SKPaymentTransaction置于“保持”状态,3)调用finishTransaction。这种持有状态的想法是我的发明,没有任何由Apple提出或由Apple支持的。我已经创建了一个SKPaymentTransaction的可变子类,并且拥有这些对象的队列(称为SPASMutablePaymentTransaction),我将其存储(使用NSCoding)到NSUserDefaults中。几乎总是,这个队列是空的,但如果我得到验证失败,就像我在这里谈论的那样,我创建一个SPASMutablePaymentTransaction对象,复制SKPaymentTransaction信息(包括收据,例如,来自捆绑包),并保存该交易进入我的持有状态队列。我可以看到我的UI的一个(通常是隐藏的)部分,这可以允许用户重试持有状态事务。
复杂?对,一点。但是,由于我们正在与用户给你钱,我正在努力做到健壮。到目前为止,它似乎在测试中运行良好。我尚未收到用户对此(或分析)的任何反馈,但它已部署到应用商店。
2)如果您正在使用基于Web的服务器进行验证,但是失败了,您应该怎么做?
对我来说,这与上面的1)类似。您已尝试对收据进行验证,但失败了。您可以先尝试刷新收据(参见上文),然后重新尝试服务器验证。在我的应用程序中,因为我总是首先进行设备上的收据验证,所以我再次刷新收据是没有意义的(因为如果设备上的收据验证失败,我会这样做)。因此,我再次将收据置于“持有”状态(如上所述),并允许用户自行决定重试持有状态交易。
3)如果您将收据存储到您自己的基于Web的服务器上,并且失败了,您应该怎么做?
(a)据推测,绝对不应该是您永久不给用户购买的情况。这是您的服务失败。在我的情况下,这可能发生,例如,如果我在与服务器通信时遇到网络错误。我在这里做了一些不同的事情。我没有立即让用户访问他们的购买,我不会将交易置于“持有”状态。相反,我设置了一个计时器来重试将收据存储到我的服务器的过程。它会在几分钟后再次重试。我告诉用户我在做什么。我将收据数据存储到NSUserDefaults中(再次使用SPASMutablePaymentTransaction对象),因此即使应用程序崩溃/终止,也会在此重试中保留。当我成功将收据保存到我的服务器时,我授予用户访问权限的权限。
(b)在这种情况下,我做调用finishTransaction:我还没有让用户访问购买。我可以通过不调用finishTransaction来避免一些这些细节,只是让我的事务观察者再次处理这个过程(例如,当应用程序下次启动时),但用户可能不得不再次输入他们的Apple Id。我的想法是,这是我的问题(即,我没有将收据信息保存到我的服务器),所以我试图在封面下处理它。
答案 1 :(得分:0)
弹出UIAlertView。见下面的代码......
self.alert("Purchase Failed", Message: "Please Make Sure You are connected to the internet and try agian")
func alert(title:String, Message:String){
let actionSheet:UIAlertController = UIAlertController(title: "\(title)", message: "\(Message)", preferredStyle: UIAlertControllerStyle.Alert)
// for alert add .Alert instead of .Action Sheet
// start copy
let firstAlertAction:UIAlertAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler:
{
(alertAction:UIAlertAction!) in
// action when pressed
})
actionSheet.addAction(firstAlertAction)
// end copy
self.presentViewController(actionSheet, animated: true, completion: nil)
}