Skip to content

Google Play: Improve upgrade check reliability - #747

Merged
d4rken merged 1 commit into
mainfrom
gplay_iap_check
Oct 21, 2023
Merged

Google Play: Improve upgrade check reliability#747
d4rken merged 1 commit into
mainfrom
gplay_iap_check

Conversation

@d4rken

@d4rken d4rken commented Oct 21, 2023

Copy link
Copy Markdown
Member

Previously we used orderId to check for valid purchases but apparently this is not a reliable way to check state.

While documentation says:

The order ID will be null if the purchase is in the Purchase.PurchaseState.PENDING state and populated if the purchase has transitioned to the Purchase.PurchaseState.PURCHASED state.

in reality I could reproduce cases where the orderId field was null despite getPurchaseState() == PURCHASED. What up Google?

My best guess so far is that the cached data that queryPurchasesAsync sometimes returns, does not always contain all fields. Clearing Google Play cache can reset it and fix that, but it's unclear why and when the "half-cached" state returns.

A normal, fully cached purchase entry looks like this:

{
    "orderId": "GPA.<id>",
    "packageName": "eu.darken.sdmse",
    "productId": "eu.darken.sdmse.iap.upgrade.pro",
    "purchaseTime": 1697351226769,
    "purchaseState": 0,
    "purchaseToken": "<token>",
    "quantity": 1,
    "acknowledged": true
}

A half-cached entry looks like this:

{
    "packageName": "eu.darken.sdmse",
    "productId": "eu.darken.sdmse.iap.upgrade.pro",
    "purchaseTime": 1680023656958,
    "purchaseState": 0,
    "purchaseToken": "<token>"
}

While the json payload contains purchaseState == UNSPECIFIED, the actual Purchase.getPurchaseState() function still returns PURCHASED in the half-cached state. Due to fallback handling:

    public int getPurchaseState() {
        switch (this.zzc.optInt("purchaseState", 1)) {
            case 4:
                return 2;
            default:
                return 1;
        }
    }

So this PR changes the check to getPurchaseState.

Previously we used `orderId` to check for valid purchases but apparently this is not a reliable way to check state.

While [documentation](https://developer.android.com/reference/com/android/billingclient/api/Purchase#getOrderId()) says:

> The order ID will be null if the purchase is in the Purchase.PurchaseState.PENDING state and populated if the purchase has transitioned to the Purchase.PurchaseState.PURCHASED state.

in reality I could reproduce cases where the `orderId` field was `null` despite `getPurchaseState() == PURCHASED`. What up Google?

My best guess so far is that the cached data that `queryPurchasesAsync` sometimes returns, does not always contain all fields.
Clearing Google Play cache can reset it and fix that, but it's unclear why and when the "half-cached" state returns.

A normal, fully cached purchase entry looks like this:

```json
{
    "orderId": "GPA.<id>",
    "packageName": "eu.darken.sdmse",
    "productId": "eu.darken.sdmse.iap.upgrade.pro",
    "purchaseTime": 1697351226769,
    "purchaseState": 0,
    "purchaseToken": "<token>",
    "quantity": 1,
    "acknowledged": true
}
```

A half-cached entry looks like this:

```json
{
    "packageName": "eu.darken.sdmse",
    "productId": "eu.darken.sdmse.iap.upgrade.pro",
    "purchaseTime": 1680023656958,
    "purchaseState": 0,
    "purchaseToken": "<token>"
}
```

While the json payload contains `purchaseState == UNSPECIFIED`, the actual `Purchase.getPurchaseState()` function still returns `PURCHASED` in the half-cached state. Due to fallback handling:

```java
    public int getPurchaseState() {
        switch (this.zzc.optInt("purchaseState", 1)) {
            case 4:
                return 2;
            default:
                return 1;
        }
    }
```

So this PR changes the check to `getPurchaseState`.
@d4rken d4rken added bug Something isn't working as expected Google Play labels Oct 21, 2023
@d4rken
d4rken merged commit 3d490f3 into main Oct 21, 2023
@d4rken
d4rken deleted the gplay_iap_check branch October 21, 2023 16:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working as expected Google Play

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant