Translations of this page:

In app purchases guide (iOS only)

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:

  • consumable - the user purchases a certain number of copies of the products and the copies disappear when they are used (consumed); for example the player can buy 10 rockets and use them
  • non-consumable - non-consumable product only needs to be purchased once and it never expires; this is typically used for unlocking game featurs (levels, chapters, puzzle packs)
  • subscriptions - time limited features

WME Lite supports purchases of non-consumable products.

Preparation

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.

The Store object

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;

Checking store availability

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 
}

Products

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); 
}

Purchasing

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.

Transactions

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:

  • purchased - the transaction was successful and the product was purchased
  • cancelled - the user cancelled the transaction
  • failed - the transaction failed
  • restored - it is a previously handled transaction requested by Store.RestoreTransactions() (see below)

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); 
  } 
}

Restoring transactions

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 
}

Unlocking functionality

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).

 
wmelite/iap_guide.txt · Last modified: 2013/05/15 19:34 by mnemonic
Recent changes RSS feed Creative Commons License Driven by DokuWiki