In app purchases, I need some serious help!

I’ve got Admob working but…

Christmas (my deadline) has come and gone and I’m still trying to get my head around the in app purchase, I only have one Non-Consumable to unlock the full game, with Google!

What’s tripped me up is that Google requires you to keep a record of all the in-app purchases made which have to be saved to a server in case somebody uninstalls the app then re-installs it and wants their purchase restored.

Why on Earth don’t Google just set a flag that can be checked by a network listener?

Like one of Rob’s tutorials states, “However, once you start using your actual Product IDs, you get one shot, just like Apple, and there’s no way to reset them other than using a different account.”

If this is the case then surely Google is keeping a record of who and who has not made a purchase?

It seems crazy that we have to create a website to store information that Google should have anyway.

I’m just about ready to give up on the idea of an in-app purchase unless somebody here is generous enough to give me their working code, I’ve spend many days reading all the docs and tutorials on this subject and my head is spinning!

Why is it so difficult?

P.S. Can I use Dropbox instead of my own website, my website is so secure that when I try to access a php file (to do a simple version number look up) that I put in my public folder I get a page not found response because my website is blocking access to it.

network.request( "http://www.mywebsite.com/getLatestVersion.php?method=getLatestVersionNumber&OS="..system.getInfo("platformName"), "GET", networkListener, params )

When I print the event.response from the networkListener I get the all html code of the page (not-found.htm) that I created years ago, not the php contents. This is driving me mad.

I just have a button that says “Restore Purchases” and follow the flow mentioned here.

https://docs.coronalabs.com/api/library/store/restore.html

I keep a local database of the items purchased and on a new install this is updated once the restore button is pressed.  I don’t have a remote record of it anywhere.

Is that not sufficient?

I’ve got Admob working but…

Christmas (my deadline) has come and gone and I’m still trying to get my head around the in app purchase, I only have one Non-Consumable to unlock the full game, with Google!

What’s tripped me up is that Google requires you to keep a record of all the in-app purchases made which have to be saved to a server in case somebody uninstalls the app then re-installs it and wants their purchase restored.

Why on Earth don’t Google just set a flag that can be checked by a network listener?

Like one of Rob’s tutorials states, “However, once you start using your actual Product IDs, you get one shot, just like Apple, and there’s no way to reset them other than using a different account.”

If this is the case then surely Google is keeping a record of who and who has not made a purchase?

It seems crazy that we have to create a website to store information that Google should have anyway.

I’m just about ready to give up on the idea of an in-app purchase unless somebody here is generous enough to give me their working code, I’ve spend many days reading all the docs and tutorials on this subject and my head is spinning!

Why is it so difficult?

P.S. Can I use Dropbox instead of my own website, my website is so secure that when I try to access a php file (to do a simple version number look up) that I put in my public folder I get a page not found response because my website is blocking access to it.

