iap-featIn the mobile development world, studios have been looking for more models to monetize their apps. This doesn’t just impact games, as business app developers are finding it more challenging to monetize as well.

There are several ways to make money with your app, including:

  1. Charge for it.
  2. Offer a limited free version and a paid full-featured version; you can use the “lite” app to drive customers to the paid one.
  3. Offer it for free and use ads to monetize.
  4. Offer it for free but charge for features.

Each version has its upsides and downsides:

  • Paid: This option has two pitfalls: first, people are now spoiled on “free,” and second, paid apps will typically reside below free apps in any ranking service, limiting your app’s visibility.
  • Free-to-play: This model worked well for many years, but it tends to clutter the app stores and some vendors have been discouraging this approach as there are better ways to accomplish the same goal.
  • Ad-funded: Using advertising is an appealing option, but you need a large install base and an app that people will use frequently for the ads to pay off. For many indie titles, the competition is stiff. Also, if you’re a game developer, keep in mind that games have a fairly short shelf life.
  • “Freemium”: You can offer the app for free and then charge for additional features using In-App Purchases (IAP). In this case, the app is free, but has some features that the customer needs to pay for before they can use them. This can be a way to accomplish option #2 — a free (but limited) game – and once you pay for it, you get the entire game. Another use of freemium is a full-blown economic system where your app has some in-game currency and players can earn this currency through play. If they need more, they can pay real-world money to get more in-game currency. Finally, you could use freemium to have a donation system: if the player enjoys the app, they may consider supporting the development for some amount of money.

In-App Purchase Options

Corona SDK currently offers IAP through three vendors:

  1. Apple’s App Store
  2. Google Play
  3. Fortumo (NOOK)

We also have two more options in the pipeline:

  1. Amazon In-App Purchase (currently in Beta)
  2. OUYA (coming soon)

For Apple, Google and Amazon, accessing IAP is done through the Corona Store API. Apple and Google are built into Corona’s core, and Amazon will be a third party plugin. Fortumo is also a third party plugin, but it does not use the store API.

Because each vendor’s “eco-system” varies from the others, there are some things that simply cannot be 100% cross-platform compatible. Part of working with IAP is learning the differences between the vendors and adapting your code to those differences.

Purchase Types

There are generally three classes of things users can purchase:

  1. One-time purchases that last the life of the app, like unlocking the full version of the app, or downloading new content once.
  2. One-time purchases that get “consumed.” Once they’re used, the app user needs to buy more. This is most commonly used for in-app currency.
  3. Subscriptions where the app user pays a monthly fee to receive some ongoing service. Subscriptions can be auto-renewing or non-renewing.

Each vendor uses different terminology for these purchases. Please view this chart to help translate each vendor’s language:

Apple Google Amazon Fortumo
One Time, Lasts Forever Non-Consumable Managed Product Entitlement Single Item
One Time; Gets Used Consumable Unmanaged Product Consumable Virtual Credits
Subscription 1. Auto-Renewable Subscription
2. Non-Renewable Subscription
Subscription Subscription n/a

Setup

Before you can use IAP, you must set up products in each vendor’s developer portal. This topic is too complex to detail in this tutorial, but in general terms, you have to:

  • Create each product you want to sell, giving it a unique ID*.
  • Submit the items for approval to the appropriate store. Both Apple and Amazon also require screenshots of your for sale screens.
  • For Apple and Google, set up unique test accounts. Amazon has a different method of testing.

* For all of these vendors, you can specify a unique ID. Google and Apple calls it Product ID. Amazon calls it a SKU. In all three cases, it’s recommended that you use the “reverse domain name” system that is used in your Bundle ID/App Identifiers. If you maintain consistent identifiers, it will make cross-platform implementation easier.

An example of this format is:

Which may translate to products like these:

Google will require you to upload your .apk with the proper permissions installed before you can add products to their store portal (although you can upload an Alpha build in which you do not need to make the products live).

These are the required permissions:

The store.* API

Corona’s built-in IAP library is the store module. Include it in your app using a require() statement as such:

