试图恢复消耗性 IAP 的沙箱

Sandbox trying to restore consumable IAP

我一直在尝试在 iOS 上测试一些消耗性 IAP,但我遇到了一个奇怪的错误。弹出一条警告文本:

"This In-App Purchase has already been bought. It will be restored for free. [Environment: Sandbox]"

我已经检查过,我确定我的 IAP 可以在 iTunesConnect 中使用。似乎我的验证过程以某种方式失败了,但我不希望出现此错误消息。有人有这方面的经验吗?

我不确定这是否是正确的操作,但是调用:

[[SKPaymentQueue defaultQueue] finishTransaction: transaction];

在重复交易中将它们清除。我怀疑由于在调试器中停止或其他原因,我从未在成功案例中调用过它。

可能你已经解决了你的问题,但我遇到了同样的问题并且有一些额外的解决方案:

对不起,我用的是Swift,不过我觉得这里可以理解。

首先,您的代码中有两个重要时刻,您必须涵盖:

class InAppPurchaseViewController: UIViewController ,  SKProductsRequestDelegate
, SKPaymentTransactionObserver //NOTE THIS!
{
 override func viewDidLoad() {
    super.viewDidLoad()
    //
    ...
    ...
    SKPaymentQueue.defaultQueue().addTransactionObserver(self)
    // here you create the observer
} 

//before you leave this ViewController you should delete this Observer.
//I had a bug which worked like this: when user leaved the IAPViewController without 
//buying something and then came back, viewDidLoad added one more Observer
// so App crashed on the attempt to buy
// for example:

@IBAction func exitTapped(sender: AnyObject) {    
    dismissViewControllerAnimated(true) { () -> Void in
        SKPaymentQueue.defaultQueue().removeTransactionObserver(self)
    }
}

添加观察者后,不要忘记完成 t运行saction:

func paymentQueue(queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
    for transaction in transactions {  
        switch transaction.transactionState {
        case .Restored:
            print("Restored")
            break
        case .Failed:
            print("Failed") 
        //FINISH TRANSACTION 
            SKPaymentQueue.defaultQueue().finishTransaction(transaction)
            break
        case .Purchasing:
            print("Purchasing")
            break
        case .Purchased:
        // DO your stuff here
        // ...
        // and FINISH TRANSACTION
           SKPaymentQueue.defaultQueue().finishTransaction(transaction)
            }
            break
        case .Deferred:
            print("Deferred")
            break
        }
    }
}

我在这里解决的另一个问题是:我忘记添加 finish t运行saction 代码行,所以我打开了 t运行saction with consumable IAP。

当我尝试通过这个 Sandbox 用户再次购买时,我收到消息 "This In-App Purchase has already been bought. It will be restored for free. [Environment: Sandbox]" 即使在我添加了 FinishT运行行动。我更改了沙箱用户。每次我 运行 应用程序时,应用程序都会提示 FirstSandBoxUser iTunes 密码。灾难...很烦人...

每次我尝试使用 FirstSandBoxUser 进行购买时,transaction.transactionState 都是.Purchasing,并且你无法在此状态下完成 t运行saction!

我做了一个假设,如果 t运行saction 没有完成,StoreKit 会把它当作非消耗品,所以我应该开始一个恢复过程,然后抓紧时间完成 t运行行动。这是代码:

func paymentQueue // see above
//....
case .Purchasing:
    print("Purchasing")
    SKPaymentQueue.defaultQueue().restoreCompletedTransactions()
    break
//...
}

func paymentQueueRestoreCompletedTransactionsFinished(queue: SKPaymentQueue) {
    for transaction in queue.transactions {
        queue.finishTransaction(transaction)
    }
}

运行这段代码一次就够了,我想,它会捕获所有"Purchasing"t运行saction,恢复它们,然后完成。

当然,在解决问题后删除此附加代码。

祝你好运!

当交易未在客户端上完成时会发生这种情况。

[[SKPaymentQueue defaultQueue] finishTransaction: transaction];

您应该在您的应用程序启动阶段注册一个 transactionObserver,以确保您会收到任何未完成交易的通知。您应该完成这些交易并做任何其他必要的事情来正确地将购买的产品交付给用户。

From apples programming guide(强调我的):

Register a transaction queue observer when your app is launched, as shown in Listing 5-1. Make sure that the observer is ready to handle a transaction at any time, not just after you add a transaction to the queue. For example, consider the case of a user buying something in your app right before going into a tunnel. Your app isn’t able to deliver the purchased content because there’s no network connection. The next time your app is launched, StoreKit calls your transaction queue observer again and delivers the purchased content at that time. Similarly, if your app fails to mark a transaction as finished, StoreKit calls the observer every time your app is launched until the transaction is properly finished.

他们推荐以下代码来注册观察者:

- (BOOL)application:(UIApplication *)application
 didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    /* ... */

    [[SKPaymentQueue defaultQueue] addTransactionObserver:observer];
}