Attention

You are reading the latest (unstable) version of this documentation, which may document features not available or compatible with Godot 3.x.

Work in progress

Godot documentation is being updated to reflect the latest changes in version 4.0. Some documentation pages may still state outdated information. This banner will tell you if you're reading one of such pages.

The contents of this page can be outdated. If you know how to improve this page or you can confirm that it's up to date, feel free to open a pull request.

Android in-app purchases

Godot offers a first-party GodotGooglePlayBilling Android plugin compatible with Godot 3.2.2 and higher. This plugin uses the Google Play Billing library instead of the now deprecated AIDL IAP implementation. For details of how to migrate from the older GodotPaymentsV3, see the migration guide: Migrating from Godot 3.2.1 and lower (GodotPaymentsV3).

If you learn better by looking at an example, you can find the demo project here.

Usage

Getting started

Make sure you have enabled and successfully set up Android Custom Builds. Follow the compiling instructions on the GodotGooglePlayBilling github page.

Note

If you use a custom build you possibly have to put your own godot-lib.***.release.aar file in the ./godot-google-play-billing/libs/ folder.

Then put the files ./godot-google-play-billing/build/outputs/aar/GodotGooglePlayBilling.***.release.aar and ./GodotGooglePlayBilling.gdap into your project in the res://android/plugins folder.

The plugin should now show up in the Android export settings, where you can enable it.

Initialize the plugin

To use the GodotGooglePlayBilling API:

  1. Obtain a reference to the GodotGooglePlayBilling singleton

  2. Connect handlers for the plugin signals

  3. Call startConnection

Initialization example:

var payment

func _ready():
    if Engine.has_singleton("GodotGooglePlayBilling"):
        payment = Engine.get_singleton("GodotGooglePlayBilling")

        # These are all signals supported by the API
        # You can drop some of these based on your needs
        payment.billing_resume.connect(_on_billing_resume) # No params
        payment.connected.connect(_on_connected) # No params
        payment.disconnected.connect(_on_disconnected) # No params
        payment.connect_error.connect(_on_connect_error) # Response ID (int), Debug message (string)
        payment.price_change_acknowledged.connect(_on_price_acknowledged) # Response ID (int)
        payment.purchases_updated.connect(_on_purchases_updated) # Purchases (Dictionary[])
        payment.purchase_error.connect(_on_purchase_error) # Response ID (int), Debug message (string)
        payment.sku_details_query_completed.connect(_on_sku_details_query_completed) # SKUs (Dictionary[])
        payment.sku_details_query_error.connect(_on_sku_details_query_error) # Response ID (int), Debug message (string), Queried SKUs (string[])
        payment.purchase_acknowledged.connect(_on_purchase_acknowledged) # Purchase token (string)
        payment.purchase_acknowledgement_error.connect(_on_purchase_acknowledgement_error) # Response ID (int), Debug message (string), Purchase token (string)
        payment.purchase_consumed.connect(_on_purchase_consumed) # Purchase token (string)
        payment.purchase_consumption_error.connect(_on_purchase_consumption_error) # Response ID (int), Debug message (string), Purchase token (string)
        payment.query_purchases_response.connect(_on_query_purchases_response) # Purchases (Dictionary[])

        payment.startConnection()
    else:
        print("Android IAP support is not enabled. Make sure you have enabled 'Custom Build' and the GodotGooglePlayBilling plugin in your Android export settings! IAP will not work.")

The API must be in a connected state prior to use. The connected signal is sent when the connection process succeeds. You can also use isReady() to determine if the plugin is ready for use. The getConnectionState() function returns the current connection state of the plugin.

Return values for getConnectionState():

# Matches BillingClient.ConnectionState in the Play Billing Library
enum ConnectionState {
    DISCONNECTED, # not yet connected to billing service or was already closed
    CONNECTING, # currently in process of connecting to billing service
    CONNECTED, # currently connected to billing service
    CLOSED, # already closed and shouldn't be used again
}

Query available items

Once the API has connected, query SKUs using querySkuDetails(). You must successfully complete a SKU query before before calling the purchase() or queryPurchases() functions, or they will return an error. querySkuDetails() takes two parameters: an array of SKU name strings, and a string specifying the type of SKU being queried. The SKU type string should be "inapp" for normal in-app purchases or "subs" for subscriptions. The name strings in the array should match the SKU product ids defined in the Google Play Console entry for your app.

Example use of querySkuDetails():

func _on_connected():
  payment.querySkuDetails(["my_iap_item"], "inapp") # "subs" for subscriptions

func _on_sku_details_query_completed(sku_details):
  for available_sku in sku_details:
    print(available_sku)

func _on_sku_details_query_error(response_id, error_message, skus_queried):
    print("on_sku_details_query_error id:", response_id, " message: ",
            error_message, " skus: ", skus_queried)

Query user purchases

To retrieve a user's purchases, call the queryPurchases() function passing a string with the type of SKU to query. The SKU type string should be "inapp" for normal in-app purchases or "subs" for subscriptions. The query_purchases_response signal is sent with the result. The signal has a single parameter: a Dictionary with a status code and either an array of purchases or an error message. Only active subscriptions and non-consumed one-time purchases are included in the purchase array.

Example use of queryPurchases():

func _query_purchases():
    payment.queryPurchases("inapp") # Or "subs" for subscriptions

func _on_query_purchases_response(query_result):
    if query_result.status == OK:
        for purchase in query_result.purchases:
            _process_purchase(purchase)
    else:
        print("queryPurchases failed, response code: ",
                query_result.response_code,
                " debug message: ", query_result.debug_message)

You should query purchases during startup after successfully retrieving SKU details. Since the user may make a purchase or resolve a pending transaction from outside your app, you should recheck for purchases when resuming from the background. To accomplish this, you can use the billing_resume signal.

Example use of billing_resume:

func _on_billing_resume():
    if payment.getConnectionState() == ConnectionState.CONNECTED:
        _query_purchases()

For more information on processing the purchase items returned by queryPurchases(), see Processing a purchase item

Purchase an item

To initiate the purchase flow for an item, call purchase() passing the product id string of the SKU you wish to purchase. Reminder: you must query the SKU details for an item before you can pass it to purchase().

Example use of purchase():

payment.purchase("my_iap_item")

The payment flow will send a purchases_updated signal on success or a purchase_error signal on failure.

func _on_purchases_updated(purchases):
    for purchase in purchases:
        _process_purchase(purchase)

func _on_purchase_error(response_id, error_message):
    print("purchase_error id:", response_id, " message: ", error_message)

Processing a purchase item

The query_purchases_response and purchases_updated signals provide an array of purchases in Dictionary format. The purchase Dictionary includes keys that map to values of the Google Play Billing Purchase class.

Purchase fields:

dictionary.put("order_id", purchase.getOrderId());
dictionary.put("package_name", purchase.getPackageName());
dictionary.put("purchase_state", purchase.getPurchaseState());
dictionary.put("purchase_time", purchase.getPurchaseTime());
dictionary.put("purchase_token", purchase.getPurchaseToken());
dictionary.put("quantity", purchase.getQuantity());
dictionary.p