Android 應用程式內購
Godot 提供官方的 GodotGooglePlayBilling Android 外掛,相容於 Godot 4.2+,並使用 Google Play Billing 函式庫。
用法
入門
請確保你已啟用並成功設定 Android Gradle Builds。接著依照 GodotGooglePlayBilling 的 GitHub 頁面 上的安裝說明操作。
初始化外掛程式
要使用 GodotGooglePlayBilling API:
Access the
BillingClient.連接其訊號以接收交易結果。
呼叫
start_connection。
初始化範例:
var billing_client: BillingClient
func _ready():
billing_client = BillingClient.new()
billing_client.connected.connect(_on_connected) # No params
billing_client.disconnected.connect(_on_disconnected) # No params
billing_client.connect_error.connect(_on_connect_error) # response_code: int, debug_message: String
billing_client.query_product_details_response.connect(_on_query_product_details_response) # response: Dictionary
billing_client.query_purchases_response.connect(_on_query_purchases_response) # response: Dictionary
billing_client.on_purchase_updated.connect(_on_purchase_updated) # response: Dictionary
billing_client.consume_purchase_response.connect(_on_consume_purchase_response) # response: Dictionary
billing_client.acknowledge_purchase_response.connect(_on_acknowledge_purchase_response) # response: Dictionary
billing_client.start_connection()
API 必須在連線狀態下才能使用。當連線成功時會送出 connected 訊號。你也可以使用 is_ready() 判斷外掛是否已就緒。get_connection_state() 函式會回傳外掛目前的連線狀態。
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.
}
查詢可購買項目
API 連線後, 請使用 query_product_details() 查詢產品 ID。在呼叫 purchase() 、 purchase_subscription() 或 update_subscription() 前, 必須先成功查詢產品詳細資料, 否則會回傳錯誤。 query_product_details() 需要兩個參數: 產品 ID 字串陣列, 以及要查詢的產品類型。一般應用內購買請用 BillingClient.ProductType.INAPP , 訂閱則用 BillingClient.ProductType.SUBS. 陣列中的 ID 字串必須與你在 Google Play Console 為 app 設定的產品 ID 一致。
query_product_details() 的使用範例:
func _on_connected():
billing_client.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.product_details:
print(available_product)
else:
print("Product details query failed")
print("response_code: ", query_result.response_code, "debug_message: ", query_result.debug_message)
查詢用戶購買紀錄
要取得使用者的購買紀錄,請呼叫 query_purchases() 並傳入要查詢的產品類型。一般應用內購買請用 BillingClient.ProductType.INAPP,訂閱則用 BillingClient.ProductType.SUBS。查詢結果會透過 query_purchases_response 訊號傳回。該訊號有一個參數:一個 Dictionary,包含回應代碼以及購買項目陣列或除錯訊息。購買陣列只包含有效訂閱與尚未消耗的一次性購買。
query_purchases() 的使用範例:
func _query_purchases():
billing_client.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.purchases:
_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.
對於 purchase() 與 purchase_subscription() , 你可以選擇性地傳入布林值, 以指示是否使用 個人化優惠
提醒: 在把某項目傳給 purchase() 前, 你 必須 先查詢該項目的產品詳細資料。此方法會回傳一個字典, 表示結帳流程是否成功啟動。其中包含回應代碼以及購買陣列或除錯訊息。
purchase() 的使用範例:
var result = billing_client.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)
購買結果會透過 on_purchases_updated 訊號傳送。
func _on_purchases_updated(result: Dictionary):
if result.response_code == BillingClient.BillingResponseCode.OK:
print("Purchase update received")
for purchase in result.purchases:
_process_purchase(purchase)
else:
print("Purchase update error")
print("response_code: ", result.response_code, "debug_message: ", result.debug_message)
處理購買項目
query_purchases_response 與 on_purchases_updated 訊號會提供一個 Dictionary 格式的購買紀錄陣列。每筆購買的字典鍵值會對應到 Google Play Billing 的 Purchase 類別欄位。
購買欄位:
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 Billing 文件中的 處理待處理交易。
消耗品
若你的應用內項目不是一次性購買,而是可重複購買的消耗品(例如金幣),可呼叫 consume_purchase() 並傳入購買字典中的 purchase_token 來消耗該項目。呼叫 consume_purchase() 會自動確認該筆購買。消耗後,使用者即可再次購買;除非再次購買,否則它不會出現在後續的 query_purchases() 查詢結果中。
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
billing_client.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
確認購買
若你的應用內項目為一次性購買,必須呼叫 acknowledge_purchase() 並傳入購買字典內的 purchase_token 來確認該筆交易。若三天內未確認,使用者會自動獲得退款,且 Google Play 會撤銷該筆購買。若你已呼叫 consume_purchase(),則會自動完成確認,不需再呼叫 acknowledge_purchase()。
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
billing_client.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
訂閱
訂閱大致與一般應用內項目相同。要取得訂閱詳細資料,請在 query_product_details() 的第二個參數傳入 BillingClient.ProductType.SUBS。要取得訂閱購買紀錄,請在 query_purchases() 傳入 BillingClient.ProductType.SUBS。
你可以檢查 query_purchases() 回傳之訂閱購買紀錄中的 is_auto_renewing,以判斷使用者是否已取消自動續訂。
新訂閱購買需進行確認,但自動續訂不需再次確認。
如果你的 App 支援在不同訂閱等級間升級或降級,應使用 update_subscription() 來進行訂閱更新流程以變更現有的有效訂閱。與 purchase() 相同,結果會透過 on_purchases_updated 訊號回傳。以下是 update_subscription() 的參數:
old_purchase_token:目前有效訂閱的購買憑證(purchase token)
replacement_mode:要套用於該訂閱的替換模式
product_id:要切換到的新訂閱之產品 ID
base_plan_id:目標訂閱的基礎方案 ID
offer_id:基礎方案下的優惠 ID(選用)
is_offer_personalized:是否啟用個人化定價(選用)
替換模式的值定義如下:
# 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,
}
預設行為為 WITH_TIME_PRORATION。
update_subscription() 的使用範例:
billing_client.update_subscription(_active_subscription_purchase.purchase_token, \
BillingClient.ReplacementMode.WITH_TIME_PRORATION, "new_sub_product_id", "base_plan_id")