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:
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 method 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 for 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, each 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); } }
Since the users can uninstall and reinstall games, or buy new devices, they must be able to restore previous purchases. Use the Store.RestoreTransactions() method to ask App Store to re-send all previously completed transactions. These transactions (if any) will be added to the list of transactions with the "restored" state (see above).
Just like the other communication methods, this one is anynchronous. You request the restored transactions and then you wait for events. In this case, the script which called Store.RestoreTransactions() will receive either the "TransactionsRestoreFinished" event or the "TransactionsRestoreFailed" event.
// display waiting animation // and a "cancel" button // ... // request restored transactions Store.RestoreTransactions(); on "TransactionsRestoreFinished" { // it went well } on "TransactionsRestoreFailed" { // it went wrong }
Once the product is purchased, you should store this information somewhere locally (it would be impractical to connect to App Store all the time). WME Lite provides two convenience methods: Store.UnlockProduct() and Store.IsProductUnlocked(). Both accept a product ID as a parameter.
When you are handling the "purchased" or "restored" transaction, you can call Store.UnlockProduct() to save the information about the purchase. In your game, just use the Store.IsProductUnlocked() to check if the user has purchased some functionality. If yes, let them continue playing, if not, display some purchase GUI and offer them to buy the functionality.
Note that you don't have to use these two methods. You could as well store the information yourself, for example using Game.RegWriteNumber().
Also note, that this information is lost if the user uninstalls the game. That's why you always must provide means of restoring previous transactions (see above).