Android inapp purchase: transaction purchased when transaction has failed!

I am testing in app purchase on Android.

I wanted to test the negative scenarios, so I’ve used a card which I cannot pay electronically.

I’ve received an error message from google, saying that I cannot use this card, but the transaction state I got was “purchased”.

Here’s the whole scenario I got and every transaction state I’ve received:

  1. Use a card I cannot use online

  2. Use a card with no money on it

  3. Use a card attached to another account

  4. Use a valid card with money on it

In google wallet, I can see 2 cancelled operations and one submitted fine [I cannot see the operation for a card attached to another account].

In my code, I log every call to the in app purchase handler to see the transaction state, and here’s the list I got after these 4 operations:

I/Corona ( 8676): ARCHER INFO =?:0 !!! TRANSACTION !!! cancelled I/Corona ( 8676): ARCHER INFO =?:0 !!! TRANSACTION !!! cancelled I/Corona ( 8676): ARCHER INFO =?:0 !!! TRANSACTION !!! cancelled I/Corona ( 8676): ARCHER INFO =?:0 !!! TRANSACTION !!! cancelled I/Corona ( 8676): ARCHER INFO =?:0 !!! TRANSACTION !!! cancelled I/Corona ( 8676): ARCHER INFO =?:0 !!! TRANSACTION !!! cancelled I/Corona ( 8676): ARCHER INFO =?:0 !!! TRANSACTION !!! cancelled I/Corona ( 8676): ARCHER INFO =?:0 !!! TRANSACTION !!! purchased I/Corona ( 8676): ARCHER INFO =?:0 !!! TRANSACTION !!! cancelled I/Corona ( 8676): ARCHER INFO =?:0 !!! TRANSACTION !!! cancelled I/Corona ( 8676): ARCHER INFO =?:0 !!! TRANSACTION !!! purchased I/Corona ( 8676): ARCHER INFO =?:0 !!! TRANSACTION !!! cancelled I/Corona ( 8676): ARCHER INFO =?:0 !!! TRANSACTION !!! cancelled I/Corona ( 8676): ARCHER INFO =?:0 !!! TRANSACTION !!! purchased

That is not something I would expect to happen.

Especially that I have purchased the app only once.

It seems that after a “purchased” call I can get “cancelled” call, is this correct? Is this valid?

I used to do nothing on “cancelled” and unlock my game on “purchased”. Now I think I have to lock the game on “cancelled”.

Here’s my code of the in app purchase handler:

local function handleInAppPurchase(event) local transaction = event.transaction if waitingPopup then waitingPopup:close() waitingPopup = nil end log:info("!!! TRANSACTION !!! %s", tostring(transaction.state)) if transaction.state == "purchased" then if transaction.productIdentifier == ARCHER.storeRemove then [...cut...] end elseif transaction.state == "restored" then if transaction.productIdentifier == ARCHER.storeRemove then [...cut...] end elseif transaction.state == "cancelled" then Popup.simplePopup(ARCHER.l("purchaseCancelled")) if transaction.productIdentifier == ARCHER.storeRemove then [...cut...] end elseif transaction.state == "failed" then log:info("errorType: %s", tostring(transaction.errorType)) log:info("errorString: %s", tostring(transaction.errorString)) Popup.simplePopup(ARCHER.l("purchaseFailed")) if transaction.productIdentifier == ARCHER.storeRemove then [...cut...] end elseif transaction.state == "refunded" then if transaction.productIdentifier == ARCHER.storeRemove then [...cut...] end end store.finishTransaction(transaction) end

In other words, I only expected this function to be called once after it was initialized. It seems it can be called multiple times if user performs multiple operations.

BTW. I expected “failed” events when I was unable to pay with my card. weird.

Now it gets even better!!

I’ve uninstalled the application and downloaded it again from play store.

I’ve started it and tried to restore my purchase.

I got information from play store, that I already own it, but the transaction I got in corona is: failed!

I/Corona (10748): ARCHER INFO =?:0 !!! TRANSACTION !!! failed I/Corona (10748): ARCHER INFO =?:0 errorType: invalidClient I/Corona (10748): ARCHER INFO =?:0 errorString: 

And there I was thinking I am close to release… ehh

Any thoughts why would this happen?

I’ve change nothing on the device.

The scenario was very simple:

  1. Get the app from play store

  2. Purchase addon

  3. Uninstall app

  4. Reinstall app

  5. Restore purchase

I’ve raised 

Case 23927

I can see a lot of attention regarding this post and this case.

