SKPaymentTransactionObserver; multiple repeated transactions - ios

I got an app where I used the SKPayment, SKPaymentTransactionObserver and so on. However, everything is going fine with the first payment, but whenever I select any other thing I want to purchase, it starts purchasing again all the things I already purchased with the new thing at the end. Tried looking it up on internet but no luck.
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
transactions.forEach( {
switch $0.transactionState {
case .purchasing:
print("purchasing item 1")
case .purchased:
print("purchased item 1")
SKPaymentQueue.default().finishTransaction($0)
downloadFileButton.setTitle("Download", for: .normal)
let userId = Auth.auth().currentUser?.uid
let childUpdate = ["item1": true]
Database.database().reference().child("users").child(userId!).child("purchase_status").updateChildValues(childUpdate)
case .failed:
print("user cancelled the sheet or an error occured")
SKPaymentQueue.default().finishTransaction($0)
case .restored:
break
case .deferred:
break
#unknown default:
break
}
})
}
I got this same code everywhere I can purchase something, but except for little changes like "item 2". When I purchase lets say 6 items in order, with the sixth purchase the console looks like this:
purchasing item 1
purchasing item 2
purchasing item 3
purchasing item 4
purchasing item 5
purchasing item 6
// After purchase:
purchased item 1
purchased item 2
purchased item 3
purchased item 4
purchased item 5
purchased item 6
EDIT: I am a beginner so any explanation, suggestions and corrections will be awesome :)

Related

Listen to when the in-app purchase window opens on iOS

SKPaymentQueue.default().add(payment)
I'm starting an in-app purchase with. But I think the purchase window sometimes opens late. Is there a method, delegate method to listen for the situation where this screen opens?
I researched this but I could not reach a conclusion, does anyone know?
You can use the delegate below to handle it (ex. show/hide progress view) regarding to SKPaymentTransactionState:
// Handle transaction status after you call .add(payment).
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction:AnyObject in transactions {
if let trans = transaction as? SKPaymentTransaction {
switch trans.transactionState {
case .purchased:
SKPaymentQueue.default().finishTransaction(trans)
// purchased..
case .failed:
SKPaymentQueue.default().finishTransaction(trans)
// failed ..
case .restored:
SKPaymentQueue.default().finishTransaction(trans)
// restored
default:
break
}
}
}
}
I suggest using a very well made (and easy to user) library for handling in-app purchases. It is called SwiftyStoreKit. enter link description here We use it in many projects and it has nice closures while handling purchasing. You can put your UI blocking progress indicator just before calling it's methods and remove when the closure returns with a result.

iOS: A default alert will be showed after purchasing a product in-app purchase

I am struggling with a problem that after purchasing a product successfully and a system alert ("You're all set, Your purchase was successful") will be showed as well. The problem is that I am not able to get a callback or any event to know the system alert was dismissed in order to display a custom popup
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction:AnyObject in transactions {
if let trans = transaction as? SKPaymentTransaction {
switch trans.transactionState {
case .purchased:
// the default alert will be showed after purchasing a product successfully
break
case .failed:
break
case .restored:
break
default:
break
}
}
}
Note that: the system has prompted won't get a callback
I wanna show the below photo after users tapped the "Ok" button from the default alert. I am not able to get a callback or any event to know the default alert was dismissed in order to display my custom popup
Thank you so much for helping me

Switching sandbox user to test Auto Renewable Subscription iOS

I have added auto renewable subscription to my iOS app. I have used a sandbox user to test the app and it worked fine. After that I logged out of the previous sandbox account and logged in with another sandbox account. Now my app sends receipts with two original transaction ids to validate from the server. It seems like my previous sandbox user data has not completely wiped off. Does anyone else experiencing the same issue? Any thoughts on this?
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction: AnyObject in transactions {
if let trans = transaction as? SKPaymentTransaction {
switch trans.transactionState {
case .purchased:
SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
if let completion = self.purchaseProductcompletion {
completion(PurchaseHandlerStatus.purchased, self.productToPurchase, trans)
}
case .failed:
SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
let errorCode = (trans.error as? SKError)?.code
if (errorCode == .paymentCancelled) {
if let completion = self.purchaseProductcompletion {
completion(PurchaseHandlerStatus.purchaseCancelled, self.productToPurchase, trans)
}
} else {
if let completion = self.purchaseProductcompletion {
completion(PurchaseHandlerStatus.purchaseFailed, self.productToPurchase, trans)
}
}
case .restored:
SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
totalRestoredPurchases += 1
default:
break
}
}
}
}
To avoid this annoying issue, you should finish all pending transactions from the old sandbox account.
When you debug in-app purchases and/or change Apple ID too often, some transactions may stay in the queue (for example, if you broke execution until transaction is finished). And these transactions will try to finish at every next app launch. In this case you may see a system alert prompting to enter your Apple ID password and this may also lead to sandbox receipt will not immediately update/ may not match logged in sandbox account.
To fix this issue, make sure you finish all pending transactions when launching the app.
SKPaymentQueue.default().finishTransaction(transaction)

Swift IAP restoreCompletedTransactions restores even non purchased items

I have 4 non-consumables in my application and the purchasing of these works without any problems.
However, I have created a restorePurchases button which restores all products regardless of whether the user has purchased them or not. I've tested this on 4 different sand box test users, and the results are consistent (i.e. for a test user who has never bought the non consumables, clicking 'restore' restores all products)
My restore code looks like:
#IBAction func restorePurchases(sender: UIButton) {
SKPaymentQueue.defaultQueue().addTransactionObserver(self)
SKPaymentQueue.defaultQueue().restoreCompletedTransactions()
}
func paymentQueueRestoreCompletedTransactionsFinished(queue: SKPaymentQueue) {
print("transactions restored")
for transaction in queue.transactions {
let t: SKPaymentTransaction = transaction
let prodID = t.payment.productIdentifier as String
switch prodID {
case "productAbc1":
defaults.setBool(true , forKey: "productAbc1")
case "productAbc2":
defaults.setBool(true , forKey: "productAbc2")
case "productAbc3":
defaults.setBool(true , forKey: "productAbc3")
case "productAbc3":
defaults.setBool(true , forKey: "productAbc4")
default:
print("IAP not setup; enable it")
}
}
}
Do I need to be checking additional parameters in paymentQueueRestoreCompletedTransactionsFinished? Comparing my code to many other similar questions / IAP examples, it looks pretty much the same. Is this an issue with the sandbox environment or code?
queue.finishTransaction(transaction)
for each case was missing; meaning they were never clearing from the queue. So the next time I attempted to restore the purchases, all previous transactions were found (and restoring).
Adding the above line of code resolved the issue.

In App Purchase - when trying to buy consumable product again - this in-app purchase has already been bought

I have consumable In-App-Purchase product in my iTunes connect, and when I'm trying to buy it twice (on my iPhone), it tells me that I already bought it. But this is the whole point of consumables, that users can buy them over and over. Any suggestions?
This happens if you haven't marked the transaction for the original purchase as finished, which you should do in your - (void)paymentQueue:(SKPaymentQueue*)queue updatedTransactions:(NSArray*)transactions method after you've successfully processed the purchase.
The method you need to call is [[SKPaymentQueue defaultQueue] finishTransaction:transaction].
public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction in transactions {
switch (transaction.transactionState) {
case .purchased:
complete(transaction: transaction)
break
case .failed:
fail(transaction: transaction)
break
case .restored:
restore(transaction: transaction)
break
case .deferred:
break
case .purchasing:
break
}
}
}

Resources