In app purchases, I need some serious help!

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:

@happymongoose

Hi Simon

It looks like I’ve successfully got your iap badger working on my game.

I just have to add the bit to check for restore purchase in app startup and I’m done.

So in my code (where the game loads for the first time) do I have to do the following before checking for a restore…

require iap_bager, set up the catalogue, iapOptions and init?

One more question, iap.getLoadProductsCatalogue() does that only work in a live application?

Thanks.

Hi again,

At the start of your game: that order is correct.  

iap.getLoadProductsCatalogue will only fetch product information from a server on an actual device - your app doesn’t have to be in production though.  So, on Google Play, it’s enough that it is in alpha.

However, the function can also provide some debug/placeholder information on the simulator too.

Have a look at the following page: in the example code, you’ll see that in the catalogue, for the product buy50coins, there are entries called simulatorPrice, simulatorDescription and simulatorTitle.  These are the values you’ll get back when you call the function on the simulator.  This should allow you to use placeholder text and prices to check the layout of your app.

That’s great, I didn’t think about doing that at the beginning.

This is the first time I’ve done this, can you tell me if the price returned in the local currency also has the symbol with it like $ or £

Thanks for your help again.

:slight_smile:

I’m 99.65% sure you get a string back containing all the currency symbols included for you - you just print out whatever string they give you.

(I can’t get to my own code to check from where I am now).

I’ll find out soon enough I’m sure, thanks again.

I’m looking for Android Beta testers, would you like to test my game?

Here’s the Twitter page for it with some screen shots: https://twitter.com/gameshowapp

I could really use the feedback.

I’m afraid I’m massively busy at the moment, so I couldn’t promise anything.  If you message me the link, I’ll do my best to have a look over the following week or so.