Skip to content

Commit 78c160d

Browse files
Add Quick Start page
1 parent 84aadb0 commit 78c160d

File tree

3 files changed

+309
-1
lines changed

3 files changed

+309
-1
lines changed

docs/Installation.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ The plugin is now active in your project.
3232
1. In main menu, go to `Project > Export`.
3333
2. Add an Android export preset if you dont have one.
3434
3. Ensure:
35+
- `gradle/use_gradle_build` is checked.
3536
- Package name matches your Google Play Console app
3637
- Internet permission is enabled
3738
- The project is signed with a release keystore

docs/quickstart.md

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

zensical.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ site_author = "syntaxerror247"
55

66
nav = [
77
"index.md",
8-
"Installation.md"
8+
"Installation.md",
9+
"quickstart.md"
910
]
1011

1112
copyright = """

0 commit comments

Comments
 (0)