store.finishTransaction() is not working properly

Hi ingemar,
I did tried to clear all the Play service and Play store cache. But things still the same.

In my case, it happens to both managed and unmanaged items. So the user can actually purchase many times unmanaged item. [import]uid: 143031 topic_id: 35459 reply_id: 141313[/import]

It’s possible that your purchases are failing after calling the [lua]store.finishTransaction()[/lua] function. This started happening to everyone in December when Google updated their in-app purchase system to version 3. We updated Corona to work with Google’s newest changes by daily build #984.

So, are you using a daily build older than #984?
If so, then that’s the issue.

You should also move your print statement outside of your “if” block to log all notifications received from Google. This way you can verify if your app is receiving any errors. If you are running into the issue that I’ve mentioned above, then you’ll receive an error right after calling the [lua]store.finishTransaction()[/lua] function, but you’ll continue to receive “purchase” notifications for the last “purchase request” that you made because your app has failed to “finish” the transaction. [import]uid: 32256 topic_id: 35459 reply_id: 140977[/import]

Thanks for your reply. I am using the latest build (2013.1016).

So I tried your way, by putting the [lua]print[/lua] statement outside the [lua]if[/lua],

like this:

[lua]local function storeTransactionCallback(event)

print("====" … event.transaction.state … " ============= " … event.transaction.productIdentifier )

if event.transaction.state == “purchased” then

end

store.finishTransaction(event.transaction)

end[/lua]
and here are snippets of what I got.

