Restore Purchase : Non-Consumable

后端 未结 1 1139
鱼传尺愫
鱼传尺愫 2020-12-22 04:37

I have completed a small app where I have a non-consumable purchase option. It is on the App Store.

The purchase of the product runs OK. It\'s my Restore Purchase fu

相关标签:
1条回答
  • 2020-12-22 05:08

    Your codes looks pretty fine for the most part, although some parts seem to be from older tutorials . There is some changes you should make, one of them is that you need to call your unlockProduct function again.

    This is the code I use (Swift 3).

    /// Updated transactions
    func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
    
        for transaction in transactions {
             switch transaction.transactionState {
    
            case .purchasing:
                // Transaction is being added to the server queue.
    
            case .purchased:
                // Transaction is in queue, user has been charged.  Client should complete the transaction.
    
                defer {
                    queue.finishTransaction(transaction)
                }
    
                let productIdentifier = transaction.payment.productIdentifier
    
                unlockProduct(withIdentifier: productIdentifier)
    
            case .failed:
                // Transaction was cancelled or failed before being added to the server queue.
    
                defer {
                    queue.finishTransaction(transaction)
                }
    
                let errorCode = (transaction.error as? SKError)?.code
    
                if errorCode == .paymentCancelled {
                    print("Transaction failed - user cancelled payment")
                } else if errorCode == .paymentNotAllowed { // Will show alert automatically
                   print("Transaction failed - payments are not allowed")
                } else {
                    print("Transaction failed - other error")
                    // Show alert with localised error description 
                }
    
            case .restored:
                // Transaction was restored from user's purchase history.  Client should complete the transaction.
    
                defer {
                    queue.finishTransaction(transaction)
                }
    
                if let productIdentifier = transaction.original?.payment.productIdentifier {
                    unlockProduct(withIdentifier: productIdentifier)
                }
    
            case .deferred:
                // The transaction is in the queue, but its final status is pending external action 
                // e.g family member approval (FamilySharing). 
                // DO NOT freeze up app. Treate as if transaction has not started yet.
            }
        }
    }
    

    Than use the delegate methods to show the restore alert

    /// Restore finished
    func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
        guard queue.transactions.count != 0 else {
            // showAlert that nothing restored
            return
        }
    
        // show restore successful alert 
    }
    
    /// Restore failed
    func paymentQueue(_ queue: SKPaymentQueue, restoreCompletedTransactionsFailedWithError error: NSError) {
    
         /// handle the restore error if you need to. 
    }
    

    Unlock product is just a method I am sure you already have too.

      func unlockProduct(withIdentifier productIdentifier: String) {
           switch productIdentifier {
             /// unlock product for correct ID
         }
      }
    

    As a side note, you should move this line

     SKPaymentQueue.default().add(self)
    

    out of your restore and buy function and put it in viewDidLoad.

    Apple recommends you add the transaction observer as soon as your app launches and only remove it when your app is closed. A lot of tutorials unfortunately dont teach you this correctly. This way you unsure that any incomplete transactions e.g due to network error, will always resume correctly.

    https://developer.apple.com/library/content/technotes/tn2387/_index.html

    In my real projects my code for IAPs is in a Singleton class so I would actually using delegation to forward the unlockProduct method to my class that handles gameData. I can than also make sure the observer is added at app launch.

    Hope this helps

    0 讨论(0)
提交回复
热议问题