Monetising your iOS Titanium App: In-App purchases

Rodolfo Perottoni
All Titanium
Published in
10 min readJan 28, 2016
We love when you, our dear user, click that buy button

A while ago the In App Purchases module was archived by Appcelerator and I've also received a few e-mails from people telling me that the module doesn't work anymore in some scenarios. Unfortunately I can't give any advice on it since I don't use the module anymore. Use it at your own risk.

For personal reasons and lack of time I also won't be able to create the tutorial for implementing In App Purchases on Android.

In-app purchases are all over the place. Maybe you never made one, but you probably already have gotten into a situation where you were asked to pay for some extra options / features in an mobile App. They are totally optional, but depending on what is being offered we sometimes get very tempted to just click that "buy" button and enjoy that new functionality. Come on, it's only $1, right?

This is a big deal for us, developers. With a large user base you can start gaining an extra monthly profit right away with lots of those $1 purchases…

… but unfortunately we have very little documentation on In-App purchases for Titanium apps.

Fear no more, comrades. You're about to step your profiting game up. In this article i'll be teaching you how you can implement a simple Consumable product (something like a chocolate bar: you buy, eat it, and if you want one more you'll need to buy it again) on your iOS app. Do note that there are various types of products which you can sell on the iOS platform, but knowing how to create and making a single one work makes the rest of the work pretty easy.

(All the steps below were made on an Mac OS X environment with the Appcelerator Studio. You may have to change some paths or whatnot for windows / linux operational systems. I have also used the gitTio package manager for downloading the required modules. You can download it with NPM right here: http://gitt.io/cli.

For my and your convenience, i'll be referring to In-App purchases as simply IAPs).

iOS: The ti.storekit module

First things first: we'll need to add some IAPs in our iTunes Connect account and some test users (so we can test our purchases without being charged). For this i'm assuming you already have an app deployed or at least ready for deploy. Go to iTunes Connect, log into your account and head to the My Apps window and select the app you're currently intending to implement IAPs.

After that you should open your IAPs page. You can find it here:

Features -> In-App Purchases

Notice the "+" button right beside the "In-App Purchases" table title. That's where we're going right now. You're gonna find yourself at a new page asking the Type of your IAP. For this example we're going to create a Consumable IAP.

Time to fill up some details about your product:

  • Reference name: That's the name which you (developer) will be able to see in your IAPs list / sale reports;
  • Product ID: This is an important one. This will be used to contact the iTunes Store and retrieve informations about your product inside the app;
  • Cleared for sale: this controls wether you want your product to be available for purchases (or not) right after Apple approves it. Check this according to your needs. If you're not sure about it, just leave it as "Yes".
  • Price tier: Apple doesn't allow you to use a custom price for your products. Instead, you have to select between one of their Price Tiers. You can check the pricing matrix right below the drop-down list, on the "View Pricing Matrix" button.
  • Language: Here you'll have to insert a name and a description for your product in a certain language. Do note that at least one language is required in order to create your IAP. This name and description will be shown to your users when you're calling the ti.storekit purchase methods.
  • Screenshot for Review: Needed for the go-live review. Right after creating your IAP you'll notice that it will be marked as "Ready to Submit". In other words, your IAPs will be available publicly only after you send them along with a new version of your App. Not only you need to select which IAPs you want to deploy with a new App version, you must also include a screenshot of the window (inside your App) where the users will be able to purchase them.

After you have filled all those fields, just click "Save" and you'll be redirected to your IAPs list. Your new IAP will be marked as "Ready to Submit" and you can start using it inside your development environment right away.

Now to adding a test user! On your iTunes Connect home page, head into the Users and roles section and click on the Sandbox Testers tab. While in that page, click the circled plus button to add a new tester.

Fill the required fields on the screen that has opened and confirm the creation of the account. Remember: you will need to login with this account on the App Store of your iOS device.

Time for some programming.

First we need to install the module into our development environment. Open terminal and head into your project's root folder. Execute the following command to install the required module (as explained above, I assume you already have gitTio installed):

gittio install ti.storekit
TiApp.xml modules section

After it is installed, open your project’s TiApp.xml and confirm if the module is included in your build settings.

We are going to create an Alloy Window with a button. This button click event will request a product from Apple Store and ask if the user wants to proceed with that purchase or not.

<Alloy>
<Window backgroundColor=”gray” onClose="cleanUp">
<Button onClick=”requestProduct” title=”Request Product”/>
</Window>
</Alloy>

Now we have to setup our controller. There are quite a few things to explain about this module. Let's start with the basics.

Every single Apple Store purchase done by the user will return a receipt. This is useful if you want to keep track of everything that a user bought. This is also required for your App Reviewing process: Apple may ask if you're storing the user's purchase history in you server (so he can use the products that he bought on multiple devices).
I highly recommend reading Apple's Receipt Validation guidelines. You can find plenty of material there explaining what is a receipt, what does it contain and how to implement receipt validation in your device or on your server.

Anyways… we are going to use receipt validation in our App (IOS 7 +), but we'll need to download the Apple Inc. Root Certificate in order to do that. The reason why we need that is very simple: Every receipt emitted by Apple will have a signature on it. When validating a receipt we'll check if the signing authority is the same as the Apple Inc. Root Certificate. You can download it here.

Once you have the certificate downloaded, copy it to your project's app/assets folder. It MUST be on this folder, and not on any children of it, otherwise your app will crash telling that the certificate was not found.

Back to our controller: let's set up some values that will be used to validate our receipts as well as some listeners to watch our purchases states.

var storekit = require('ti.storekit');function init() {
storekit.receiptVerificationSandbox = Ti.App.deployType !== 'production';
storekit.bundleVersion = '1.0';
storekit.bundleIdentifier = 'au.com.example';
storekit.addEventListener('transactionState', transactionStateChanged);
storekit.addTransactionObserver();
}
// Never forget to remove the transaction observer
// and your event listeners, otherwise you might cause memory leaks and unwanted problems
// Assigned to the close event of my Window element (see XML)
function cleanUp() {
storekit.removeTransactionObserver();
storekit.removeEventListener('transactionState', transactionStateChanged);
storekit = null;
}
init();
  • receiptVerificationSandbox = NEVER CHANGE THIS! By setting this to true, we are declaring that we wish to use Apple's Sandbox verification server. If by accident you deploy your app to the production environment with this set to true, you might consider looking for another job;
  • bundleVersion = Few things to consider here. If you're changing only the major and minor numbers of your version (E.g. 2.1, 2.2, 3.0, 3.1, and so forth), you can assign Ti.App.Version for this variable. On the other hand, if you are more detailed versioning scheme like I am (E.g. 2.0.2.2, 2.0.2.3, and so forth), you should use only the first three numbers of your version (E.i. 2.0.2, 2.0.3, and so forth). If you're still not sure of which value to use for this variable you can build your project, go to the generated build/iphone folder (in your project's root folder) and open the XCode project that has been created there (the one with the extension .xcodeproj). Once you have Xcode open, click on your project on the left bar, then in the center section of Xcode click on the tab "General". The value that you should be using is the one on the Version field;
  • bundleIdentifier = Your app's Application Id, as defined on your TiApp.xml file;
  • transactionState events = these events will command the show. Assigning a callback to be invoked when this event is triggered is crucial, otherwise you'll never know if your purchase has been approved, completed, cancelled or whatever;
  • addTransactionObserver() = transactionState events will be only thrown if you call this method. Please note that this must be called after all your event listeners are set up on the storekit module reference.

Now to the methods which will contact the Apple Store and request for a certain product.

function requestProduct() {
storekit.requestProducts(['au.com.example.smallCreditPackage'], function (evt) {
if (!evt.success) {
alert(‘Sorry, the App Store seems to be down right now. Please try again soon.’);
} else if (evt.invalid) {
alert(‘Invalid product.’);
} else {
purchaseProduct(evt.products[0]);
}
});
}
function purchaseProduct(product) {
storekit.purchase({product: product});
};

Notice that on the requestProducts method i'm passing an array of values and a callback. Even though you want to request only ONE product, you still need to pass its ID inside an array.
Those IDs are the ones that you've chosen when creating the IAPs on the previous steps. Once Apple Store returns your request, your callback will be invoked and an object with a few important properties will be passed as a parameter to it. If the request succeeded, you can call storekit.purchase(); right away with the product that you want to buy.

The code above addresses every possible outcome which may occur when requesting a product, so you can just copy and paste it like a boss.

You should see something like this after calling the storekit.purchase(); method.

After calling storekit.purchase(); and choosing if you want to buy the requested product or not (image above), the storekit module will start throwing events whenever your transaction state changes.

Don't click "Buy" if there's no "Environment: Sandbox", or you'll lose your money! Remember to log in into the SandBox Tester account that we've created earlier.

On the first part of our controller we've set a callback to be invoked whenever an event called transactionState is triggered by the module. That callback is all that remains in order to complete our purchasing workflow. Let's get into it.

var transactionStateChanged = function(evt) {
switch (evt.state) {
case storekit.TRANSACTION_STATE_FAILED:
if (evt.cancelled) {
alert('Purchase cancelled');
} else {
alert(evt.message);
}

evt.transaction && evt.transaction.finish();
break;
case storekit.TRANSACTION_STATE_PURCHASED:
if(storekit.validateReceipt()) {
Ti.API.info(JSON.stringify(evt.receipt.toString()));
alert('Purchase completed!');
}

evt.transaction && evt.transaction.finish();
break;
}
};

For this example we are only going to watch for two transaction states: TRANSACTION_STATE_FAILED and TRANSACTION_STATE_PURCHASED. These methods should be enough for most apps you'll be publishing. For a list of all possible transaction states that you can watch, head to the module specifications and search for "TRANSACTION_STATE".

Notice that i'm invoking the method evt.transaction.finish(); on each one of those states. You must call this method in order to finish the current pending transaction, otherwise it will stay in the purchasing queue indefinitely, making it impossible to make new purchases.

When the method storekit.validateReceipt(); is invoked, the module will check if the current receipt in your device is valid. It might as well throw an error if you don't have a receipt at all. Head to the Validate A Receipt section on this page to understand what is validated when this method is called. If the receipt present in your device is valid, then IT'S DONE! You're free to do whatever you find appropriate with your user's receipt.

As i've stated above, Apple may ask you if you're storing your users purchase history somewhere. This is important in case you want them to be able to use the products that they bought on multiple devices.

A quick example of what I have in one of my published Apps is an in-app credit system which is controlled by my API. Users are able to buy credit packages, and when they do it I will send the receipt JSON right away to my API. Once there, i'm validating the receipt against Apple's server and if I get a positive return i'll increase that user's credit count by the quantity that he bought. You're free to implement whatever kind of in-app features you want, though.

When publishing your app to the Apple Store you must remember to select which IAPs will be included in your App Review, otherwise your app will be published and whenever one of your users try to buy your products, they will receive a message telling them that the product is invalid.

All that done, you're good to start flying with your own wings. Trying other product types, exploring the module documentation some more (you can find it below): it's all up to you. If you have any questions, feel free to send me an e-mail at rodolfo.perottoni@gmail.com. I'd be happy to help.

Useful resources

Ti.StoreKit module on GitHub:
https://github.com/appcelerator-modules/ti.storekit/tree/master/ios

Ti.StoreKit module documentation:
https://github.com/appcelerator-modules/ti.storekit/blob/master/ios/documentation/index.md

Ti.StoreKit module example (Not Alloy):
https://github.com/appcelerator-modules/ti.storekit/blob/master/ios/example/app.js

Apple's Receipt Validation Programming Guide:
https://developer.apple.com/library/ios/releasenotes/General/ValidateAppStoreReceipt/Introduction.html#//apple_ref/doc/uid/TP40010573-CH105-SW1

--

--