[blockcode]
D/Finsky ( 1350): [1] MarketBillingService.sendResponseCode: Sending response RESULT_OK for request 2097830839612122931 to net.topfuncoolgames.barfriends.
D/Volley ( 1350): [1] Request.finish: 3171 ms: [] https://android.clients.google.com/vending/api/ApiRequest NORMAL 40 InAppRestoreTransactionsRequestProto
D/Finsky ( 1350): [32418] MarketBillingService.getPreferredAccount: net.topfuncoolgames.barfriends: Account from first account.
D/Finsky ( 1350): [32418] MarketBillingService.getPreferredAccount: net.topfuncoolgames.barfriends: Account from first account.
D/Finsky ( 1350): [32395] MarketBillingService.getPreferredAccount: net.topfuncoolgames.barfriends: Account from first account.
D/Finsky ( 1350): [32395] MarketBillingService.getPreferredAccount: net.topfuncoolgames.barfriends: Account from first account.
D/Finsky ( 1350): [32395] MarketBillingService.getPreferredAccount: net.topfuncoolgames.barfriends: Account from first account.
D/Finsky ( 1350): [32395] MarketBillingService.getPreferredAccount: net.topfuncoolgames.barfriends: Account from first account.
D/Finsky ( 1350): [32418] MarketBillingService.getPreferredAccount: net.topfuncoolgames.barfriends: Account from first account.
D/Finsky ( 1350): [32418] MarketBillingService.getPreferredAccount: net.topfuncoolgames.barfriends: Account from first account.
I/Corona ( 8394): ====purchased ============= net.topfuncoolgames.barfriends.recipebook
D/Finsky ( 1350): [32395] MarketBillingService.getPreferredAccount: net.topfuncoolgames.barfriends: Account from first account.
D/Finsky ( 1350): [32395] MarketBillingService.getPreferredAccount: net.topfuncoolgames.barfriends: Account from first account.
D/Finsky ( 1350): [32394] MarketBillingService.getPreferredAccount: net.topfuncoolgames.barfriends: Account from first account.
D/Finsky ( 1350): [32394] MarketBillingService.getPreferredAccount: net.topfuncoolgames.barfriends: Account from first account.
D/Finsky ( 1350): [32418] MarketBillingService.getPreferredAccount: net.topfuncoolgames.barfriends: Account from first account.
D/Finsky ( 1350): [32418] MarketBillingService.getPreferredAccount: net.topfuncoolgames.barfriends: Account from first account.
D/Finsky ( 1350): [32395] MarketBillingService.getPreferredAccount: net.topfuncoolgames.barfriends: Account from first account.
D/Finsky ( 1350): [32395] MarketBillingService.getPreferredAccount: net.topfuncoolgames.barfriends: Account from first account.
D/Finsky ( 1350): [32394] MarketBillingService.getPreferredAccount: net.topfuncoolgames.barfriends: Account from first account.
D/Finsky ( 1350): [32394] MarketBillingService.getPreferredAccount: net.topfuncoolgames.barfriends: Account from first account.
D/Finsky ( 1350): [32418] MarketBillingService.getPreferredAccount: net.topfuncoolgames.barfriends: Account from first account.
D/Finsky ( 1350): [32418] MarketBillingService.getPreferredAccount: net.topfuncoolgames.barfriends: Account from first account.
D/Finsky ( 1350): [32395] MarketBillingService.getPreferredAccount: net.topfuncoolgames.barfriends: Account from first account.
D/Finsky ( 1350): [32395] MarketBillingService.getPreferredAccount: net.topfuncoolgames.barfriends: Account from first account.
D/Finsky ( 1350): [32394] MarketBillingService.getPreferredAccount: net.topfuncoolgames.barfriends: Account from first account.
D/Finsky ( 1350): [32394] MarketBillingService.getPreferredAccount: net.topfuncoolgames.barfriends: Account from first account.
D/Finsky ( 1350): [32418] MarketBillingService.getPreferredAccount: net.topfuncoolgames.barfriends: Account from first account.
D/Finsky ( 1350): [32418] MarketBillingService.getPreferredAccount: net.topfuncoolgames.barfriends: Account from first account.
D/Finsky ( 1350): [32395] MarketBillingService.getPreferredAccount: net.topfuncoolgames.barfriends: Account from first account.
D/Finsky ( 1350): [32395] MarketBillingService.getPreferredAccount: net.topfuncoolgames.barfriends: Account from first account.
D/Finsky ( 1350): [32394] MarketBillingService.getPreferredAccount: net.topfuncoolgames.barfriends: Account from first account.
D/Finsky ( 1350): [32394] MarketBillingService.getPreferredAccount: net.topfuncoolgames.barfriends: Account from first account.
D/Finsky ( 1350): [32418] MarketBillingService.getPreferredAccount: net.topfuncoolgames.barfriends: Account from first account.
D/Finsky ( 1350): [32418] MarketBillingService.getPreferredAccount: net.topfuncoolgames.barfriends: Account from first account.
D/Finsky ( 1350): [32395] MarketBillingService.getPreferredAccount: net.topfuncoolgames.barfriends: Account from first account.
D/Finsky ( 1350): [32395] MarketBillingService.getPreferredAccount: net.topfuncoolgames.barfriends: Account from first account.
D/Finsky ( 1350): [32394] MarketBillingService.getPreferredAccount: net.topfuncoolgames.barfriends: Account from first account.
D/Finsky ( 1350): [32394] MarketBillingService.getPreferredAccount: net.topfuncoolgames.barfriends: Account from first account.
I/Corona ( 9588): ====purchased ============= net.topfuncoolgames.barfriends.recipebook
I/Corona ( 9588): ====cancelled ============= net.topfuncoolgames.barfriends.diamond1
I/Corona ( 9588): ====cancelled ============= net.topfuncoolgames.barfriends.diamond1
I/Corona ( 9588): ====cancelled ============= net.topfuncoolgames.barfriends.diamond1
I/Corona ( 9588): ====cancelled ============= net.topfuncoolgames.barfriends.diamond1
I/Corona ( 9588): ====cancelled ============= net.topfuncoolgames.barfriends.diamond1
I/Corona ( 9588): ====cancelled ============= net.topfuncoolgames.barfriends.diamond1
I/Corona ( 9588): ====cancelled ============= net.topfuncoolgames.barfriends.diamond1
I/Corona ( 9588): ====cancelled ============= net.topfuncoolgames.barfriends.diamond1
I/Corona ( 9588): ====cancelled ============= net.topfuncoolgames.barfriends.diamond1
I/Corona ( 9588): ====cancelled ============= net.topfuncoolgames.barfriends.diamond1
I/Corona ( 9588): ====cancelled ============= net.topfuncoolgames.barfriends.diamond1
I/Corona ( 9588): ====cancelled ============= net.topfuncoolgames.barfriends.diamond1
I/Corona ( 9588): ====cancelled ============= net.topfuncoolgames.barfriends.diamond1
I/Corona ( 9588): ====cancelled ============= net.topfuncoolgames.barfriends.diamond1
I/Corona ( 9588): ====cancelled ============= net.topfuncoolgames.barfriends.diamond1
I/Corona ( 9588): ====cancelled ============= net.topfuncoolgames.barfriends.diamond1
I/Corona ( 9588): ====refunded ============= net.topfuncoolgames.barfriends.testitem
D/Finsky ( 1350): [1] MarketBillingService.sendResponseCode: Sending response RESULT_OK for request 1160756147106683979 to net.topfuncoolgames.barfriends.
D/Finsky ( 1350): [32395] MarketBillingService.getPreferredAccount: net.topfuncoolgames.barfriends: Account from first account.
D/Finsky ( 1350): [32394] MarketBillingService.getPreferredAccount: net.topfuncoolgames.barfriends: Account from first account.
D/Finsky ( 1350): [32418] MarketBillingService.getPreferredAccount: net.topfuncoolgames.barfriends: Account from first account.
D/Finsky ( 1350): [32395] MarketBillingService.getPreferredAccount: net.topfuncoolgames.barfriends: Account from first account.




[/blockcode]

Above snippet is showing 1 set of response, and each set is printing 18 items (I had made 18 purchases, refunded/cancelled most). I received total of 18 sets every time I call store.restore(). Which is 18 x 18 purchased state, if I didn’t cancel/refund.

Seems like store.finishTransaction() is not working. The same item appear again and again for each store.purchase() and store.restore() call.

All items had been refunded, except [blockcode]net.topfuncoolgames.barfriends.recipebook[/blockcode]

This is driving me crazy, and it never happens to my previous 5 games with IAP.

I can also confirm this problem is also happening on Corona IAP sample code, if I compile it to my package id, and changed the product id to mine. [import]uid: 143031 topic_id: 35459 reply_id: 140992[/import]

I am getting sick of this problem. This is the exact code I tested:

[lua]local store = require “store”

local function callback(event)

local transaction = event.transaction

print("++++ " … transaction.state … " " … transaction.productIdentifier … “++++”)

if transaction.state == “purchased” then

elseif transaction.state == “restored” then

elseif transaction.state == “refunded” then

elseif transaction.state == “cancelled” then

elseif transaction.state == “failed” then

end

store.finishTransaction(transaction)

end

if store.availableStores.apple then
store.init(“apple”, callback)
elseif store.availableStores.google then
store.init(“google”, callback)
end
local widget = require “widget”

local buyBtn = widget.newButton{
label = “buy”,
onRelease = function()
store.purchase({
“net.topfuncoolgames.barfriends1.cash1”
})

end

}
buyBtn.x = 100
buyBtn.y = 100

local restoreBtn = widget.newButton{
label = “restore”,
onRelease = function()
store.restore()
end
}
restoreBtn.x = 100
restoreBtn.y = 200[/lua]
Then make 1 purchase:

this is what I got:
[blockcode]
++++ purchased net.topfuncoolgames.barfriends1.cash1++++
[/blockcode]
Then I made 2nd purchase of same item
[blockcode]
++++ purchased net.topfuncoolgames.barfriends1.cash1++++
++++ purchased net.topfuncoolgames.barfriends1.cash1++++
[/blockcode]
And you know what is the creepiest? Constantly spamming [blockcode]store.restore()[/blockcode] will return all the transaction, so user has unlimited in-game money.

Please correct me if there is something wrong with my code.

This is what I tested:

  • A 2nd test device with a new test account
  • A new app with different ID, uploaded using new developer console.

