Google play billing API: How to understand the user is subscribed?
The implementation of the billing process looks good, but missing a check to determine whether the subscription is really active at the current moment.
Observing can be done by using LiveData objects. So that we do not need the SharedPreferences or so to hold the state. I'll cover this at the observing part below. A detailed answer:
Purchases list
Let's first explain what the purchases list here exactly means in the billing API:
- This is the list of all the purchases the user has for an in-app item or subscription.
- These purchases have to be acknowledged by either the app or the backend (recommended via the backend, but both are possible)
- This purchases list includes payments which are still pending and also the payments that are not acknowledged yet.
Seeing the acknowledge step being implemented, I assume the acknowledgement of the payment is done successfully.
Point 3 is why it doesn't detect the actual subscribed state, as the state of the purchases aren't checked.
Checking the subscribed state
The queryPurchases()
call returns the payments of the user for the requested products. The array that we receive back can have multiple items (mostly one per in-app item or subscription). We need to check them all.
Each purchase has some more data. Here are the methods that we need for checking the state:
getSku()
// To verify the product is what we wantgetPurchaseState()
// To get the actual purchase statusisAcknowledged()
// To know if the payment is acknowledged, if not, it means that the payment is not successful yet
In order to check whether a purchase is currently paid and active for the PREMIUM sku:
boolean isPremiumActive = Constants.PREMIUM_SKU.equals(purchase.getSku()) && purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED && purchase.isAcknowledged()
If we want to check if any of the subscriptions is active, we check for the other sku's the same (looping through the sku's and purchases)
* Note that now if isPremiumActive
is true, it means that the user currently has an active subscription. This means that if the user canceled his subscription but still has paid till the ending period, this value will still be true. Simply because the user has still the right to access the content until the expiration of the billing period.
* In case the subscription period is really over (cancelled or expired), the billing client will not return the purchase anymore.
Observing the current status
Now that we know how to verify the purchases, we can read this state easily by using LiveData so that we can access it anytime. In the example we already have te LiveData purchases
, this one contains all the purchases and is filled after the queryPurchases()
call.
- Creating the LiveData
Let's create a new LiveData which uses this purchases
LiveData, but instead will return true or false based on whether we have the PREMIUM_SKU active:
public LiveData<Boolean> isSubscriptionActive = Transformations.map(purchases, purchases -> {
boolean hasSubscription = false;
for (Purchase purchase : purchases) {
// TODO: Also check for the other SKU's if needed
if (Constants.PREMIUM_SKU.equals(purchase.getSku()) && purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED && purchase.isAcknowledged()) {
// This purchase is purchased and acknowledged, it is currently active!
hasSubscription = true;
}
}
return hasSubscription;
});
Add this block in the BillingClientLifecycle, it will emit the value true or false if the purchases list changes
- Observing it
As usual, observe this LiveData in the Activity in which you want to receive the update:
billingClientLifecycle.isSubscriptionActive.observe(this, hasSubscription -> {
if (hasSubscription) {
// User is subscribed!
Toast.makeText(this, "User has subscription!", Toast.LENGTH_SHORT).show();
} else {
// User is a regular user!
}
});
Put this in the MainActivity
in your case. It will observe for the subscription changes and trigger in one of the two functions when it changes.
* If the livedata is not wanted but rather a direct way of retrieving the value, you can also just use a boolean field inside the billingClientLifecycle
and update this correctly at the processPurchases()
method with the same check as above.
Advanced
For a more advanced usage, we can also use the other states of the purchase object:
In case the purchase has a state of Purchase.PurchaseState.PENDING
, it means that the Google or the User still have some steps to do to verify the payment. Basically this means that the billing API is not sure whether the payment was fulfilled if this happens. We could inform the user about this state for example too by showing a message to fulfil his payment or so.
If a purchase is paid but not acknowledged yet, it means the acknowledge step in the BillingClientLifecycle
was not successful. Additionally, if this is the case, Google Play will automatically refund the payment to the user. For example: for monthly subscriptions the acknowledgement period is 3 days, so after 3 days the user gets the money back and the purchase is removed.