store.finishTransaction() is not working properly

Hi all great people,

I am currently implementing in app purchase to my game, but faced a weird behavior that I never encountered in my previous few games.

First time, when I purchase 1 item, transactionCallback 1 purchased state, I get 1 item.

Second time, I purchase another 1 item, transactionCallback 2 purchased state, I get 2 items now (plus previous item, I have 3 items)

Third time, I purchase another 1 item, transactionCallback 3 purchased state, I get 3 items. (plus previous items, I have 6 items now)

and so on…

Typically, this behavior will happen, if no store.finishTransaction(event.transaction), but in my case, I called it, and to prove the store.finishTransaction is executed, I added a print() after it. But not working.

This is a minimum viable code I tested, that shows error:

[lua]local store = require (“store”)
local count = 0
local function storeTransactionCallback(event)

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

count = count + 1
print("=====" … event.transaction.state … “===== " … count … " =====”)

end

store.finishTransaction(event.transaction)

end

if store.availableStores.apple then
store.init(“apple”, storeTransactionCallback)
elseif store.availableStores.google then
store.init(“google”, storeTransactionCallback)
end
local function purchase()
count = 0
store.purchase({“net.topfuncoolgames.barfriends.testitem”})
–store.restore()
end[/lua]
In the 3rd time of purchase() call, this is what it returns:

=====purchased===== 1 =====
=====purchased===== 2 =====
=====purchased===== 3 =====
Why is this happening?
[import]uid: 143031 topic_id: 35459 reply_id: 335459[/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]

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 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]

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]

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]

hey klaus and d8, easy. calm down.

I have experienced strange behavior with my account too at that time. I took build 984, builded, released. and all worked fine. same did ingemar. i am still not sure if this is a corona thing, i doubt.
[import]uid: 90610 topic_id: 35459 reply_id: 141168[/import]

Chill people, let’s solve problem.

  1. The [blockcode]cancelled [/blockcode] is happening to me, because I cancelled it.

  2. I have managed and unmanaged items.

  3. I create my app in the Android Developer Console new design
    Let’s provide every bit of knowledge together and solve problem.

[import]uid: 143031 topic_id: 35459 reply_id: 141170[/import]

In my case, i used unmanaged items, and old design Developer Console [import]uid: 86585 topic_id: 35459 reply_id: 141197[/import]

In my case, i used unmanaged items, and new design Developer Console [import]uid: 15448 topic_id: 35459 reply_id: 141198[/import]

Dr.Klaus,
I think your in-app purchase issue is different than topfuncoolgames’ issue. So, I’ll respond to you on your forum thread so as not to cause confusion on this thread.

topfuncoolgames,
Since this is your forum thread, I’m going to focus on your canceled notification issue here.

I’ve looked up your issue on Google’s issue database and I believe I’ve found a similar issue here…
http://code.google.com/p/marketbilling/issues/detail?id=29

One commenter on that page indicated that removing all test users and adding a new test user account to the Android Developer page worked-around this problem. This sounds familiar to me because I remember another Corona developer having a similar issue. Like his test gmail account was being flagged as fraudulent by Google Checkout. So, you may want to try testing with a different account to see if this works-around this issue. Although, I seriously hope this does not happen to real customer accounts.

Also, how long does it take for the purchase notification to be received by your Lua listener? Is it near immediately or does it take a couple of minutes? I ask because I’m wondering if the Google Play purchase requests are timing out and its the Google Play app on your Android device that is generating all of the canceled notifications.

Today, I also double checked our code to make sure that we are handle in-app billing notifications and finishing/confirming those notifications correctly. I even compared against Google’s newest sample code (their Dungeons app) and current documentation. Everything appears to be fine on our end (I spent a few hours confirming this today). As long as the transaction that you are receiving in Lua has a “event.transaction.identifier” string, then the [lua]store.finishTransaction()[/lua] function will send a CONFIRM_NOTIFICATIONS request to Google Play. If that identifier is nil or an empty string, then our finish function will ignore the given transaction. You may want to try printing that identifier to the log too. Just be aware that it’s normal for it to be an empty string for errors or when the end-user Backs-out of the in-app purchase window.
[import]uid: 32256 topic_id: 35459 reply_id: 141285[/import]

Hi Joshua,

Thanks for your reply.

The response from Google Play is immediate, and, yes, my phone has 2 google account linked, and both of the account were listed as test account in developer console. (although only the primary account has credit card associate to it)

The [blockcode]transaction.identifier[/blockcode] can be printed successfully.

I tried to removed the secondary account from my phone and also from developer console test user, but it still behave the same.

It would be some trouble for me to set up another test account, as I have no extra credit card to use. But, yea, will test that out with different user. [import]uid: 143031 topic_id: 35459 reply_id: 141296[/import]

Wait a minute… I just had a thought.

Could it be the Google Play app on the device that is caching purchase transactions and isn’t aware of a developers test accounts? This could explain all of these “cancelled” events since a normal user shouldn’t be able to purchase more than once.
Even if a developer cancels the purchase in their Merchant account, this info would never be propagated to the device.

If this is the case, is there a way to delete the Google Play app cache? [import]uid: 70847 topic_id: 35459 reply_id: 141297[/import]