I ship an Android app with subscriptions using the legacy (pre-offer) model — not the new base-plan/offer system. The app has two mutually exclusive plans (e.g., “Plan A” and “Plan B”): the user must never hold both at the same time.
There’s no user login in the app. On first launch we generate a random UUID locally as the “user ID”. If the app is reinstalled, a new UUID is generated, so we can’t reliably correlate to any past local state. In practice the only trustworthy source is the Google Play Billing Library on the client.
Current client logic (simplified):
// 1) Check if user currently has an active sub
val active = billingClient.queryPurchasesAsync(QueryPurchasesParams.newBuilder()
.setProductType(BillingClient.ProductType.SUBS)
// 2) If active -> set old token for upgrade/downgrade
if (active.hasPurchase) {
val oldToken = active.purchasesList.first().purchaseToken
launchBillingFlow(
BillingFlowParams.newBuilder()
.setSubscriptionUpdateParams(
SubscriptionUpdateParams.newBuilder()
.setOldPurchaseToken(oldToken)
.build()
)
.build()
)
} else {
// 3) If not active -> treat as new purchase (no old token)
launchBillingFlow(BillingFlowParams.newBuilder().build())
}
Problem:
When the user’s previous subscription is paused, queryPurchasesAsync() doesn’t report it as active. The app therefore treats this as “no current sub” and allows a new purchase. Later, when the paused sub auto-resumes, the user ends up with two concurrent subscriptions (violating our “only one plan at a time” rule).
I’m aware the Play Console can disable “pause” globally, but please do not suggest that — I’m trying to understand the technical options when pause is enabled.
What I’m trying to figure out (legacy, no offers):
Client-side detection: Is there any way, using only the Play Billing Library on device, to detect that the current Play account has a paused subscription (for our SKU) before starting a new purchase flow?
Getting the old token: Is there any supported way to obtain the previous (paused) subscription’s
purchaseTokenon device so that I can pass it toSubscriptionUpdateParams#setOldPurchaseToken(...)and ensure this becomes a change/upgrade rather than a second, parallel sub?Restore paths: Does any combination of
queryPurchasesAsync()and/or purchase “restore” APIs return enough information for paused subs? (My understanding is no, but I haven’t been able to prove this conclusively.)No backend identity: Because there is no login and the local UUID changes on reinstall, I cannot reliably look up a past token on a server. Even if I added login, that ID might not match the Google account used for Play purchases, so it wouldn’t be authoritative by itself.
Constraints & scope:
- Legacy subscriptions (pre-offer).
- Two plans must be mutually exclusive — never both at the same time.
- Please don’t propose “disable pause in Console”; I’m specifically asking about what’s possible on the client (or with Play APIs that don’t require already knowing a token).
Question (succinctly):
In the legacy (non-offer) model, how can an app prevent a second subscription from being created while a previous one is paused, given there’s no login and we only have the client-side Billing APIs? Is there any officially supported way to (a) detect a paused subscription or (b) recover the old purchaseToken needed for setOldPurchaseToken(...) under these constraints?
Any authoritative guidance or official docs clarifying the expected behavior/limitations would be greatly appreciated.