There is something broken in Corona, it’s either store.purchase or store.restore.
When calling the restore function, if user have never bought my addon, there will be no response to the transaction handler!
If I call purchase for a user, who has bought the addon before, there will be failed status.

I would love to call .restore before .purchase, but it’s not possible without ever knowing what the response from .restore is (in case user never bought the addon).

Now it gets even better!!

I’ve uninstalled the application and downloaded it again from play store.

I’ve started it and tried to restore my purchase.

I got information from play store, that I already own it, but the transaction I got in corona is: failed!

I/Corona (10748): ARCHER INFO =?:0 !!! TRANSACTION !!! failed I/Corona (10748): ARCHER INFO =?:0 errorType: invalidClient I/Corona (10748): ARCHER INFO =?:0 errorString: 

And there I was thinking I am close to release… ehh

Any thoughts why would this happen?

I’ve change nothing on the device.

The scenario was very simple:

  1. Get the app from play store

  2. Purchase addon

  3. Uninstall app

  4. Reinstall app

  5. Restore purchase

I’ve raised 

Case 23927

I can see a lot of attention regarding this post and this case.

There is something broken in Corona, it’s either store.purchase or store.restore.
When calling the restore function, if user have never bought my addon, there will be no response to the transaction handler!
If I call purchase for a user, who has bought the addon before, there will be failed status.

I would love to call .restore before .purchase, but it’s not possible without ever knowing what the response from .restore is (in case user never bought the addon).

@krystian6: have you ever found a “solution”?

I have some strange behavior: people buy it, but nothing happens. then people go on buy again, and it says that they already own it. nothing happens, and restore doesnt work either.

i am lost? funny thing is, that it seems to work for most, but not all.

@dingo

well the solution for me was to create a separate button to restore purchases in options.

Every time I get purchased or cancelled I simply set everything in my game to correct values. Thankfully I don’t have to download anything.

So the answer is… no… I haven’t found a solution, only a workaround which will probably not help you much [the restore button].

I’ve raised plenty of Android issues recently, but none of them were addressed, nobody contacted me about them.

Krystian

The invalidClient is caused by one of these conditions:

  • You are logged into the Google Play app with your developer account. Purchases always fail in this case.  You need to use a test account instead.
  • You have not set up an account with the Google Play app yet.
  • You have not agreed to the Google Play app’s license agreement yet.
  • The Google Play app is failing to connect to Google’s servers.

With regards to store.restore()… Android IAP 2 does not support returning anything if a restore call is made and they have never bought anything.  You can set a timer to trigger code to handle whatever condition you need as if you got a return from restore.  Personally, I would have programmed my transaction handler in such a way that my app went on in whatever locked state its in if I never got a return from store.restore().  Only if I get something, should I go unlock things.  This in combination with some settings that may tell you to trust the user and unlock the feature should be supported.  Your app should work in Airplane mode in which case you’re never going to get a response from store.restore() anyway.  

As for the odd duplicates, it could be that you’re code isn’t finishing the transaction for some reason and they are being left int he queue…

@krystian6: have you ever found a “solution”?

I have some strange behavior: people buy it, but nothing happens. then people go on buy again, and it says that they already own it. nothing happens, and restore doesnt work either.

i am lost? funny thing is, that it seems to work for most, but not all.

@dingo

well the solution for me was to create a separate button to restore purchases in options.

Every time I get purchased or cancelled I simply set everything in my game to correct values. Thankfully I don’t have to download anything.

So the answer is… no… I haven’t found a solution, only a workaround which will probably not help you much [the restore button].

I’ve raised plenty of Android issues recently, but none of them were addressed, nobody contacted me about them.

Krystian

The invalidClient is caused by one of these conditions:

  • You are logged into the Google Play app with your developer account. Purchases always fail in this case.  You need to use a test account instead.
  • You have not set up an account with the Google Play app yet.
  • You have not agreed to the Google Play app’s license agreement yet.
  • The Google Play app is failing to connect to Google’s servers.

With regards to store.restore()… Android IAP 2 does not support returning anything if a restore call is made and they have never bought anything.  You can set a timer to trigger code to handle whatever condition you need as if you got a return from restore.  Personally, I would have programmed my transaction handler in such a way that my app went on in whatever locked state its in if I never got a return from store.restore().  Only if I get something, should I go unlock things.  This in combination with some settings that may tell you to trust the user and unlock the feature should be supported.  Your app should work in Airplane mode in which case you’re never going to get a response from store.restore() anyway.  

As for the odd duplicates, it could be that you’re code isn’t finishing the transaction for some reason and they are being left int he queue…