Swift Store Kit In app Purchase transactionState - ios

I have make an app that uses store kit to add some character ..
I have added the paymentQueue function but when the pop-up is displayed and I press Cancel, the transactionState is .Completed
In this method if a user cancels the transaction he obtains the character without paying.
func paymentQueue(queue: SKPaymentQueue!, updatedTransactions transactions: [AnyObject]!) {
println("add paymnet")
for transaction:AnyObject in transactions {
var trans = transaction as SKPaymentTransaction
switch trans.transactionState {
case .Purchased:
println("Purchase Successful")
SKPaymentQueue.defaultQueue().finishTransaction(transaction as SKPaymentTransaction)
queue.finishTransaction(trans)
break;
case .Failed:
println("Buy Cancelled")
SKPaymentQueue.defaultQueue().finishTransaction(transaction as SKPaymentTransaction)
queue.finishTransaction(trans)
break;
default:
println("default")
break;
}
}
}
This is the link of the tutorial i followed
Function tested on real device and sand box user.

Related

iOS Auto Renewal Subscriptions - Alert confirmation callback

How can I check when the user tap the accept button in the last Alert showed by Apple after the user make a subscription purchase.
I have the following code
public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction in transactions {
switch transaction.transactionState {
case .purchased:
complete(transaction: transaction)
case .failed:
fail(transaction: transaction)
case .restored:
restore(transaction: transaction)
case .deferred:
break
case .purchasing:
break
default:
break
}
}
}
But the complete method is called before user tap on the Alert.
How can I check the Alert callback ?

iOS IAP paymentQueue:updatedTransactions

The SKPaymentTransactionObserver.paymentQueue:updatedTransactions returns a array of transactions. When I make a payment, how do I know which transaction is the payment I made? Does it always return one transaction when I make a payment?
Meanwhile, this observer function is also called when restoring transactions. So, what is the best practice to handle the updatedTransactions?
BTW, my subscription product is a auto-renew subscription.
iterate through the loop of the transactions and check for each transaction.
public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction in transactions {
switch (transaction.transactionState) {
case .purchased:
completeTransaction(transaction: transaction)
break
case .failed:
failedTransaction(transaction: transaction)
break
case .restored:
restoreTransaction(transaction: transaction)
break
case .deferred:
// TODO show user that is waiting for approval
break
case .purchasing:
break
}
}
}
private func completeTransaction(transaction: SKPaymentTransaction) {
print("completeTransaction...")
deliverPurchaseForIdentifier(identifier: transaction.payment.productIdentifier)
SKPaymentQueue.default().finishTransaction(transaction)
}
private func restoreTransaction(transaction: SKPaymentTransaction) {
guard let productIdentifier = transaction.original?.payment.productIdentifier else { return }
print("restoreTransaction... \(productIdentifier)")
deliverPurchaseForIdentifier(identifier: productIdentifier)
SKPaymentQueue.default().finishTransaction(transaction)
}
private func failedTransaction(transaction: SKPaymentTransaction) {
if let error = transaction.error as NSError? {
if error.domain == SKErrorDomain {
// handle all possible errors
switch (error.code) {
case SKError.unknown.rawValue:
print("Unknown error")
BaseViewController.currentViewController?.Alert(title: MFErrors.purchaseFaild.messgae.title, msg: MFErrors.purchaseFaild.messgae.body)
case SKError.clientInvalid.rawValue:
print("client is not allowed to issue the request")
BaseViewController.currentViewController?.Alert(title: MFErrors.accountNotAllowedToMakePurchase.messgae.title, msg: MFErrors.accountNotAllowedToMakePurchase.messgae.body)
case SKError.paymentCancelled.rawValue:
print("user cancelled the request")
case SKError.paymentInvalid.rawValue:
print("purchase identifier was invalid")
case SKError.paymentNotAllowed.rawValue:
print("this device is not allowed to make the payment")
BaseViewController.currentViewController?.Alert(title: MFErrors.purchaseFaild.messgae.title, msg: MFErrors.purchaseFaild.messgae.body)
default:
break;
}
}
ProgressViewManager.shared.hide()
}
SKPaymentQueue.default().finishTransaction(transaction)
}
private func deliverPurchaseForIdentifier(identifier: String?) {
guard let identifier = identifier else { return }
//Check if this transactions is a subscription
//SubscriptionsProductIdentifiers is an array of subscriptions product ids you sent to the app store to get SKProducts
//If subscription
if SubscriptionsProductIdentifiers.contains(identifier) {
}else{
//If non-consumable, consumables etc...
}
}
here's complete Store Manager in my previous answer:
How to handle shouldAddStorePayment for In-App Purchases in iOS 11?

