Defold In-app purchase extension API documentation

This extension provides a unified, simple to use interface to several different stores for in-app purchase:

  • Apple’s iOS Appstore - StoreKit
  • Google Play Billing 3.0
  • Amazon ‘in-app billing’ 2.0.61
  • Facebook Canvas ‘game payments’

These services gives you the opportunity to sell products as:

  • Standard in-app products (one time billing) of consumables or non-consumables and
  • Subscriptions (recurring, automated billing)

The current Defold interface allows full interaction with Apple’s Storekit functionality. For Google Play and Facebook Canvas, the interface is identical, meaning that you can run the same code on either platform. However, some process flow might differ from platform to platform. Also note that there is currently no support for OS X purchases through the Mac Appstore.

Detailed documentation from Apple, Google, Amazon and Facebook can be found here:

Installation

To use this library in your Defold project, add the following URL to your game.project dependencies:

https://github.com/defold/extension-iap/archive/master.zip

We recommend using a link to a zip file of a specific release.

For Facebook Canvas you also need to add the Facebook extension as a dependency.

Testing Google Play Billing with static responses

On Android it is recommended that you start implementing IAP in your app by using static responses from Google Play. This enables you to verify that everything in your app works correctly before you publish the app. Four reserved product IDs exist for testing static In-app Billing responses:

android.test.purchased
Google Play responds as though you successfully purchased an item. The response includes a JSON string, which contains fake purchase information (for example, a fake order ID).
android.test.canceled
Google Play responds as though the purchase was canceled. This can occur when an error is encountered in the order process, such as an invalid credit card, or when you cancel a user’s order before it is charged.
android.test.refunded
Google Play responds as though the purchase was refunded.
android.test.item_unavailable
Google Play responds as though the item being purchased was not listed in your application’s product list.

Setting up your app for purchases/billing

