This is an old revision of the document!
—-
Starting with iOS 3.0, the users can purchase "products" directly from applications. This technology is called "in app purchases". These products can be of three types:
WME Lite supports purchases of non-consumable products.
All products must be defined in the iTunes Connect interface to be available. Follow the instructions outlined in Apple documentation.
A few important notes:
* After you define products in iTunes Connect it takes some time before they are accessible to your app. * In app purchases must be tested on the actual device. They don't work in iOS simulator. * For testing, create test user in iTunes Connect, don't use your primary Apple ID.
WME Lite provides a scripting interface for in app purchases via a Store object. Store object is accessed via Game.Store property. It is recommended that you put the Store object to a separate variable for convenience:
var Store = Game.Store;
The game should always first check whether the store is available. The users can disable in app purchases in the iPhone Settings (Settings → General → Restrictions → In-App Purchases). If the store is not available, notify the user and don't let them purchase products.
if (!Store.Available) { // notify user }
As stated above, all the available products must be defined in iTunes Connect first. Each product has a unique ID (for example "org.deadcode.wmelite.unlock").
The game must know which IDs it's going to use. But before they can be purchased, the game must ask the App Store whether the products are available. This is done using the Store.ValidateProducts() method.
Store.ValidateProducts("org.deadcode.wmelite.unlock");
If you need to validate multiple IDs, separate them with semicolon:
Store.ValidateProducts("product1;product2;product3");
This method is asynchronous. Meaning it takes a while before the product IDs are validated (the game must connect to App Store via network). Once the products are validated, the script receives a "ProductsValidated" event. Since the validation takes time, you may want to display some waiting animation. Also always provide some kind of "cancel" button.
// display waiting animation // and a "cancel" button // ... // invoke validation Store.ValidateProducts("org.deadcode.wmelite.unlock"); on "ProductsValidated" { // products are now validated, display purchase GUI } // assuming your cancel button is called "cancel"... on "cancel" { // cancel the process (close the purchase window etc.) }
Once the products are validated, you can query which ones are avaiable, and which IDs are invalid.
// list invalid product IDs for (var i = 0; i < Store.NumInvalidProducts; i = i + 1) { Game.Msg(Store.GetInvalidProduct(i)); }
In case of valid products you can access the name, description and price of the products. Use this info to offer the products to the user. This information is localized, based on the user's App Store country (you can fill localized descriptions in iTunes Connect).
// list valid products for (var i = 0; i < Store.NumValidProducts; i = i + 1) { var product = Store.GetValidProduct(i); Game.Msg("ID: " + product.Id + " Name: " + product.Name + " Description: " + product.Description + " Price: " + product.Price); }
Once you are sure that the store is available and the products are valid, you can allow the user to purchase them. It is done using the Store.Purchase() method:
Store.Purchase("org.deadcode.wmelite.unlock");
Once again, this methos is asynchronous. You trigger the purchase and then you need to wait for a transaction event (see below). Again, you may want to display some waiting animation and a cancel button while the purchase is in progress.
Purchases (and purchase attempts) will cause App Store to generate transactions. App Store is then sending the transaction notifications to the application and awaits confirmation.
First, the application must allow reception of transaction events:
Store.EnableEvents();
The important concept here is, that the script that called this method will receive the transaction events. If multiple scripts called it, the last one wins. If the script no longer exists, WME Lite will route the event to Game object.
Since in iOS the application can be terminated at any moment (incoming call etc.), there may be pending transactions (App Store initiated the transaction but the application didn't have a chance to confirm it). App Store will re-send any pending transactions immediately after the application calls Store.EnableEvents(). For that reason you should call it immediately when the game starts, in game.script, and this script should be able to handle transactions. That way even if the game quits before confirming a transaction, the purchase will be finalized immediately after the user restars the game.
Whenever App Store sends a transaction form confirmation, a "TransactionsUpdated" event is triggered in the script that last called Store.EnableEvents(). The game can then access all pending transactions like this:
// list all pending transactions on "TransactionsUpdated" { for (var i = 0; i < Store.NumTransactions; i = i + 1) { var trans = Store.GetTransaction(i); Game.Msg("ID: " + trans.Id + " Product ID: " + trans.ProductId + " State: " + trans.State); } }
As you can see, teach transaction has a unique ID, an ID of the purchased product, and a state. Use the state to decide what to do about the transaction. The state can be one of the following strings:
You MUST confirm all pending transactions once you processed them, using the Store.FinishTransaction() method. If you fail to do so, App Store will re-send the transaction.
So the actual transaction handling code should look like this:
// handle all pending transactions on "TransactionsUpdated" { for (var i = 0; i < Store.NumTransactions; i = i + 1) { var trans = Store.GetTransaction(i); if (trans.State == "purchased" || trans.State == "restored") { // do something with the purchased product Id UnlockPurchasedProduct(trans.ProductId); } else if (trans.State == "failed") { // perhaps notify the user? } else if (trans.State == "cancelled") { // perhaps notify the user? } // but in any case tell the App Store that you handled the transaction! Store.FinishTransaction(trans.Id); } }