network.request( “http://www.mywebsite.com/getLatestVersion.php?method=getLatestVersionNumber&OS="..system.getInfo("platformName”), “GET”, networkListener, params )

When I print the event.response from the networkListener I get the all html code of the page (not-found.htm) that I created years ago, not the php contents. This is driving me mad.

I think essentially everything you said is incorrect.  Google stores the purchase state of each in app item, of which you can check for whenever you want.  I suggest reviewing Google’s page on In App Billing: http://developer.android.com/google/play/billing/api.html

OK, I started reading about in app purchases here: https://coronalabs.com/blog/2013/09/03/tutorial-understanding-in-app-purchases/

Which says, " Google does not provided a “restored” state.  Even if you call store.restore(), Goggle considers these  “purchased” items. Thus, you need to determine the difference between “purchased” and “restored” if that’s important for the app. To handle this, you should store the transaction’s receipt and, when you receive a purchase request, you should compare against previous purchases and see if you found a matched receipt (this accounts for apps being deleted and re-installed). Note that you can’t save your receipts locally — instead, you have to setup an online method to store and retrieve these receipts. While this is not a trivial task, comparing these digital receipts (which are complex, encrypted chunks of data) makes it difficult for someone to fake a receipt during the restore phase. This is the best practice to help mitigate piracy."

Which got me looking at how to store receipts online.

I will now start here: http://developer.android.com/google/play/billing/billing_admin.html

then…

https://docs.coronalabs.com/plugin/google-iap-v3/index.html

https://docs.coronalabs.com/guide/monetization/IAP/index.html

https://docs.coronalabs.com/api/library/store/restore.html

Thanks @funkyvisions and @JonPM for your help in pointing me in the right direction.

I will keep this topic open until I have this working, if anyone else has anything to add please do.

You should use IAP badger. It takes the guesswork out, and makes it very simple to include IAP for those that use it.

https://forums.coronalabs.com/topic/59137-iap-badger-a-unified-approach-to-in-app-purchases/

Do you care about the difference between restored and purchased?  For me it just indicates they bought something that should now be enabled in the app.  I don’t really care beyond that.

Google and Apple work differently when it comes to IAPs.  There is no “restored” state in the callback function on Google.   Items are either purchased or they are not.  I haven’t used IAP Badger yet, but it is worth looking into.  IAP’s can be a little confusing, especially since there are subtle differences between platforms.

Hi Alex

I did have a look at IAP badger before, but I didn’t find it easy to understand, there’s far too much going on.

While I appreciate what it’s trying to accomplish there’s information overload, do you know what I mean?

Not only that but the examples given are nearly 300 lines of code with spinners and what-not to decipher.

The same is true with Rob’s tutorial, “Understanding In-App Purchases”, a very good tutorial, never the less it still requires more reading and is bloated.

The basic workflow for your app involves:

  1. Determine the store your app is running on and initialize the store using store.init().
  2. If you think the app has been reinstalled (i.e. there is no settings file, etc.), then (for Google and Amazon) you can call store.restore() when your app starts because it is silent. Apple will prompt your app to login to the iTunes App Store when your app starts. It’s best to delay the restore until later. Apple wants you to have a button specifically to restore purchases.
  3. For Apple, Google Play V3 and Amazon, you can use store.loadProducts() to get a list of your items using localized names and prices. Google Play V2 does not offer this with the version of IAP that Corona SDK uses. When Google shows the purchase dialog, the localized values will be shown then. Many people will just hard code their items in a custom display.
  4. Offer a purchase button or display that allows the customer a chance to purchase your items. This should lead to a call to store.purchase().
  5. Inside the event handler function, perform whatever activities you need to unlock your app or process your purchase.  For Google Play V3, this might be a good time to call store.consumePurchase() if the items is something like coins or gems that are being added to the app’s inventory immediately and can’t be tracked separately.  Items like potions in a fantasy game, or purchasable power up’s can be consumed when they are actually used.
  6. Make sure to call store.finishTransaction() at the end, regardless of the transaction state. Failing to call this function can cause your app to receive too many transactions!

In addition, each vendor’s store variances create situations that you need to adapt to. Let’s look at a couple of key situations:

  1. Consumables are never restored.  You will never get a “restored” state that contains a consumable item. The reason is that these items are considered “used up” and there is no need to restore them. You will only get restored items for non-consumable items.
  2. Google does not provided a “restored” state.  Even if you call store.restore(), Goggle considers these  “purchased” items. Thus, you need to determine the difference between “purchased” and “restored” if that’s important for the app. To handle this, you should store the transaction’s receipt and, when you receive a purchase request, you should compare against previous purchases and see if you found a matched receipt (this accounts for apps being deleted and re-installed). Note that you can’t save your receipts locally — instead, you have to setup an online method to store and retrieve these receipts. While this is not a trivial task, comparing these digital receipts (which are complex, encrypted chunks of data) makes it difficult for someone to fake a receipt during the restore phase. This is the best practice to help mitigate piracy. The code above does not, for brevity. What it does, however, is attempt to map purchases to restores when Google is involved.
  3. If there is nothing to be restored, the event listener will not be called,  so you should plan for this condition accordingly. If there is nothing to restore, there is nothing to do. Many people get snagged by this because they are looking for a “trigger” to say the process was complete. You should use a timer with a reasonable timeout to trigger your effect if there is no work to do.
  4. Amazon and Google allow refunds,  but they handle it differently. Google can generate a “refunded” state. If you see this state come through, you should have your app lock whatever feature that product ID unlocked. Of course, this only happens for non-consumable items.  Products that are are consumed cannot be refunded. For Amazon, the state is “revoked”.   Apple does not support refunds.
  5. Apple triggers a store login for restores.  The observant reader may have noticed that the store.init() sections above do not have a store.restore(). Google and Amazon will silently trigger restorations if the user is logged into the respective app store. Apple however, will prompt for the user’s password if they have not accessed the billing system in the last few minutes. Because of this, you don’t want a modal dialog to interfere with your users getting into the app and enjoying it. You can defer this restore until the user takes an action, until the place in your code where you actually need to determine their past purchases (or perhaps provide the user a “Restore past purchases” button in the interface). Apple will reject apps that do not call store.restore() in a proper manner.

While I’m writing this I’ve got 7 windows open…

https://github.com/happymongoose/iap_badger
http://happymongoosegames.co.uk/iapbadger.php?page=tutorial.markdown
http://developer.android.com/google/play/billing/billing_overview.html (This leads to EVEN MORE guides and tutorials)
https://coronalabs.com/blog/2013/09/03/tutorial-understanding-in-app-purchases/
https://docs.coronalabs.com/api/library/store/restore.html (These 3 lead to more docs, guides and tutorials)
https://docs.coronalabs.com/plugin/google-iap-v3/index.html
https://docs.coronalabs.com/guide/monetization/IAP/index.html

I’m going around in circles!

@JonPM

Right now I don’t need a unified approach for all the different stores, just Google.

@funkyvisions

I don’t care about restored and purchased because I’m only interested in Google, for now.

I’m pretty sure if I spend the time (I’ve spent over a week on this so far) and take out all the iOS stuff, consumable items, Amazon, encryption, hashes, Google V2 and so on… I will probably end up with working code a few lines long, eventually.

I was hoping that somebody here would say, “Hi QuizMaster, it’s easy! All I do is…”, instead of the usual “Did you read this tutorial?, Did you read the docs? Take a look at this link…”

Anyhow, thanks for your input so far, but I’m still stuck and more confused than before and ready to give up.

All I want to do is have 1 in-app purchase to unlock the full game with GPGS, is it really that difficult?

Can you explain exactly where your issues are?  Your first post is a bit scattered and unclear as to what you need exactly.  It sounds like you’re trying to restore purchases?  But also checking current app version?  I am just as confused as you :slight_smile:

My first post was scattered because I was lead to believe (and maybe it’s true according to Google) that purchases should be stored and retrieved online, which raised more questions.

Checking current app version was a simple way of trying to get a data base working between my website and my app, when I got that working I was going to modify it for the purpose above.

All I want to do is have 1 in-app purchase to unlock the full game with Google, and of coarse you have no choice but to handle restore purchases, don’t you?

False.  Google stores all transaction details themselves.  Basically when your app loads you initialize the store and check the status of each in app item (which in your case is only one).  Google will respond with whether that item is purchased or not by that user.  Here’s the approach I’ve taken to a similar setup:  Basically your default app state is all “Pro version” features are locked (by code).  On app startup the app checks with Google to see if the Pro version is purchased, if it is purchase then I unlock all the items via code.  If not then everything remains locked.  

I think where you’re getting confused is on iOS there is a “restored” state.  Basically when a user downloads your app, purchases the in app item, uninstalls your app, then reinstalls it, the previous purchase state has been locally erased.  So, again only in iOS, you need to have a “Restore” button that the user clicks, then logs into App Store, and this sends a message back to your app that the user previously purchased that item. 

Now on Android there is no “restored” state.  Instead, every time your app starts you check with Google’s server if the user has previously purchased the item (without the user having to manually check it).  This data comes back to you in the “Purchased” state. 

Now I know it can be frustrating when people just throw links at you.  But at this point you should really go over https://docs.coronalabs.com/guide/monetization/IAP/index.html#setupandroid

This literally has all the info you need to get IAP’s working on Android.  Start with Google Play Setup, once you’re done there move down to Store Functions.  

Now once you’ve got some solid code in your app, test it out.  If it doesn’t work then post your code here and we can try to help.

Again, ignore all the “Restore” stuff as that only pertains to iOS. 

That’s cleared up a lot of nonsense, now I can move forward and actually implement code.

Thank you Jon!

That’s kind of what I was saying. I treat iOS and android exactly the same except for the restored vs purchased status. On both platforms I have a restore purchases button that writes the statuses to a local database and it’s used as the reference as to whether somethings has been unlocked or not. It does sound like on android I could do this automatically without the button, but I prefer to keep them both the same.

It’s a good point about the examples in the documentation being long and complicated for IAP Badger - if it helps, I’ve added a links to the simplest possible purchase code on the main documentation page.

@happymongoose, thanks very much, I’m looking at it now, I will let you know how I get on.

@happymongoose, I’ve implemented your iap code and all looks good so far.

I just have one question, how does your code handle the restore purchase with regards to GPGS?

Upon a new install I would like my app to check with Google if the customer has purchased the unlock game before, if so the game is automatically unlocked.

Thanks in advance.

Hi Quizmaster,

When your app is started, just call iap.restore() - I do this in a systemEvent listener for applicationStart.   When the store responds, everything that the user has purchased in the past will come back as a restored product (rather than a purchased one) and your app can respond as it needs to.

The code to do the restore is:

iap.restore(false, yourListenerFunction)

In this case, you don’t need to set a timeout listener, because you don’t care if the user hasn’t bought products in the past.

Hope that makes sense  :slight_smile:

Simon

Thanks for the quick reply and thanks for the wonderful code you created and thanks for making things easier for me.

I started with your simplified code, got that working then started going through your more detailed code and added salt, doNotLoadInventory=true and debugMode=true

THANK YOU!!

Just remember to remove your references to doNotLoadInventory and debugMode when you go into production (or want to test on live devices).

:smiley:

I will, thanks.

:slight_smile: