I have a TableViewController, in viewDidLoad , I'm requesting for all the products available and then reloading the tableview with number of cells = number of products.
What is happening is when i click on buy Button and cancel it, It prints "Buy Cancelled" only once.
But When I go to some other viewcontroller and come back to this tablevc again (as I'm opening this tableVC again ViewDidLoad loads again) cells are loaded correctly. But then when I try to buy and click on cancel again. it prints "Buy Cancelled" Twice.
If I go to other view and open this vc again. Then it prints Three times.... this goes on.
This is my viewDidLoad
if (SKPaymentQueue.canMakePayments()) {
var productIDs:NSSet = NSSet(objects: productOne, productTwo, productThree, productFour, productFive)
var productsRequest:SKProductsRequest = SKProductsRequest(productIdentifiers: productIDs)
productsRequest.delegate = self;
productsRequest.start();
}else{
println("can't make purchases");
}
and delegate Method
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;
}
}
}
Related
I have a collectionView with paging enabled in the storyboard. Each collection view cell is the full width of the screen
return CGSize(width: collectionView.bounds.width, height: collectionView.bounds.height)
I am trying to set up IAP and have encountered an Issue. Each cell has a button that checks for a product_ID associated with the row when tapped.
print("About to fetch the product...")
// Can make payments
if (SKPaymentQueue.canMakePayments())
{
let productID:NSSet! = NSSet(object: self.product_ID)
let productsRequest:SKProductsRequest = SKProductsRequest(productIdentifiers: productID as! Set<String>)
productsRequest.delegate = self
productsRequest.start()
print("Fetching Products")
}else{
print("Can't make purchases")
UIAlertView(title: "Purchases Disabled",
message: "Purchases are disabled on your device! Enable Purchases in the Restriction Settings on your device.",
delegate: nil, cancelButtonTitle: "OK").show()
}
payment queue
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.default().finishTransaction(transaction as! SKPaymentTransaction)
// Handle the purchase
print(product_ID)
UserDefaults.standard.set(true, forKey: product_ID)
setTitle = "Apply Theme"
case .failed:
print("Purchased Failed")
setTitle = "4.99"
SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
case .restored:
print("Already Purchased")
//SKPaymentQueue.default().restoreCompletedTransactions()
// Handle the purchase
UserDefaults.standard.set(true, forKey: product_ID)
setTitle = "Apply Theme"
default:
break
}
purchaseButton.setTitle(setTitle, for: .normal)
}
}
}
`
The issue I have is that when I press the button, it doesn't return 1 productID, it returns 3. It processes the payment for the cell I select and every adjacent cell.. It seems that even when the other cells are off screen they are still active. They don't seem to be removed by the system. Is there a solution to this? I've never run into an issue like this before.
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.
In my app, the user can make two different purchases.
Here's my paymentQueue function :
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, . Restored:
print("Product Purchased / Restored")
SKPaymentQueue.defaultQueue().finishTransaction(transaction as! SKPaymentTransaction)
// TO DO
if selectedProduct == "product1" {
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "product1Purchased")
} else if selectedProduct == "product2" {
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "product2Purchased")
}
case .Failed:
print("Purchased Failed")
SKPaymentQueue.defaultQueue().finishTransaction(transaction as! SKPaymentTransaction)
break
default:
break
}
}
}
}
I created a variable named selectedProduct to detect which product the user select. If he click on the button to buy the first product, the variable selectedProduct hold the value "product1".
The problem is when the user click the Restore Purchases button, the app check if the selected product is "product1" or "product2", but selectedProduct has no value if the user click on the Restore Purchases button, so nothing happen.
How can I do please?
Do not use a variable such as selectedProduct to determine which product was purchased or restored. Look inside the SKPaymentTransaction for the needed information.
case .Purchased, . Restored:
print("Product Purchased / Restored")
SKPaymentQueue.defaultQueue().finishTransaction(transaction as! SKPaymentTransaction)
if trans.payment.productIdentifier == "product1" {
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "product1Purchased")
} else if trans.payment.productIdentifier == "product2" {
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "product2Purchased")
}
Adjust as needed for your actual product ids.
Ok, so I'm working in Swift and I just need help. I have followed 4 different tutorials on how to implement in app purchases in sprite kit with Swift, copied code verbatim, and nothing is working for me.
Here are the steps I have taken:
Gone in Itunes Connect and created an in app purchase under my app's record. My in app purchase's product Id is "GameOverSaveSavior"
In Xcode, I've turned my app's In app purchase capability to ON, made sure my team is set to my account, and made sure my Bundle Identifier under info is set to my app's bundle identifier in ITunes Connect
Before writing any code, I have import StoreKit in my GameScene.swift file
As for code, this is what I have done:
(1) In Gamescene.swift, at the end of my didMoveToView func, I have:
// Set IAPS
if(SKPaymentQueue.canMakePayments()) {
println("IAP is enabled, loading")
var productID:NSSet = NSSet(objects: "GameOverSaveSavior")
var request: SKProductsRequest = SKProductsRequest(productIdentifiers: productID as Set<NSObject>)
request.delegate = self
request.start()
} else {
println("please enable IAPS")
}
This outputs "IAP is enabled, loading" when the app is run.
(2) In GameScene.swift, within the class but outside of didMoveToView, I have all the functions and variables others have used for in app purchases:
var list = [SKProduct]()
var p = SKProduct()
func purchaseMade() {
println("they bought it!")
}
func buyProduct() {
println("buy" + p.productIdentifier)
var pay = SKPayment(product: p)
SKPaymentQueue.defaultQueue().addTransactionObserver(self)
SKPaymentQueue.defaultQueue().addPayment(pay as SKPayment)
}
func productsRequest(request: SKProductsRequest!, didReceiveResponse response: SKProductsResponse!) {
println("product request")
var myProduct = response.products
for product in myProduct {
println("product added")
println(product.productIdentifier)
println(product.localizedTitle)
println(product.localizedDescription)
println(product.price)
list.append(product as! SKProduct)
}
}
func paymentQueueRestoreCompletedTransactionsFinished(queue: SKPaymentQueue!) {
println("transactions restored")
var purchasedItemIDS = []
for transaction in queue.transactions {
var t: SKPaymentTransaction = transaction as! SKPaymentTransaction
let prodID = t.payment.productIdentifier as String
switch prodID {
case "GameOverSaveSavior":
purchaseMade()
//Right here is where you should put the function that you want to execute when your in app purchase is complete
default:
println("IAP not setup")
}
}
var alert = UIAlertView(title: "Thank You", message: "Your purchase(s) were restored. You may have to restart the app before banner ads are removed.", delegate: nil, cancelButtonTitle: "OK")
alert.show()
}
func paymentQueue(queue: SKPaymentQueue!, updatedTransactions transactions: [AnyObject]!) {
println("add paymnet")
for transaction:AnyObject in transactions {
var trans = transaction as! SKPaymentTransaction
println(trans.error)
switch trans.transactionState {
case .Purchased, .Restored:
println("buy, ok unlock iap here")
println(p.productIdentifier)
let prodID = p.productIdentifier as String
switch prodID {
case "GameOverSaveSavior":
//Here you should put the function you want to execute when the purchase is complete
var alert = UIAlertView(title: "Thank You", message: "You may have to restart the app before the banner ads are removed.", delegate: nil, cancelButtonTitle: "OK")
alert.show()
default:
println("IAP not setup")
}
queue.finishTransaction(trans)
break;
case .Failed:
println("buy error")
queue.finishTransaction(trans)
break;
default:
println("default")
break;
}
}
}
func finishTransaction(trans:SKPaymentTransaction)
{
println("finish trans")
}
func paymentQueue(queue: SKPaymentQueue!, removedTransactions transactions: [AnyObject]!)
{
println("remove trans");
}
This outputs "product request" to the console when the app is run.
(3) In GameScene.swift, in my touchesBegan func, I have the following for when the right button is touched:
//In app purchase
if touchedNode == saveMeBtn {
println("button touched!")
for product in list {
var prodID = product.productIdentifier
if(prodID == "GameOverSaveSavior") {
p = product
buyProduct() //This is one of the functions we added earlier
break;
}
}
This outputs "button touched!" when the button is touched.
I do not know what I am doing wrong. The code compiles with no errors, yet nothing occurs when my button is touched. There is no alert asking the user to sign in or to purchase anything- nothing.
I have mainly followed this question:
in app purchase in SKScene
Is there anything else I should have done prior to writing the code? Am I missing something in my code?
I think I can help you:
I had your issue in the past. And another similar one which I fixed here: My IAP isn't working. Bugs at func Paymentqueue
Here is the solution I had found:
Delete
SKPaymentQueue.defaultQueue().addTransactionObserver(self)
everywhere you have it and put it once (ONLY ONCE) in a place where it will be executed each time your app boots up ( I put it in viewDidLoad()).
This will check for all unfinished transactions and terminate them once the app has loaded, thus removing any possible errors before your users triggers an IAP.
(If this answer helped you, don't forget to upvote ;))
P.S.: Also, this wasn't my issue, but make sure to finishTransaction() for each PurchaseState, like here:
func paymentQueue(queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
print("Add Payment")
for transaction:AnyObject in transactions{
let trans = transaction as! SKPaymentTransaction
print(trans.error)
switch trans.transactionState{
case .Purchased:
print("IAP unlocked")
print(p.productIdentifier)
let prodID = p.productIdentifier as String
switch prodID{
case "IAP id":
print("Keep on")
keepOn()
default:
print("IAP not setup")
}
queue.finishTransaction(trans)
break
case .Failed:
print ("Buy error")
queue.finishTransaction(trans)
break
default:
print("default: Error")
break
}
}
}
Never forget this:
queue.finishTransaction(trans)
And make separate case for .Restored.
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.