The procedure on iOS and Android is similar:

  1. Make sure you are a registered Apple or Google Play developer.
  2. Set up your project so it works on your target device. See the iOS development and Android development guides.
  3. Set up the app for testing:

    • For Android, this is done on the Google Play Developer Console.
    • For iOS, this is done on iTunes Connect. Make sure that your App ID (created in the “Member Center” on https://developer.apple.com) has “In-App Purchase” enabled.

    iTunes Connect and Google Play Dev Console

  4. For Google Play, you need to upload and publish an alpha .apk file. For iTunes Connect, you should not upload the development binary to iTunes Connect until the application is ready for App Review approval. If you upload a binary to iTunes Connect and it is not fully functional, Apple will likely reject it.

  5. Create products for your app.

    iTunes Products

    Google Play Products

  6. Add test users.
    • The iTunes Connect page Users and Roles allow you to add users that can do test purchases in the sandbox environment. You should sign your app with a Developer certificate and use the sandbox account in Appstore on the test device.
    • From the Google Play Developer Console, choose Settings > Account Details where you can add user emails to the License Testing section. Separate the emails by commas. This allows your testers to use test purchases that don’t actually cost real money.
    • On Google Play, you also need to set up a Google Group for your testers. Google uses Groups to manage testers that can download your app from the Alpha and Beta stores. Click on the Alpha Testing tab and then Manage list of testers to add your Google Group as Alpha testers. The app must have passed through alpha publishing before you can see the opt-in link.

Alpha testers

The procedure on Facebook:

  1. Make sure you are a registered Facebook developer. Go to Facebook for developers, “My Apps” and “Register as a developer”, follow the steps.
  2. Facebook has extensive payment functionality and requires support of both synchronous and asynchronous payments. More info here Payment overview
  3. Set up app hosting and callback server:
  4. Set up you canvas app. Follow the steps on Facebook Developer Dashboard.
  5. Add test users. This is done in the “Canvas Payments” section of the app dashboard.
  6. Create products for your app Defining products.

Asynchronous transactions

The IAP API is asynchronous, meaning that after each request that your program sends to the server, the program will not halt and wait for a response. Instead, the program continues as ordinary and when the response arrives, a callback function is invoked where you can react to the response data.

To fetch all product information available:

local COINS_ID = "com.defold.examples.coins"
local LOGO_ID = "com.defold.examples.logo"

local function product_list(self, products, error)
    if error == nil then
        for i,p in pairs(products) do
            print(p.ident)
            print(p.title)
            print(p.description)
            print(p.currency_code)
            print(p.price_string)
        end
    else
        print(error.error)
    end
end

function init(self)
    -- Initiate a fetch of products (max 20 at a time for Google Play)
    iap.list({ COINS_ID, LOGO_ID }, product_list)
end

To perform actual transactions, first register a function that will listen to transaction results, then call the store function at the appropriate time:

local function iap_listener(self, transaction, error)
    if error == nil then
        if transaction.state == iap.TRANS_STATE_PURCHASING then
            print("Purchasing...")
        elseif transaction.state == iap.TRANS_STATE_PURCHASED then
            print("Purchased!")
        elseif transaction.state == iap.TRANS_STATE_UNVERIFIED then
            print("Unverified!")
        elseif transaction.state == iap.TRANS_STATE_FAILED then
            print("Failed!")
        elseif transaction.state == iap.TRANS_STATE_RESTORED then
            print("Restored")
        end
    else
        print(error.error)
    end
end

function on_message(self, message_id, message, sender)
    ...
    -- Register the function that will listen to IAP transactions.
    iap.set_listener(iap_listener)
    -- Initiate a purchase of a coin...
    iap.buy(COINS_ID)
    ...
end

The device operating system will automatically show a pop-up window allowing the user to go through with the purchase. The interface clearly indicates when you are running in the test/sandbox environment.

Confirm purchase

Android purchase

Confirm purchase

Synchronous payments

Most payment providers only supports synchronous payments. This means that the client (your application) will receive a notification when the payment is complete, TRANS_STATE_PURCHASED. This is the final state of the payment, meaning no more callbacks will be done on this transaction.

Asynchronous payments

Some payment providers require supporting asynchronous payments. This means that the client (your application) will only receive a notification when the payment is initiated. In order to verify completion of payment, further communication needs to be done between the developer server (or client) and the payment provider in order to verify. In the case of an initiated asynchronous payment the IAP listener will receive the state TRANS_STATE_UNVERIFIED to indicate this (as opposed to TRANS_STATE_PURCHASED). This is the final state of the payment, meaning no more callbacks will be done on this transaction.

Purchase fulfilment

In order to complete a purchase from a payment provider, the application needs to signal a purchase fulfilment to the provider telling the provider the purchase has gone through (for example by developer server-side verification). IAP supports auto-completion, where fulfilment is automatically signalled to the provider when a purchase is complete (this is the default behavior). You can also disable auto-completion in the game project settings. You are then required to call iap.finish() when the transaction is complete, which will signal purchase fulfilment to the provider.

Consumable vs non-consumable products

The Google Play store does only support consumable products. If you need non-consumable products it is recommended to use manual fulfillment of purchases and never finish purchases for products that should be non-consumable. As long as a purchase isn’t finished it will be returned as an active purchase when iap.set_listener() is called.

The Apple App Store supports non-consumable products which means that you need to finish all purchases when you provide products to your users. You can do it automatically by keeping the default behavior in the game project settings or manually (if you want to do that after server validation, for example) using iap.finish().

Transaction receipt

The receipt is a signed chunk of data that can be sent to the App Store to verify that the payment was successfully processed. This is most useful when designing a store that uses a separate server to verify that payment was processed.

Differences between supported platforms

Amazon supports two different product types: subscriptions and consumable products.

Google Play and Apple supports three different product types: subscriptions, consumable and non-consumable products.

If you want to simulate non-consumable products on Amazon you need to make sure to not call iap.finish() on the product in question (and make sure to not have enabled Auto Finish Transactions in game.project).

Calls to iap.buy() and iap.set_listener() will return all non-finished purchases on Google Play. (This will not happen on iOS)

The concept of restoring purchases does not exist on Google Play/Amazon. Calls to iap.restore() on iOS will return all purchased products (and have product state set to TRANS_STATE_RESTORED). Calls to iap.restore() on Google Play will return all non-finished purchases (and have product state set to TRANS_STATE_PURCHASED).

Troubleshooting

Android iap.list() returns “failed to fetch product”
You need to upload and publish an .apk on the alpha or beta channels on the Google Play Developer Console. Also make sure that the time and date on your device is correct.
Android (Google Play) iap.list() never returns more than 20 products
Google has an limit of 20 products per request. The solution is to make multiple calls to iap.list() and combine the results if the number of products exceeds 20.
iOS iap.list() returns nothing
Make sure that you’ve requested an iOS Paid Applications account, and all proper documentation has been filed. Without proper authorization, your iOS app purchasing (even test purchases) will not work.

Check that the AppId you have on the “Member Center” has in-app purchases activated and that you are signing your app (or the dev-app) with a provisioning profile that is up to date with the AppId (check the “Enabled Services:” field in the provisioning profile details in the “Certificates, Identifiers & Profiles” area of “Member Center”)

Wait. It can take a few hours for the In-App product IDs to propagate to the Sandbox environment.

iOS iap.list() fails logging error “Unexpected callback set”
iap.list() does not support nested calls. Calling iap.list() from an iap.list() callback function will be ignored, with the engine logging this error.
On iOS, the “price_string” field contains ‘~’ characters
The ‘~’ characters are placeholders where no matching character could be found in the font file. The “price_string” field returned in the product list when using iap.list() is formatted with a non breaking space (\u00a0) between the value and the currency denominator. If you render this string in the GUI, you need to add the character to the font’s extra_characters field. On Mac OS X you can type non breaking spaces by pressing Option + SPACE. See http://en.wikipedia.org/wiki/Non-breaking_space for more information.

Source code

The source code is available on GitHub

API reference

API Reference


Did you spot an error or do you have a suggestion? Please let us know on GitHub!

GITHUB