Please, someone must be able to reproduce the bug. Some us your minimal code that works (not the corona sample, too long) [import]uid: 143031 topic_id: 35459 reply_id: 141638[/import]

store.finishTransaction() is working properly. Google made special metod to consume old purchases, but corona team do not implement it + strange bug with N*N callbacks for N not consumed purchases :frowning:

One of my gamers have 19 purchases. Next purchase wil get him 20*20=400 transaction callbacks :frowning: - it is fantastic!!! :slight_smile:

2 Joshua , please check this issue!

Thanks.

one more topic about: http://developer.coronalabs.com/forum/2013/01/29/consumepurchase-recurring-iab-purchases-android [import]uid: 15448 topic_id: 35459 reply_id: 141017[/import]

I can’t wait for any support from Corona anymore. So I cooked my own solution.
I stored all the transaction receipt in database, and check if the transaction happens before.

If yes, ignore it, else proceed it.

Hope this is useful. [import]uid: 143031 topic_id: 35459 reply_id: 141649[/import]

I have made this 3 days ago, as i wrote before:

>I confirm, the multiple requests of callback transaction function is happened on latest builds for google play. The temporary solution, to store reciept ids of succeded transactions in file, and compare for unique ids on every callback. [import]uid: 86585 topic_id: 35459 reply_id: 141658[/import]

Ops, sorry for not paying enough attention, as I am hoping Corona will give us official fixes. Thanks! [import]uid: 143031 topic_id: 35459 reply_id: 141661[/import]

I personally think this is more of a Google issue. I may be wrong, but I saw these issues start to happen with my IAP apps immediately after they introduced version 3 of their In-App Purchase API.
Since Corona is using version 2, I think that Google might not be handling the version 2 API calls the same way as before.

I don’t have any proof, and I don’t have any immediate projects that require IAP this month so I can do further testing. However I’ll be starting one next month, so I’ll be joining the circus then… [import]uid: 70847 topic_id: 35459 reply_id: 141664[/import]

I confirm, the multiple requests of callback transaction function is happened on latest builds for google play. The temporary solution, to store reciept ids of succeded transactions in file, and compare for unique ids on every callback. [import]uid: 86585 topic_id: 35459 reply_id: 141038[/import]

And it seems to me that the core problem in function store.finishTransaction( transaction ), it basicly does not work and causes this repeats of old transactions again. [import]uid: 86585 topic_id: 35459 reply_id: 141669[/import]

Everyone,

Those “cancelled” notifications are coming from Google Play. Corona simply takes any received notifications from Google Play’s in-app purchase system and passes them over to your Lua listener.

You will receive “cancelled” notifications if you make a purchase with the same gmail account that you use to publish your app with. Google documents this here…
http://developer.android.com/google/play/billing/billing_testing.html#billing-testing-real

The above link tells you how to set up test accounts to test in-app purchases. [import]uid: 32256 topic_id: 35459 reply_id: 141083[/import]

Joshua ,

My english is bad, but I can read Google documents :slight_smile:

  1. I tested this issue from real account and real money only. Only real account was used for testing!
  2. I have no canceled purchases.

Please, find for us some others google documents to read :slight_smile:

Thanks. [import]uid: 15448 topic_id: 35459 reply_id: 141084[/import]

Joshua, please, don`t think, that all are so stupid, it is not “cancelled” notifications !!! Just belive that listener recive multiple same “purchased” events !! [import]uid: 86585 topic_id: 35459 reply_id: 141086[/import]

I’m sorry that Google Play is giving you so much trouble, but to be honest, I don’t think there is anything we can “fix” on our end. I’m not trying to be difficult here, but there really isn’t much to the in-app purchase system. We just establish the connection to the Google Play app, send the requests, and receive responses/notifications according to Google’s documentation and Dungeon example. The [lua]store.finishTransaction()[/lua] function definitely works or else your purchases would not work at all… and I’ve confirmed that that function will send a CONFIRM_NOTIFICATIONS request for any transaction object that you gave it that has an identifier.

The only thing that I can think of is that you’re running into a bug with Google Play. You can see a full listing of their bugs here…
http://code.google.com/p/marketbilling/issues/list

As you can see in the link above, a lot of Android developers are frustrated with Google’s in-app purchase system too. It may very well be that Google broke something when they updated to version 3, because it was working flawlessly all last year.

The following bug reports sound like the issue that you’re running into…
http://code.google.com/p/marketbilling/issues/detail?id=94
http://code.google.com/p/marketbilling/issues/detail?id=92

Google did accept that their is a bug with refunds and cancelled purchases. Developers seem to think that there is a caching issue on the Google server end.

I think your app tracking transaction IDs is a reasonable way of working around this issue.
[import]uid: 32256 topic_id: 35459 reply_id: 141737[/import]

There’s no reason to get upset. I don’t think anyone here is stupid. The canceled notification issue that I posted above is just a common issue that many Android developers run into, which is why I mentioned it.

I just re-tested Google in-app purchases now and it’s working for me. I’m not receiving several notifications after making 1 purchase.

I do know that Google’s in-app purchase system can be quite fussy at times. It seems to happen more often in certain countries than others; possibly due to the purchase transaction taking too long on Google’s end causing the purchase to be canceled. Just have a look at the links below where other Android developers complain about this…
http://code.google.com/p/marketbilling/issues/detail?id=45
http://code.google.com/p/marketbilling/issues/detail?id=37

My point being that I don’t think Corona is doing anything wrong. Those notifications that your apps are receiving are in fact coming from Google Play. Corona merely passes them to your app, duplicates and all. It’s not Corona’s job to manage the transaction. Corona merely sends/receives the in-app purchase packets. Your code has to decide what to do with these notifications. If you can prove that Corona is doing it wrong, then I’d love to hear it, but if it was completely broken then I would be getting far far more feedback from other Corona developers.
[import]uid: 32256 topic_id: 35459 reply_id: 141096[/import]

Joshua, PLEASE!

You wrote: “I’m not receiving several notifications after making 1 purchase.”

  1. Try to make more then “1 purchase.”
  2. Purchases must be Consumable.

You will get 1 notification for first purchase, 4 notification for second purchase, 9 notification for 3rd and etc.

Try it please with Consumable purchases.

Thanks.

EDIT: This issue may be resolved by implementation consumePurchase() metod in Corona.
http://developer.android.com/google/play/billing/api.html [import]uid: 15448 topic_id: 35459 reply_id: 141103[/import]

Corona currently uses version 2 of Google’s In-App Billing API, not version 3. We consider the version 3 API to be unfinished until they support all of the features in version 2.

The version 2 API does not support the ability to check if the “managed” product has already been purchased. That is up to the app to manage this. That said, the Google Play store will show an “Already purchased” error onscreen if this is attempted. So, I don’t think this is the source of the extra notifications that you are receiving.

Have you tried contacting Google as to why you are receiving so many cancelled notifications? You might be having some kind of an account issue with Google Checkout. You can find Google’s contact information here…
http://support.google.com/googleplay/android-developer/bin/answer.py?hl=en&answer=136601&rd=1
[import]uid: 32256 topic_id: 35459 reply_id: 141119[/import]

It seems we got “canceled” notification from corona support :frowning:

Joshua,
You dont read my messages, but may be you can look at?

I HAVE NO CANCELED ORDERS IN GOGLE CHECKOUT AT ALL!!! ALL PURCHASES PROCESSED AND ADDED TO MY ACCOUNT!
Do you really read all my message?

NO CANCELED
NO CANCELED
NO CANCELED
NO CANCELED
NO CANCELED

Do you see it? Ok, Some more:

NO CANCELED
NO CANCELED
NO CANCELED
NO CANCELED
NO CANCELED
[import]uid: 15448 topic_id: 35459 reply_id: 141151[/import]

Joshua, you are the super professional, you know ? :slight_smile: You are the best in coronalabs staff, show this dialog to your chief :slight_smile: [import]uid: 86585 topic_id: 35459 reply_id: 141163[/import]