Up to date

This page is up to date for Godot 4.2. If you still find outdated information, please open an issue.

Android In-App-Käufe

Godot bietet ein eigenes GodotGooglePlayBilling Android-Plugin, das mit Godot 4 kompatibel ist und die Google Play Billing-Bibliothek verwendet.

Verwendung

Erste Schritte

Stellen Sie sicher, dass Sie Android Gradle-Builds aktiviert und erfolgreich eingerichtet haben. Folgen Sie den Kompilierungsanweisungen auf der GodotGooglePlayBilling Github-Seite.

Legen Sie dann die Dateien ./godot-google-play-billing/build/outputs/aar/GodotGooglePlayBilling.***.release.aar und ./GodotGooglePlayBilling.gdap in Ihr Projekt im Ordner res://android/plugins.

Das Plugin sollte nun in den Android-Exporteinstellungen auftauchen, wo Sie es aktivieren können.

Initialisieren Sie das Plugin

Um die GodotGooglePlayBilling-API zu verwenden:

  1. Beschaffen Sie sich eine Referenz auf das Singleton GodotGooglePlayBilling

  2. Verknüpfen Sie Handler für die Signale des Plugins

  3. Rufen Sie startConnection auf

Initialisierungsbeispiel:

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_product_details_query_completed) # Products (Dictionary[])
        payment.sku_details_query_error.connect(_on_product_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 'Gradle Build' and the GodotGooglePlayBilling plugin in your Android export settings! IAP will not work.")

Die API muss sich vor der Verwendung in einem Verbindungszustand befinden. Das Signal connected wird gesendet, wenn der Verbindungsprozess erfolgreich war. Sie können auch isReady() verwenden, um festzustellen, ob das Plugin bereit zur Benutzung ist. Die Funktion getConnectionState() gibt den aktuellen Verbindungsstatus des Plugins zurück.

Rückgabewerte für 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
}

Verfügbare Elemente abfragen

Sobald die API verbunden ist, können Sie die SKUs mit querySkuDetails() abfragen. Sie müssen eine SKU-Abfrage erfolgreich abschließen, bevor Sie die Funktionen purchase() oder queryPurchases() aufrufen, sonst geben diese einen Fehler zurück. Die Funktion querySkuDetails() benötigt zwei Parameter: ein Array von SKU-Namen und einen String, der den Typ der abgefragten SKU angibt. Der SKU-Typ String sollte "inapp" für normale In-App Käufe oder "subs" für Abonnements sein. Die Namensstrings im Array sollten mit den SKU-Produkt-IDs übereinstimmen, die im Eintrag in der Google Play Console für Ihre App definiert sind.

Beispiel für die Verwendung von querySkuDetails():

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

func _on_product_details_query_completed(product_details):
  for available_product in product_details:
    print(available_product)

func _on_product_details_query_error(response_id, error_message, products_queried):
    print("on_product_details_query_error id:", response_id, " message: ",
            error_message, " products: ", products_queried)

Abfrage von Nutzerkäufen

Um die Einkäufe eines Benutzers abzufragen, rufen Sie die Funktion queryPurchases() auf und übergeben einen String mit dem Typ der abzufragenden SKU. Der SKU-Typ String sollte "inapp" für normale In-App-Käufe oder "subs" für Abonnements sein. Das Signal query_purchases_response wird mit dem Ergebnis gesendet. Das Signal hat einen einzigen Parameter: ein Dictionary mit einem Statuscode und entweder einem Array von Käufen oder einer Fehlermeldung. Nur aktive Abonnements und nicht verbrauchte einmalige Käufe werden in das Kauf-Array aufgenommen.

Beispiel für die Verwendung von 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)

Sie sollten Käufe während des Starts abfragen, nachdem Sie erfolgreich SKU-Details abgerufen haben. Da der Benutzer möglicherweise einen Kauf tätigt oder eine ausstehende Transaktion von außerhalb Ihrer Anwendung auflöst, sollten Sie bei der Wiederaufnahme aus dem Hintergrund erneut nach Käufen suchen. Um dies zu erreichen, können Sie das Signal billing_resume verwenden.

Beispiel für die Verwendung von billing_resume:

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

Weitere Informationen zur Verarbeitung der von queryPurchases() zurückgegebenen Einkaufsposten finden Sie unter Bearbeitung eines Einkaufspostens

Kauf eines Artikels

Um den Kaufvorgang für einen Artikel zu starten, rufen Sie purchase() auf und übergeben die Produkt-ID der SKU, die Sie kaufen möchten. Zur Erinnerung: Sie müssen die SKU-Details für einen Artikel abfragen, bevor Sie ihn an purchase() übergeben können.

Beispiel für die Verwendung von purchase():

payment.purchase("my_iap_item")

Der Zahlungsfluss sendet ein purchases_updated-Signal bei Erfolg oder ein purchase_error-Signal bei Misserfolg.

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)

Bearbeitung eines Kauf-Artikels

Die Signale query_purchases_response und purchases_updated liefern ein Array von Käufen im Format Dictionary. Das Dictionary für Einkäufe enthält Schlüssel, die auf Werte der Google Play Billing-Klasse Einkäufe abgebildet werden.

Kauf-Felder:

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.put("signature", purchase.getSignature());
// PBL V4 replaced getSku with getSkus to support multi-sku purchases,
// use the first entry for "sku" and generate an array for "skus"
ArrayList<String> skus = purchase.getSkus();
dictionary.put("sku", skus.get(0)); # Not available in plugin
String[] skusArray = skus.toArray(new String[0]);
dictionary.put("products", productsArray);
dictionary.put("is_acknowledged", purchase.isAcknowledged());
dictionary.put("is_auto_renewing", purchase.isAutoRenewing());

Kaufstatus prüfen

Überprüfen Sie den Wert purchase_state eines Kaufs, um festzustellen, ob ein Kauf abgeschlossen wurde oder noch aussteht.

PurchaseState-Werte:

# Matches Purchase.PurchaseState in the Play Billing Library
enum PurchaseState {
    UNSPECIFIED,
    PURCHASED,
    PENDING,
}

Wenn sich ein Kauf in einem PENDING-Status befindet, sollten Sie den Inhalt des Kaufs nicht vergeben oder den Kauf weiter bearbeiten, bis er den PURCHASED-Status erreicht. Wenn Sie eine Store-Schnittstelle haben, möchten Sie vielleicht Informationen über ausstehende Käufe anzeigen, die im Google Play Store abgeschlossen werden müssen. Weitere Einzelheiten zu ausstehenden Käufen finden Sie unter Umgang mit ausstehenden Transaktionen in der Dokumentation der Google Play Billing Library.

Verbrauchsartikel

Wenn es sich bei Ihrem In-App Gegenstand nicht um einen einmaligen Kauf handelt, sondern um einen Verbrauchsgegenstand (z.B. Münzen), der mehrmals gekauft werden kann, können Sie einen Gegenstand verbrauchen, indem Sie consumePurchase() aufrufen und den Wert purchase_token aus dem Kaufverzeichnis übergeben. Der Aufruf von consumePurchase() bestätigt automatisch einen Kauf. Wenn der Benutzer ein Produkt konsumiert, kann er es erneut kaufen. Es erscheint dann nicht mehr in nachfolgenden queryPurchases() Aufrufen, es sei denn, es wird erneut gekauft.

Beispiel für die Verwendung von consumePurchase():

func _process_purchase(purchase):
    if "my_consumable_iap_item" in purchase.products and purchase.purchase_state == PurchaseState.PURCHASED:
        # Add code to store payment so we can reconcile the purchase token
        # in the completion callback against the original purchase
        payment.consumePurchase(purchase.purchase_token)

func _on_purchase_consumed(purchase_token):
    _handle_purchase_token(purchase_token, true)

func _on_purchase_consumption_error(response_id, error_message, purchase_token):
    print("_on_purchase_consumption_error id:", response_id,
            " message: ", error_message)
    _handle_purchase_token(purchase_token, false)

# Find the sku associated with the purchase token and award the
# product if successful
func _handle_purchase_token(purchase_token, purchase_successful):
    # check/award logic, remove purchase from tracking list

Bestätigung von Käufen

Wenn es sich bei Ihrem In-App-Artikel um einen einmaligen Kauf handelt, müssen Sie den Kauf bestätigen, indem Sie die Funktion acknowledgePurchase() aufrufen und den Wert purchase_token aus dem Kauf-Dictionary übergeben. Wenn Sie einen Kauf nicht innerhalb von drei Tagen bestätigen, erhält der Nutzer automatisch eine Rückerstattung, und Google Play widerruft den Kauf. Wenn Sie comsumePurchase() aufrufen, wird der Kauf automatisch bestätigt und Sie müssen acknowledgePurchase() nicht aufrufen.

Beispiel für die Verwendung von acknowledgePurchase():

func _process_purchase(purchase):
    if "my_one_time_iap_item" in purchase.products and \
            purchase.purchase_state == PurchaseState.PURCHASED and \
            not purchase.is_acknowledged:
        # Add code to store payment so we can reconcile the purchase token
        # in the completion callback against the original purchase
        payment.acknowledgePurchase(purchase.purchase_token)

func _on_purchase_acknowledged(purchase_token):
    _handle_purchase_token(purchase_token, true)

func _on_purchase_acknowledgement_error(response_id, error_message, purchase_token):
    print("_on_purchase_acknowledgement_error id: ", response_id,
            " message: ", error_message)
    _handle_purchase_token(purchase_token, false)

# Find the sku associated with the purchase token and award the
# product if successful
func _handle_purchase_token(purchase_token, purchase_successful):
    # check/award logic, remove purchase from tracking list

Abonnements

Abonnements funktionieren größtenteils wie normale In-App-Artikel. Verwenden Sie "subs" als zweites Argument für querySkuDetails(), um Details zu Abonnements zu erhalten. Übergeben Sie "subs" an queryPurchases(), um Details zum Abonnementkauf zu erhalten.

Sie können is_auto_renewing in einem Abonnementkauf, der von queryPurchases() zurückgegeben wird, überprüfen, ob ein Benutzer ein sich automatisch verlängerndes Abonnement gekündigt hat.

Sie müssen neue Abonnementkäufe bestätigen, aber nicht automatische Abonnementverlängerungen.

Wenn Sie Upgrades oder Downgrades zwischen verschiedenen Abonnementstufen unterstützen, sollten Sie updateSubscription() verwenden, um den Abonnement-Aktualisierungsfluss zu nutzen, um ein aktives Abonnement zu ändern. Wie bei purchase() werden die Ergebnisse durch die Signale purchases_updated und purchase_error zurückgegeben. Es gibt drei Parameter für updateSubscription():

  1. Das Kauf-Token des derzeit aktiven Abonnements

  2. Der Produkt-ID-String der Abonnement-SKU, die geändert werden soll

  3. Der auf das Abonnement anzuwendende Anteilsmodus.

Die Anteilswerte sind wie folgt definiert:

enum SubscriptionProrationMode {
    # Replacement takes effect immediately, and the remaining time
    # will be prorated and credited to the user.
    IMMEDIATE_WITH_TIME_PRORATION = 1,
    # Replacement takes effect immediately, and the billing cycle remains the same.
    # The price for the remaining period will be charged.
    # This option is only available for subscription upgrade.
    IMMEDIATE_AND_CHARGE_PRORATED_PRICE,
    # Replacement takes effect immediately, and the new price will be charged on
    # next recurrence time. The billing cycle stays the same.
    IMMEDIATE_WITHOUT_PRORATION,
    # Replacement takes effect when the old plan expires, and the new price
    # will be charged at the same time.
    DEFERRED,
    # Replacement takes effect immediately, and the user is charged full price
    # of new plan and is given a full billing cycle of subscription,
    # plus remaining prorated time from the old plan.
    IMMEDIATE_AND_CHARGE_FULL_PRICE,
}

Das Default-Verhalten ist IMMEDIATE_WITH_TIME_PRORATION.

Beispiel für die Verwendung von updateSubscription:

payment.updateSubscription(_active_subscription_purchase.purchase_token, \
                                            "new_sub_sku", SubscriptionProrationMode.IMMEDIATE_WITH_TIME_PRORATION)

Die Funktion confirmPriceChange() kann verwendet werden, um eine Preisänderung für ein Abonnement zu bestätigen. Übergeben Sie die Produkt-ID der Abonnement-SKU, die Gegenstand der Preisänderung ist. Das Ergebnis wird mit dem Signal price_change_acknowledged gesendet.

Beispiel für die Verwendung von confirmPriceChange():

enum BillingResponse {SUCCESS = 0, CANCELLED = 1}

func confirm_price_change(product_id):
    payment.confirmPriceChange(product_id)

func _on_price_acknowledged(response_id):
    if response_id == BillingResponse.SUCCESS:
        print("price_change_accepted")
    elif response_id == BillingResponse.CANCELED:
        print("price_change_canceled")