As the “freemium” app monetization strategy has grown in popularity, many developers have encountered the same problem: how can we give away fully-unlocked copies of our app to reviewers or friends? Apple gives developers 100 free promo codes for each new version of an app to hand out, but Google Play does not, and even so those codes only help if your app doesn’t unlock features via in-app purchase (“IAP”). As of now there is no way for us to hand out IAP promo codes (though there are reports that this may change). To make matters worse, if you try to create your own system for unlocking features, the odds are very good that your app submission will be rejected by Apple’s reviewers. To the best of my knowledge, there’s nothing stopping you from creating your own IAP promo code system on Android, but who has time to create a whole menu system for unlocking content? Fortunately there is an easy way to unlock content on both iOS and Android simultaneously, with very little change to your code – and since this method is completely invisible, it does not trigger an App Store rejection! Read on to find out how…
Launching Your App Via URL Schemes
An “unsung hero” feature of the Corona SDK is the ability to apply “URL schemes” to our apps, which allow the app to be launched by clicking on or navigating to a URL that tells the device to open our app. For example, a URL that begins with “http://” tells your device to open the link in a browser like Safari. Likewise, a link that begins with “mailto:” tells your device to open up its email client. Your custom URL scheme can be anything you want, and you define it in your build.settings. Below is a sample build.settings for an app called “My Awesome App.” If you had this app installed on your iOS or Android device, and clicked the link myawesomeapp://launch, the app would open:
settings = { ---------------------------------------------------- -- ORIENTATION SETTINGS: ---------------------------------------------------- orientation = { default = "portrait", supported = { "portrait"}, }, ---------------------------------------------------- -- iOS SETTINGS: ---------------------------------------------------- iphone = { components = {}, plist = { CFBundleDisplayName = "My Awesome App", UIApplicationExitsOnSuspend = false, CFBundleURLTypes = { {CFBundleURLSchemes = { "myawesomeapp", }} }, } }, ---------------------------------------------------- -- ANDROID SETTINGS: ---------------------------------------------------- android = { versionCode="10", usesPermissions = { "android.permission.INTERNET", }, intentFilters = { { actions = { "android.intent.action.VIEW" }, categories ={ "android.intent.category.DEFAULT", "android.intent.category.BROWSABLE", }, data = { scheme = "myawesomeapp" }, }, }, }, }
Using URLs to Unlock Content
While launching your app via URL is cool, what’s even cooler is that we can trigger specific functions inside our app based on the second half of the URL (what comes after the “colon-slash-slash”). By adding a Runtime listener that checks to see if the app was opened via URL, and then performs actions based on the content of that URL, we can do all sorts of things, including unlock IAP content. Below is a fully-functioning main.lua that listens for the specific URL “myawesomeapp://unlock” and then runs a function called unlock() when that URL is opened:
local function unlock( ) print("UNLOCKED!") end local function urlListener(event) if event.type == "applicationOpen" and event.url == "myawesomeapp://unlock" then unlock() end end Runtime:addEventListener( "system", urlListener ) local launchArgs = ... if launchArgs and launchArgs.url then Runtime:dispatchEvent({name = "system", type="applicationOpen", url=launchArgs.url}) end
If you want more details on the specifics of how the above code works, I suggest reading this Corona Labs blog post from a few years ago, which does a pretty goo job of explaining it.
Preventing Piracy
The example above works just fine, and would allow you to tell friends or reviewers to download your app and open the URL “myawesomeapp://unlock” to get all those premium features for free. But what if one of those people told a friend? And that friend posted the URL to an internet message board? Or tweeted it? The example above does nothing to prevent this sort of piracy, and if your super-secret URL became not-so-super-secret, you’d have a real problem on your hands. But there is a way for us to get around this by authenticating URLs against a simple web-hosted text file that then tells the app which function to run (or not run) accordingly. Using this method, you’ll have the ability to add new promo codes or invalidate old ones if they are being exploited – all without having to update your app. Here’s how:
- First, download my promoURLs lua module and place it in the same folder as your main.lua.
-
Now require the module into your app by adding the following line of code to your main.lua, preferably somewhere near the top:
local promoURLs = require("promoURLs")
-
Now we need to make a few edits to promoURLs.lua. Open it up in your favorite text editor and follow these steps:
- On line 30, there is a local variable prefix that defaults to “http://www.mywebsite.com/promocodes/”. Change that to a web-hosted folder that you control. Remember to leave the trailing slash!
- On line 33, there is a local variable schemeName that defaults to “promoURLs.” Change that so that it perfectly matches the URL scheme you defined in your build.settings.
- In the section titled “Promo Functions (Make as many as you need),” (lines 35 – 51) add functions to unlock the various in-app purchases for your app – or create functions to do anything at all! You could create a function that automatically gives a player a very high score, for example. It’s up to you!
- IMPORTANT: Each promo function must be a function of the table “promoURLs” (i.e. “promoURLs.myFunction”) – check out the two included example functions to see how it’s done.
-
Now that our app is properly configured, we just need to prepare at least one text file that will be used to authenticate our promo codes. Here’s how these files work:
- For each promo code we want to make active, we’ll need a text file whose name matches our promo code. For example, if we wanted to activate the promo code myawesomeapp://example, we would name our text file example.txt.
- The text file should only contain a single word, which must match the name of one of our promo functions, minus the “promoURLs.” prefix. For example, if we wanted our promo code to call the function promoURLs.myFunction, the text file would simply contain the word myFunction.
- Now that we’ve prepared our text file, upload it to the same web-hosted folder you referenced in the prefix variable in promoURLs.lua.
Once you have completed the above steps, opening your promo code URL on a device with your app installed will launch your app, which will then look online for a text file whose name matches the promo code. If it finds that file, it will attempt to run the function referenced in the text file.
Adding and Removing Promo Codes
The beauty of this system is that you can add and/or remove promo codes any time you wish, without having to update your app. Let’s say you wanted to send a promo code to TouchArcade, just for them. Simply upload a text file called “TouchArcade.txt” to the web folder you reference in promoURLs.lua, whose contents contain the name of one of your promo functions. Then send the promo code myawesomeapp://TouchArcade to Touch Arcade, and you’re all done! When you want to deactivate that promo code, simply delete that text file – that’s it!
Have You Used It?
This module is free to download, free to use, and you are free to modify it to your heart’s content. I hope that you find it useful! But if you do use it in one of your apps, please share a link to your app in the comments, or contact me so I can see how you integrated it into your app. I’d love nothing more than to see to see my code getting used out in the wild. Enjoy!
Awesome idea! Thanks for the help.
Thanks for the tip!