IAPs are being restoring when they have not yet been purchased yet

When the user calls restorePurchases(), the non-consumable com.premium is restored even thought they do not own it.
Here are the functions that are responsible for the restoring purchases and purchasing IAPs. This is only an issue with non-consumable IAPs.
There is no issue with purchasing. And if the user attempts to purchase an IAP that they already have, it is simply restored. Thank you for looking in to this.
func restorePurchases() {
SKPaymentQueue.default().add(self)
SKPaymentQueue.default().restoreCompletedTransactions()
}
func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
print("transactions restored")
for transaction in queue.transactions {
let t: SKPaymentTransaction = transaction
let prodID = t.payment.productIdentifier as String
print("starting restoring")
switch prodID {
case "com.premium":
print("restoring ads")
removeAds()
case "com.cash":
print("we dont restore coins")
case "com.level":
print("we dont restore levels")
default:
print("can't restore")
}
}
Here is my payment queue also.
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
print("add paymnet")
for transaction:AnyObject in transactions {
let trans = transaction as! SKPaymentTransaction
print(trans.error)
switch trans.transactionState {
case .purchased:
print("buying, ok unlock iap here")
print(p.productIdentifier)
let prodID = p.productIdentifier as String
switch prodID {
case "com.premium":
print("buying ads")
removeAds()
case "com.cash":
print("buying coins")
addCoins()
case "com.level":
print("buying levels")
addLevels()
default:
print("can't buy")
}
queue.finishTransaction(trans)
break;
case .failed:
print("buy error")
queue.finishTransaction(trans)
break;
default:
print("default")
break;
}
}
}
You should not update any purchase status in paymentQueueRestoreCompletedTransactionsFinished. This function just lets you know that the restoration process has completed. You could use this to update your UI or display an alert or something.
The restoration process delivers the transactions to be restored to the updatedTransactions function where you handle the .restored state in the same way that you handle the .purchased state.
Essentially "restore" just replays the purchase transaction process for non-consumable and auto-renewing subscription purchase types.

iOS - How to delay of showing message "purchase was successful" to wait until the validation receipt finished

i've searched and can't find any way to create a delay of showing message: "Your purchase was successful" to wait until the validation receipt finished.
I've tried to quote the line SKPaymentQueue.defaultQueue().finishTransaction(transaction as! SKPaymentTransaction) but the message still fires.
func paymentQueue(queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
print("Received Payment Transaction Response from Apple");
for transaction:AnyObject in transactions {
if let trans:SKPaymentTransaction = transaction as? SKPaymentTransaction{
switch trans.transactionState {
case .Purchased:
print("Product Purchased");
SKPaymentQueue.defaultQueue().finishTransaction(transaction as! SKPaymentTransaction)
// validate receipt and update money
validateReceipt(trans.payment.productIdentifier)
break;
case .Failed:
print("Purchased Failed");
SKPaymentQueue.defaultQueue().finishTransaction(transaction as! SKPaymentTransaction)
self.indicator.Hide()
break;
case .Restored:
print("restored")
SKPaymentQueue.defaultQueue().finishTransaction(transaction as! SKPaymentTransaction)
//[self restoreTransaction:transaction];
self.indicator.Hide()
break;
default:
break;
}
}
}
}
Basically there is no way to delay the "purchase was successful" message.
But you can show an additional alert after validating the receipt, showing the results of validation to user.

'Thank You - Your transaction has been successful' popup - what part of the Storekit code triggers it?

I am trying to intercept/delay the App Store transaction successful pop-up that a transaction was successful until AFTER I get hold of the receipt so that I can run some validation on receipt id, product id etc.
However, I cannot seem to find the bit of code (I assume it is something in the PaymentQueue) that actually triggers it.
Thoughts?
func paymentQueue(queue: SKPaymentQueue!, updatedTransactions transactions: [AnyObject]!) {
println("Received Payment Transaction Response from Apple");
for transaction:AnyObject in transactions {
if let trans:SKPaymentTransaction = transaction as? SKPaymentTransaction{
switch trans.transactionState {
case .Purchased:
println("Product Purchased");
SKPaymentQueue.defaultQueue().finishTransaction(transaction as SKPaymentTransaction)
break;
case .Failed:
println("Purchased Failed");
SKPaymentQueue.defaultQueue().finishTransaction(transaction as SKPaymentTransaction)
break;
// case .Restored:
//[self restoreTransaction:transaction];
default:
break;
}
}
}
}

Resources