Library
godot-iap
Library version
godot-iap 3.0.1 (also present on main at 77f91d963c17629b9b0a3c167cf47497ae3a621b)
Affected platform
Android and iOS
Store
Google Play Store and App Store
Environment
Godot 4.7.x, GDScript addon API
What happened?
get_available_purchases() collapses store and bridge failures into an empty array, so callers cannot distinguish "the user owns nothing" from "the entitlement lookup failed."
On Android, the native bridge returns [] when it is not initialized, when option parsing fails, or when store.getAvailablePurchases() throws:
The GDScript wrapper also converts Android parse failures and iOS { success: false, error: ... } payloads to []:
This makes the Android restore wrapper report success even when the lookup failed, because it ignores the lookup outcome and unconditionally sets result.success = true:
For non-consumable entitlements this is unsafe: an app may treat a transient store failure as an authoritative empty entitlement set and revoke access, while the restore UI tells the user that restoration succeeded.
Expected behavior
The Godot API should preserve an explicit success/error result for available-purchase queries on both platforms. restore_purchases() should return success = false (and an error/code) when its underlying query fails.
The existing array-returning method can remain for compatibility, but a result-bearing API should be available and used by restore. For example:
{
"success": false,
"purchases": [],
"error": "Not initialized",
"code": "not-prepared"
}
Reproduction steps
- Use godot-iap 3.0.1 in a Godot Android project.
- Instantiate the addon without a successful
init_connection() (or make the billing service unavailable).
- Call
await get_available_purchases().
- Observe
[], which is indistinguishable from a successful query with no purchases.
- Call
await restore_purchases().
- Observe a
VoidResult whose success field is true, even though no successful store query occurred.
The same loss of error information occurs on iOS when _call_ios_async("getAvailablePurchases", ...) returns a failure payload.
Suggested regression coverage
- Android not-initialized and thrown-exception paths return a structured failure.
- iOS async failure payload survives the GDScript wrapper.
- Android restore returns failure when its available-purchases query fails.
- A successful empty purchase list remains distinguishable from an error.
Logs or stack trace
No exception reaches the caller. The observed values are:
get_available_purchases() -> []
restore_purchases().success -> true
Additional context
The restore documentation describes a result that should report whether restoration succeeded: https://www.openiap.dev/docs/apis/restore-purchases
Library
godot-iap
Library version
godot-iap 3.0.1 (also present on
mainat77f91d963c17629b9b0a3c167cf47497ae3a621b)Affected platform
Android and iOS
Store
Google Play Store and App Store
Environment
Godot 4.7.x, GDScript addon API
What happened?
get_available_purchases()collapses store and bridge failures into an empty array, so callers cannot distinguish "the user owns nothing" from "the entitlement lookup failed."On Android, the native bridge returns
[]when it is not initialized, when option parsing fails, or whenstore.getAvailablePurchases()throws:The GDScript wrapper also converts Android parse failures and iOS
{ success: false, error: ... }payloads to[]:This makes the Android restore wrapper report success even when the lookup failed, because it ignores the lookup outcome and unconditionally sets
result.success = true:For non-consumable entitlements this is unsafe: an app may treat a transient store failure as an authoritative empty entitlement set and revoke access, while the restore UI tells the user that restoration succeeded.
Expected behavior
The Godot API should preserve an explicit success/error result for available-purchase queries on both platforms.
restore_purchases()should returnsuccess = false(and an error/code) when its underlying query fails.The existing array-returning method can remain for compatibility, but a result-bearing API should be available and used by restore. For example:
{ "success": false, "purchases": [], "error": "Not initialized", "code": "not-prepared" }Reproduction steps
init_connection()(or make the billing service unavailable).await get_available_purchases().[], which is indistinguishable from a successful query with no purchases.await restore_purchases().VoidResultwhosesuccessfield istrue, even though no successful store query occurred.The same loss of error information occurs on iOS when
_call_ios_async("getAvailablePurchases", ...)returns a failure payload.Suggested regression coverage
Logs or stack trace
Additional context
The restore documentation describes a result that should report whether restoration succeeded: https://www.openiap.dev/docs/apis/restore-purchases