android_in_app_purchases.rst 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. .. _doc_android_in_app_purchases:
  2. Android in-app purchases
  3. ========================
  4. Godot offers a first-party ``GodotGooglePlayBilling`` Android plugin compatible with Godot 4 which uses the `Google Play Billing library <https://developer.android.com/google/play/billing>`_.
  5. Usage
  6. -----
  7. Getting started
  8. ~~~~~~~~~~~~~~~
  9. Make sure you have enabled and successfully set up :ref:`Android Gradle Builds <doc_android_gradle_build>`.
  10. Follow the installation instructions on the ``GodotGooglePlayBilling`` `github page <https://github.com/godotengine/godot-google-play-billing>`__.
  11. Initialize the plugin
  12. ~~~~~~~~~~~~~~~~~~~~~
  13. To use the ``GodotGooglePlayBilling`` API:
  14. 1. Access the ``BillingClient`` autoload singleton, it's automatically added when the plugin is enabled.
  15. 2. Connect to its signals to receive billing results.
  16. 3. Call ``start_connection``.
  17. Initialization example:
  18. ::
  19. var billing_client
  20. func _ready():
  21. BillingClient.connected.connect(_on_connected) # No params
  22. BillingClient.disconnected.connect(_on_disconnected) # No params
  23. BillingClient.connect_error.connect(_on_connect_error) # response_code: int, debug_message: String
  24. BillingClient.query_product_details_response.connect(_on_query_product_details_response) # response: Dictionary
  25. BillingClient.query_purchases_response.connect(_on_query_purchases_response) # response: Dictionary
  26. BillingClient.on_purchase_updated.connect(_on_purchase_updated) # response: Dictionary
  27. BillingClient.consume_purchase_response.connect(_on_consume_purchase_response) # response: Dictionary
  28. BillingClient.acknowledge_purchase_response.connect(_on_acknowledge_purchase_response) # response: Dictionary
  29. BillingClient.start_connection()
  30. The API must be in a connected state prior to use. The ``connected`` signal is sent
  31. when the connection process succeeds. You can also use ``isReady()`` to determine if the plugin
  32. is ready for use. The ``get_connection_state()`` function returns the current connection state
  33. of the plugin.
  34. Return values for ``get_connection_state()``:
  35. ::
  36. # Matches BillingClient.ConnectionState in the Play Billing Library.
  37. # Access in your script as: BillingClient.ConnectionState.CONNECTED
  38. enum ConnectionState {
  39. DISCONNECTED, # This client was not yet connected to billing service or was already closed.
  40. CONNECTING, # This client is currently in process of connecting to billing service.
  41. CONNECTED, # This client is currently connected to billing service.
  42. CLOSED, # This client was already closed and shouldn't be used again.
  43. }
  44. Query available items
  45. ~~~~~~~~~~~~~~~~~~~~~
  46. Once the API has connected, query product IDs using `query_product_details()`. You must successfully complete
  47. a product details query before calling the ``purchase()``, ``purchase_subscription()``, or ``update_subscription()`` functions,
  48. or they will return an error. ``query_product_details()`` takes two parameters: an array
  49. of product ID strings and the type of product being queried.
  50. The product type should be ``BillingClient.ProductType.INAPP`` for normal in-app purchases or ``BillingClient.ProductType.SUBS`` for subscriptions.
  51. The ID strings in the array should match the product IDs defined in the Google Play Console entry
  52. for your app.
  53. Example use of ``query_product_details()``:
  54. ::
  55. func _on_connected():
  56. BillingClient.query_product_details(["my_iap_item"], BillingClient.ProductType.INAPP) # BillingClient.ProductType.SUBS for subscriptions.
  57. func _on_query_product_details_response(query_result: Dictionary):
  58. if query_result.response_code == BillingClient.BillingResponseCode.OK:
  59. print("Product details query success")
  60. for available_product in query_result.result_array:
  61. print(available_product)
  62. else:
  63. print("Product details query failed")
  64. print("response_code: ", query_result.response_code, "debug_message: ", query_result.debug_message)
  65. Query user purchases
  66. ~~~~~~~~~~~~~~~~~~~~
  67. To retrieve a user's purchases, call the ``query_purchases()`` function passing
  68. a product type to query. The product type should be
  69. ``BillingClient.ProductType.INAPP`` for normal in-app purchases or ``BillingClient.ProductType.SUBS`` for subscriptions.
  70. The ``query_purchases_response`` signal is sent with the result.
  71. The signal has a single parameter: a :ref:`Dictionary <class_Dictionary>` with
  72. a response code and either an array of purchases or a debug message.
  73. Only active subscriptions and non-consumed one-time purchases are
  74. included in the purchase array.
  75. Example use of ``query_purchases()``:
  76. ::
  77. func _query_purchases():
  78. BillingClient.query_purchases(BillingClient.ProductType.INAPP) # Or BillingClient.ProductType.SUBS for subscriptions.
  79. func _on_query_purchases_response(query_result: Dictionary):
  80. if query_result.response_code == BillingClient.BillingResponseCode.OK:
  81. print("Purchase query success")
  82. for purchase in query_result.result_array:
  83. _process_purchase(purchase)
  84. else:
  85. print("Purchase query failed")
  86. print("response_code: ", query_result.response_code, "debug_message: ", query_result.debug_message)
  87. Purchase an item
  88. ~~~~~~~~~~~~~~~~
  89. To launch the billing flow for an item:
  90. - Use ``purchase()`` for in-app products, passing the product ID string.
  91. - Use ``purchase_subscription()`` for subscriptions, passing the product ID and base plan ID. You may also optionally provide an offer ID.
  92. For both ``purchase()`` and ``purchase_subscription()``, you can optionally pass a boolean to indicate whether
  93. offers are `personallised <https://developer.android.com/google/play/billing/integrate#personalized-price>`_
  94. Reminder: you **must** query the product details for an item before you can
  95. pass it to ``purchase()``.
  96. This method returns a dictionary indicating whether the billing flow was successfully launched.
  97. It includes a response code and either an array of purchases or a debug message.
  98. Example use of ``purchase()``:
  99. ::
  100. var result = BillingClient.purchase("my_iap_item")
  101. if result.response_code == BillingClient.BillingResponseCode.OK:
  102. print("Billing flow launch success")
  103. else:
  104. print("Billing flow launch failed")
  105. print("response_code: ", result.response_code, "debug_message: ", result.debug_message)
  106. The result of the purchase will be sent through the ``on_purchases_updated`` signal.
  107. ::
  108. func _on_purchases_updated(result: Dictionary):
  109. if result.response_code == BillingClient.BillingResponseCode.OK:
  110. print("Purchase update received")
  111. for purchase in result.result_array:
  112. _process_purchase(purchase)
  113. else:
  114. print("Purchase update error")
  115. print("response_code: ", result.response_code, "debug_message: ", result.debug_message)
  116. Processing a purchase item
  117. ~~~~~~~~~~~~~~~~~~~~~~~~~~
  118. The ``query_purchases_response`` and ``on_purchases_updated`` signals provide an array
  119. of purchases in :ref:`Dictionary <class_Dictionary>` format. The purchase Dictionary
  120. includes keys that map to values of the Google Play Billing
  121. `Purchase <https://developer.android.com/reference/com/android/billingclient/api/Purchase>`_ class.
  122. Purchase fields:
  123. ::
  124. order_id: String
  125. purchase_token: String
  126. package_name: String
  127. purchase_state: int
  128. purchase_time: int (milliseconds since the epoch (Jan 1, 1970))
  129. original_json: String
  130. is_acknowledged: bool
  131. is_auto_renewing: bool
  132. quantity: int
  133. signature: String
  134. product_ids: PackedStringArray
  135. Check purchase state
  136. ~~~~~~~~~~~~~~~~~~~~
  137. Check the ``purchase_state`` value of a purchase to determine if a
  138. purchase was completed or is still pending.
  139. PurchaseState values:
  140. ::
  141. # Matches Purchase.PurchaseState in the Play Billing Library
  142. # Access in your script as: BillingClient.PurchaseState.PURCHASED
  143. enum PurchaseState {
  144. UNSPECIFIED,
  145. PURCHASED,
  146. PENDING,
  147. }
  148. If a purchase is in a ``PENDING`` state, you should not award the contents of the
  149. purchase or do any further processing of the purchase until it reaches the
  150. ``PURCHASED`` state. If you have a store interface, you may wish to display
  151. information about pending purchases needing to be completed in the Google Play Store.
  152. For more details on pending purchases, see
  153. `Handling pending transactions <https://developer.android.com/google/play/billing/integrate#pending>`_
  154. in the Google Play Billing Library documentation.
  155. Consumables
  156. ~~~~~~~~~~~
  157. If your in-app item is not a one-time purchase but a consumable item (e.g. coins) which can be purchased
  158. multiple times, you can consume an item by calling ``consume_purchase()`` passing
  159. the ``purchase_token`` value from the purchase dictionary.
  160. Calling ``consume_purchase()`` automatically acknowledges a purchase.
  161. Consuming a product allows the user to purchase it again, it will no longer appear
  162. in subsequent ``query_purchases()`` calls unless it is repurchased.
  163. Example use of ``consume_purchase()``:
  164. ::
  165. func _process_purchase(purchase):
  166. if "my_consumable_iap_item" in purchase.product_ids and purchase.purchase_state == BillingClient.PurchaseState.PURCHASED:
  167. # Add code to store payment so we can reconcile the purchase token
  168. # in the completion callback against the original purchase
  169. BillingClient.consume_purchase(purchase.purchase_token)
  170. func _on_consume_purchase_response(result: Dictionary):
  171. if result.response_code == BillingClient.BillingResponseCode.OK:
  172. print("Consume purchase success")
  173. _handle_purchase_token(result.token, true)
  174. else:
  175. print("Consume purchase failed")
  176. print("response_code: ", result.response_code, "debug_message: ", result.debug_message, "purchase_token: ", result.token)
  177. # Find the product associated with the purchase token and award the
  178. # product if successful
  179. func _handle_purchase_token(purchase_token, purchase_successful):
  180. # check/award logic, remove purchase from tracking list
  181. Acknowledging purchases
  182. ~~~~~~~~~~~~~~~~~~~~~~~
  183. If your in-app item is a one-time purchase, you must acknowledge the purchase by
  184. calling the ``acknowledge_purchase()`` function, passing the ``purchase_token``
  185. value from the purchase dictionary. If you do not acknowledge a purchase within
  186. three days, the user automatically receives a refund, and Google Play revokes the purchase.
  187. If you are calling ``comsume_purchase()`` it automatically acknowledges the purchase and
  188. you do not need to call ``acknowledge_purchase()``.
  189. Example use of ``acknowledge_purchase()``:
  190. ::
  191. func _process_purchase(purchase):
  192. if "my_one_time_iap_item" in purchase.product_ids and \
  193. purchase.purchase_state == BillingClient.PurchaseState.PURCHASED and \
  194. not purchase.is_acknowledged:
  195. # Add code to store payment so we can reconcile the purchase token
  196. # in the completion callback against the original purchase
  197. BillingClient.acknowledge_purchase(purchase.purchase_token)
  198. func _on_acknowledge_purchase_response(result: Dictionary):
  199. if result.response_code == BillingClient.BillingResponseCode.OK:
  200. print("Acknowledge purchase success")
  201. _handle_purchase_token(result.token, true)
  202. else:
  203. print("Acknowledge purchase failed")
  204. print("response_code: ", result.response_code, "debug_message: ", result.debug_message, "purchase_token: ", result.token)
  205. # Find the product associated with the purchase token and award the
  206. # product if successful
  207. func _handle_purchase_token(purchase_token, purchase_successful):
  208. # check/award logic, remove purchase from tracking list
  209. Subscriptions
  210. ~~~~~~~~~~~~~
  211. Subscriptions work mostly like regular in-app items. Use ``BillingClient.ProductType.SUBS`` as the second
  212. argument to ``query_product_details()`` to get subscription details. Pass ``BillingClient.ProductType.SUBS``
  213. to ``query_purchases()`` to get subscription purchase details.
  214. You can check ``is_auto_renewing`` in the a subscription purchase
  215. returned from ``query_purchases()`` to see if a user has cancelled an
  216. auto-renewing subscription.
  217. You need to acknowledge new subscription purchases, but not automatic
  218. subscription renewals.
  219. If you support upgrading or downgrading between different subscription levels,
  220. you should use ``update_subscription()`` to use the subscription update flow to
  221. change an active subscription. Like ``purchase()``, results are returned by the
  222. ``on_purchases_updated`` signal.
  223. These are the parameters of ``update_subscription()``:
  224. 1. old_purchase_token: The purchase token of the currently active subscription
  225. 2. replacement_mode: The replacement mode to apply to the subscription
  226. 3. product_id: The product ID of the new subscription to switch to
  227. 4. base_plan_id: The base plan ID of the target subscription
  228. 5. offer_id: The offer ID under the base plan (optional)
  229. 6. is_offer_personalized: Whether to enable personalized pricing (optional)
  230. The replacement modes values are defined as:
  231. ::
  232. # Access in your script as: BillingClient.ReplacementMode.WITH_TIME_PRORATION
  233. enum ReplacementMode {
  234. # Unknown...
  235. UNKNOWN_REPLACEMENT_MODE = 0,
  236. # The new plan takes effect immediately, and the remaining time will be prorated and credited to the user.
  237. # Note: This is the default behavior.
  238. WITH_TIME_PRORATION = 1,
  239. # The new plan takes effect immediately, and the billing cycle remains the same.
  240. CHARGE_PRORATED_PRICE = 2,
  241. # The new plan takes effect immediately, and the new price will be charged on next recurrence time.
  242. WITHOUT_PRORATION = 3,
  243. # Replacement takes effect immediately, and the user is charged full price of new plan and
  244. # is given a full billing cycle of subscription, plus remaining prorated time from the old plan.
  245. CHARGE_FULL_PRICE = 5,
  246. # The new purchase takes effect immediately, the new plan will take effect when the old item expires.
  247. DEFERRED = 6,
  248. }
  249. Default behavior is ``WITH_TIME_PRORATION``.
  250. Example use of ``update_subscription``:
  251. ::
  252. BillingClient.update_subscription(_active_subscription_purchase.purchase_token, \
  253. BillingClient.ReplacementMode.WITH_TIME_PRORATION, "new_sub_product_id", "base_plan_id")