Attention: Here be dragons
This is the latest
(unstable) version of this documentation, which may document features
not available in or compatible with released stable versions of Godot.
Checking the stable version of the documentation...
Android 应用内购买
Godot 提供与 Godot 4 兼容的第一方 GodotGooglePlayBilling
Android 插件,该插件使用 Google Play 结算系统库。
用法
入门
Make sure you have enabled and successfully set up Android Gradle Builds.
Follow the installation instructions on the GodotGooglePlayBilling
github page.
初始化插件
要使用 GodotGooglePlayBilling
API:
Access the
BillingClient
autoload singleton, it's automatically added when the plugin is enabled.Connect to its signals to receive billing results.
Call
start_connection
.
初始化示例:
var billing_client
func _ready():
BillingClient.connected.connect(_on_connected) # No params
BillingClient.disconnected.connect(_on_disconnected) # No params
BillingClient.connect_error.connect(_on_connect_error) # response_code: int, debug_message: String
BillingClient.query_product_details_response.connect(_on_query_product_details_response) # response: Dictionary
BillingClient.query_purchases_response.connect(_on_query_purchases_response) # response: Dictionary
BillingClient.on_purchase_updated.connect(_on_purchase_updated) # response: Dictionary
BillingClient.consume_purchase_response.connect(_on_consume_purchase_response) # response: Dictionary
BillingClient.acknowledge_purchase_response.connect(_on_acknowledge_purchase_response) # response: Dictionary
BillingClient.start_connection()
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 get_connection_state()
function returns the current connection state
of the plugin.
Return values for get_connection_state()
:
# Matches BillingClient.ConnectionState in the Play Billing Library.
# Access in your script as: BillingClient.ConnectionState.CONNECTED
enum ConnectionState {
DISCONNECTED, # This client was not yet connected to billing service or was already closed.
CONNECTING, # This client is currently in process of connecting to billing service.
CONNECTED, # This client is currently connected to billing service.
CLOSED, # This client was already closed and shouldn't be used again.
}
查询可用项
Once the API has connected, query product IDs using query_product_details(). You must successfully complete
a product details query before calling the purchase()
, purchase_subscription()
, or update_subscription()
functions,
or they will return an error. query_product_details()
takes two parameters: an array
of product ID strings and the type of product being queried.
The product type should be BillingClient.ProductType.INAPP
for normal in-app purchases or BillingClient.ProductType.SUBS
for subscriptions.
The ID strings in the array should match the product IDs defined in the Google Play Console entry
for your app.
Example use of query_product_details()
:
func _on_connected():
BillingClient.query_product_details(["my_iap_item"], BillingClient.ProductType.INAPP) # BillingClient.ProductType.SUBS for subscriptions.
func _on_query_product_details_response(query_result: Dictionary):
if query_result.response_code == BillingClient.BillingResponseCode.OK:
print("Product details query success")
for available_product in query_result.result_array:
print(available_product)
else:
print("Product details query failed")
print("response_code: ", query_result.response_code, "debug_message: ", query_result.debug_message)
查询用户购买记录
To retrieve a user's purchases, call the query_purchases()
function passing
a product type to query. The product type should be
BillingClient.ProductType.INAPP
for normal in-app purchases or BillingClient.ProductType.SUBS
for subscriptions.
The query_purchases_response
signal is sent with the result.
The signal has a single parameter: a Dictionary with
a response code and either an array of purchases or a debug message.
Only active subscriptions and non-consumed one-time purchases are
included in the purchase array.
Example use of query_purchases()
:
func _query_purchases():
BillingClient.query_purchases(BillingClient.ProductType.INAPP) # Or BillingClient.ProductType.SUBS for subscriptions.
func _on_query_purchases_response(query_result: Dictionary):
if query_result.response_code == BillingClient.BillingResponseCode.OK:
print("Purchase query success")
for purchase in query_result.result_array:
_process_purchase(purchase)
else:
print("Purchase query failed")
print("response_code: ", query_result.response_code, "debug_message: ", query_result.debug_message)
购买项目
To launch the billing flow for an item:
- Use purchase()
for in-app products, passing the product ID string.
- Use purchase_subscription()
for subscriptions, passing the product ID and base plan ID. You may also optionally provide an offer ID.
For both purchase()
and purchase_subscription()
, you can optionally pass a boolean to indicate whether
offers are personallised
Reminder: you must query the product details for an item before you can
pass it to purchase()
.
This method returns a dictionary indicating whether the billing flow was successfully launched.
It includes a response code and either an array of purchases or a debug message.
purchase()
的示例用法:
var result = BillingClient.purchase("my_iap_item")
if result.response_code == BillingClient.BillingResponseCode.OK:
print("Billing flow launch success")
else:
print("Billing flow launch failed")
print("response_code: ", result.response_code, "debug_message: ", result.debug_message)
The result of the purchase will be sent through the on_purchases_updated
signal.
func _on_purchases_updated(result: Dictionary):
if result.response_code == BillingClient.BillingResponseCode.OK:
print("Purchase update received")
for purchase in result.result_array:
_process_purchase(purchase)
else:
print("Purchase update error")
print("response_code: ", result.response_code, "debug_message: ", result.debug_message)
处理购买项目
The query_purchases_response
and on_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.
购买项目:
order_id: String
purchase_token: String
package_name: String
purchase_state: int
purchase_time: int (milliseconds since the epoch (Jan 1, 1970))
original_json: String
is_acknowledged: bool
is_auto_renewing: bool
quantity: int
signature: String
product_ids: PackedStringArray
检查购买状态
检查购买的 purchase_state
值以确定购买已完成还是仍处于待处理状态。
PurchaseState 取值:
# Matches Purchase.PurchaseState in the Play Billing Library
# Access in your script as: BillingClient.PurchaseState.PURCHASED
enum PurchaseState {
UNSPECIFIED,
PURCHASED,
PENDING,
}
如果购买处于 PENDING
状态,则在其达到 PURCHASED
状态之前,你不应授予购买的内容或对购买进行任何进一步处理。如果你有商店界面,你可能希望显示有关需要在 Google Play 商店中完成的待处理购买的信息。有关待处理购买的更多详细信息,请参阅 Google Play 结算库文档中的处理待处理交易。
消耗品
If your in-app item is not a one-time purchase but a consumable item (e.g. coins) which can be purchased
multiple times, you can consume an item by calling consume_purchase()
passing
the purchase_token
value from the purchase dictionary.
Calling consume_purchase()
automatically acknowledges a purchase.
Consuming a product allows the user to purchase it again, it will no longer appear
in subsequent query_purchases()
calls unless it is repurchased.
Example use of consume_purchase()
:
func _process_purchase(purchase):
if "my_consumable_iap_item" in purchase.product_ids and purchase.purchase_state == BillingClient.PurchaseState.PURCHASED:
# Add code to store payment so we can reconcile the purchase token
# in the completion callback against the original purchase
BillingClient.consume_purchase(purchase.purchase_token)
func _on_consume_purchase_response(result: Dictionary):
if result.response_code == BillingClient.BillingResponseCode.OK:
print("Consume purchase success")
_handle_purchase_token(result.token, true)
else:
print("Consume purchase failed")
print("response_code: ", result.response_code, "debug_message: ", result.debug_message, "purchase_token: ", result.token)
# Find the product 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
确认购买
If your in-app item is a one-time purchase, you must acknowledge the purchase by
calling the acknowledge_purchase()
function, passing the purchase_token
value from the purchase dictionary. If you do not acknowledge a purchase within
three days, the user automatically receives a refund, and Google Play revokes the purchase.
If you are calling comsume_purchase()
it automatically acknowledges the purchase and
you do not need to call acknowledge_purchase()
.
Example use of acknowledge_purchase()
:
func _process_purchase(purchase):
if "my_one_time_iap_item" in purchase.product_ids and \
purchase.purchase_state == BillingClient.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
BillingClient.acknowledge_purchase(purchase.purchase_token)
func _on_acknowledge_purchase_response(result: Dictionary):
if result.response_code == BillingClient.BillingResponseCode.OK:
print("Acknowledge purchase success")
_handle_purchase_token(result.token, true)
else:
print("Acknowledge purchase failed")
print("response_code: ", result.response_code, "debug_message: ", result.debug_message, "purchase_token: ", result.token)
# Find the product 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
订阅
Subscriptions work mostly like regular in-app items. Use BillingClient.ProductType.SUBS
as the second
argument to query_product_details()
to get subscription details. Pass BillingClient.ProductType.SUBS
to query_purchases()
to get subscription purchase details.
You can check is_auto_renewing
in the a subscription purchase
returned from query_purchases()
to see if a user has cancelled an
auto-renewing subscription.
你需要确认新的订阅购买,但不需要确认自动订阅续订。
If you support upgrading or downgrading between different subscription levels,
you should use update_subscription()
to use the subscription update flow to
change an active subscription. Like purchase()
, results are returned by the
on_purchases_updated
signal.
These are the parameters of update_subscription()
:
old_purchase_token: The purchase token of the currently active subscription
replacement_mode: The replacement mode to apply to the subscription
product_id: The product ID of the new subscription to switch to
base_plan_id: The base plan ID of the target subscription
offer_id: The offer ID under the base plan (optional)
is_offer_personalized: Whether to enable personalized pricing (optional)
The replacement modes values are defined as:
# Access in your script as: BillingClient.ReplacementMode.WITH_TIME_PRORATION
enum ReplacementMode {
# Unknown...
UNKNOWN_REPLACEMENT_MODE = 0,
# The new plan takes effect immediately, and the remaining time will be prorated and credited to the user.
# Note: This is the default behavior.
WITH_TIME_PRORATION = 1,
# The new plan takes effect immediately, and the billing cycle remains the same.
CHARGE_PRORATED_PRICE = 2,
# The new plan takes effect immediately, and the new price will be charged on next recurrence time.
WITHOUT_PRORATION = 3,
# 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.
CHARGE_FULL_PRICE = 5,
# The new purchase takes effect immediately, the new plan will take effect when the old item expires.
DEFERRED = 6,
}
Default behavior is WITH_TIME_PRORATION
.
Example use of update_subscription
:
BillingClient.update_subscription(_active_subscription_purchase.purchase_token, \
BillingClient.ReplacementMode.WITH_TIME_PRORATION, "new_sub_product_id", "base_plan_id")