This extension provides a unified, simple to use interface to several different stores for in-app purchase:
These services gives you the opportunity to sell products as:
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:
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.
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
android.test.canceled
android.test.refunded
android.test.item_unavailable
The procedure on iOS and Android is similar:
Set up the app for testing:
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.
Create products for your app.
The procedure on Facebook:
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.
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.
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.
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.
The Google Play store does only support consumable products. If you need non-consumable products it is recommended to use manual fulfilment 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. If you do not call iap.finish()
on a purchase you still need to indicate to Google Play that the purchase has been handled. You can do this by calling iap.acknowledge()
. If you do not call iap.acknowledge()
the purchase will be automatically refunded by Google after a few days.
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()
.
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.
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).
iap.list()
returns “failed to fetch product”iap.list()
never returns more than 20 productsiap.list()
and combine the results if the number of products exceeds 20.iap.list()
returns nothingCheck 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.
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.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.The source code is available on GitHub
Did you spot an error or do you have a suggestion? Please let us know on GitHub!
GITHUB