Of course you need to do this in each module/scene where you’ll be accessing the store. From there, three main API calls will be used (there are few others which we’ll cover later):

  • store.init()
  • store.purchase()
  • store.restore()

The store.init() call lets you define which store you wish to work with, while providing an event listener function to handle the results from interactions with the store.

To purchase things, use store.purchase() — simply pass to the function what you want to buy, and then when the transaction with the app’s user is done, the function you defined in store.init() will be called with the results of the transaction. Your app would then respond accordingly (unlock features, add to your “coin” count, etc.).

In the event that the app user deletes your app or wipes their device and re-installs your app, they are entitled to re-obtain their purchases that were non-consumable. It’s your responsibility to restore these purchases, which Corona allows via the store.restore() call.

NOTE: Consumable items like coins, gems, etc. are not restored. None of the vendors will respond to requests to restore these.

The other functions in the store.* API includes:

Sample Store Code

Let’s look at a sample API store project. Before we do, you’ll need a few things to support your app. First, you will need the ability to load and save settings data so you can track if the person has paid for their unlock, the current coins count, etc. Let’s include that in a utility.lua file that we’ll include with our code. This is a lot of code to digest, but it’s the minimal structure.

utility.lua

main.lua

Workflow and Notes

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, perhaps by using a button specifically to restore purchases.
  3. For Apple and Amazon, you can use store.loadProducts() to get a list of your items using localized names and prices. Google 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.
  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.

Making Purchases

Making purchases is fairly easy. Simply pass your product identifier to the store.purchase() function:

This code should be fairly straightforward and would be typically found in the scene where your store interface is set up. The one “gotcha” here is that both Apple and Google allow you to purchase multiple items at once. Therefore, store.purchase() accepts an array of product ID strings.  Amazon, however, only allows purchasing one item at a time, so you only pass a single string. This is a case that you need to code for — while most users only purchase one thing at a time, it needs to be an array of items, even if just one is being passed in.

Testing In-App Purchase

This tutorial can’t go in-depth about this topic because it’s too complex and too “vendor specific,” but here are a few tips to help you out:

Apple

  • You need specific test accounts which you can create in iTunes Connect. They are standard Apple IDs, so you will need to have a unique email address for each account. If you have your own web hosting service, you can set up aliases like “iap01@yourdomain.com” and “iap02@yourdmain.com” that send emails to your primary email address as a “catch all.” Note that Apple will make you validate the email by clicking on a link within it, so you must be able to receive it.
  • You must log out from iTunes on your device before you run the test app. This test app can be provisioned with either a Development or AdHoc profile to access the IAP sandbox. When prompted, log in with your test account.
  • You get “one shot” to buy your item because Apple remembers that your account has purchased that item. If you need to test again, you need a different account. There is no way to reset these purchases.
  • If you see “you need to submit your app and reject the binary,” you can disregard that advice.  Currently, you just need to submit your items for review — but it does take a little time for your items to propagate through Apple’s servers.

Google

  • Google requires specific test accounts as well. They are regular email addresses that you provide in the portal. The trick is getting your device to no longer be “you” but rather to be one of your test accounts. Google provides some product IDs that you can use instead of your own IDs. You can test a product that “always succeeds” and one that “always fails” and do this as many times as you need to. 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.
  • Google’s system is sensitive to developers. For instance, if you request too many restore() requests in too short of a time, they will block your account for several weeks, assuming there is suspicious activity tied to it.

Amazon

Amazon’s IAP plugin is still in development and is subject to change.

  • Besides the differences listed above, their testing process involves downloading their IAP App Development Kit. This contains a .apk file that you can side-load onto your device which intercepts your app’s purchases during testing. You can then use your existing account (or other accounts) to test.
  • You must also download a JSON file from their developer portal which contains your products and side-load this onto the device as well. Their IAP test app uses the JSON file instead of actually talking back to Amazon’s servers.
  • From the IAP test app, you can reject purchases, which gives you a good chance to test “revoked” purchase states.

In Summary…

As you can see, IAP is a key part of the monetization puzzle. While some people prefer to utilize a paid app approach, using IAP with a “freemium” model can be a great way to monetize your apps and games.

Continue reading here:  

Tutorial: Understanding In-App Purchases