I have a really strange problem with Google In-app billing API.
I want to get the details of my available subscriptions to show their localised price on the UI. I'm using the IabHelper to query the details of the SKUs I specify, but the result contains no products, but after I purchase the subscription it's working, even if I cancel the subscription then and reinstall the app.
Here is my code:
List<String> additionalSkus = new ArrayList<>();
for (Subscription subscription : subscriptions) {
additionalSkus.add(subscription.getSku());
}
iabHelper.queryInventoryAsync(true, additionalSkus, new IabHelper.QueryInventoryFinishedListener() {
#Override
public void onQueryInventoryFinished(IabResult result, Inventory inv) {
if (iabHelper == null || result.isFailure()) {
Log.i("subs", result.getMessage());
}
for (Subscription subscription : subscriptions) {
SkuDetails skuDetails = inv.getSkuDetails(subscription.getSku());
if (skuDetails != null) {
subscription.setLocalizedPrice(skuDetails.getPrice());
Log.i("subs", subscription.getLocalizedPrice());
}
}
}
});
I have everything set up right about the In-app billing process. My products are active, I have a published beta APK, I can even get the details of my managed products, and purchase managed products and subscriptions, but this isn't working.
If you're using the latest version of IabHelper class, you should note that the function arguments relevant to additional skus to query are split to moreItemSkus (PRODUCT skus) and moreSubsSkus (SUBSCRIPTIONS skus).
public void queryInventoryAsync(final boolean querySkuDetails, final List<String> moreItemSkus,
final List<String> moreSubsSkus, final QueryInventoryFinishedListener listener)
In your case, your call should be
iabHelper.queryInventoryAsync(true, null, additionalSkus, new IabHelper.QueryInventoryFinishedListener() ...
Related
QueryInventoryFinishedListener of IabHelper has not returned the expired subscription items.
On the other hand, PurchaseHistoryResponseListener of Google Play Billing Library seems to receive all purchased items, which is including expired items.
On Google Play Billing Library, we have to check the purchased date of PurchaseHistoryResponseListener and each expiration date of items?
queryPurchases vs queryPurchaseHistoryAsync
Generally, we should use queryPurchases(String skuType), which does not returns expired items. queryPurchaseHistoryAsync returns enabled and disabled items, as you see the documentation like following.
queryPurchases
Get purchases details for all the items bought within your app. This method uses a cache of Google Play Store app without initiating a network request.
queryPurchaseHistoryAsync
Returns the most recent purchase made by the user for each SKU, even if that purchase is expired, canceled, or consumed.
About queryPurchaseHistoryAsync
I could not image the use case for queryPurchaseHistoryAsync. If we need to use queryPurchaseHistoryAsync, we need the implementation to check if it is expired or not.
private PurchaseHistoryResponseListener listener = new PurchaseHistoryResponseListener() {
#Override
public void onPurchaseHistoryResponse(int responseCode, List<Purchase> purchasesList) {
for (Purchase purchase : purchasesList) {
if (purchase.getSku().equals("sku_id")) {
long purchaseTime = purchase.getPurchaseTime();
// boolean expired = purchaseTime + period < now
}
}
}
};
Purchase object does not have the information of period, so the above period must be acquired from BillingClient.querySkuDetailsAsync or be hard-coded. The following is sample implementation to use querySkuDetailsAsync.
List<String> skuList = new ArrayList<>();
skuList.add("sku_id");
SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
params.setSkusList(skuList).setType(BillingClient.SkuType.SUBS);
billingClient.querySkuDetailsAsync(params.build(), new SkuDetailsResponseListener() {
#Override
public void onSkuDetailsResponse(int responseCode, List<SkuDetails> skuDetailsList) {
if (skuDetailsList == null) {
return;
}
for (SkuDetails skuDetail : skuDetailsList) {
if (skuDetail.getSku().equals("sku_id")) {
String period = skuDetail.getSubscriptionPeriod();
}
}
}
});
I need to retrieve the price of my InApp Purchase and display it as a Title in my Card Layout.
This is how the card looks like, instead of "sku price", I need to have the actual price.
I have only this IAP so I shouldn't need an array or a list.
For implementing in App Billing I followed Android Developers - In App Billing Guide
So far I tried to write this inside my onQueryInventoryFinished() method, without any luck.
IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
#Override
public void onQueryInventoryFinished(IabResult result, Inventory inv) {
//have we been disposed of in the meantime? if so, quit
if (mHelper == null) return;
//is it a failure?
if(result.isFailure()) {
alert("Failed to query Inventory: " + result);
return;
}
String skuPrice = inv.getSkuDetails(SKU_PREMIUM).getPrice();
((TextView)findViewById(R.id.card_buy_title)).setText(skuPrice);
//do we have the premium upgrade?
Purchase premiumPurchase = inv.getPurchase(SKU_PREMIUM);
isPremium = (premiumPurchase != null && verifyDeveloperPayload(premiumPurchase));
}
};
As far as I was able to understand this thing I did should get the price of already purchased items. So maybe that is why it does not work.
Can anyone of you point me in the right direction?
In your mHelper setup you have to query all inventory by calling
mHelper.queryInventoryAsync(true, allSkus, null, mGotInventoryListener);
where allSkus is a list of all skues.
Check IabHelper.
I'm making an Android application that will include a subscription using in-app billing from Google (https://developer.android.com/google/play/billing/index.html).
The aim is to let user to have 1 subscription per device.
I know that Google limit subscription for 1 google mail account but not for 1 device, that's why I made my own restriction using a server with a database.
So I made a pool of 10 subscriptions in the developer console product list and I want that when a device subscribe for the 1st subscription, a second device (using the same google account) will subscribe for the next subscription...
But when I want the second device chose automatically the next subscription not even bought on the account, it is saying to me "Product already own". The problem is that the current inventory is not refreshed.
I'm using IabHelper and here is the part of code where I'm trying to buy the next subscription available.
public void initIab() throws IabHelper.IabAsyncInProgressException {
iabHelper = new IabHelper(this, AppConfig.APPLICATION_KEY);
iabHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) throws IabHelper.IabAsyncInProgressException {
if (result.isSuccess()) {
iabHelper.queryInventoryAsync(iabInventoryListener());
billingServiceReady = true;
}
}
});
}
private IabHelper.QueryInventoryFinishedListener iabInventoryListener() {
return new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
if (iabHelper == null) {
return;
}
if (!result.isSuccess()) {
return;
}
String[] PREMIUM_KEYS = {"premium1","premium2","premium3","premium4","premium5","premium6","premium7","premium8","premium9","premium10"};
Purchase premiumPurchase = null;
String premiumKey;
boolean payload = false;
for(String key : PREMIUM_KEYS) {
if (inventory.hasPurchase(key)) {
premiumPurchase = inventory.getPurchase(key);
premiumKey = key;
payload = verifyDeveloperPayload(hasPurchase,premiumKey);
if(payload)
break;
}
}
session.setPremium(achatPremium != null && payload);
}
};
}
Please could you help me to find a solution ?
Sorry for my bad english.
Thank you.
I am using Google's In-App Billing for my Android app.
I used the IabHelper class from Google's how to, as their billing seems extremely complicated.
My issue is I want to know if the purchase is successful or not. I think I'm following the process correctly, but in my logs I see a lot of users that get the upgrade, but whose purchase never shows up in my Google Play payments account. (i.e. they get the upgrade for free).
I'm logging the GP order ids, sometimes its a number like,
GPA.1234-5678-9123-1234
But sometimes its like,
1234567891234.1234567891234
Normally I think its the non GPA orders that don't get charged.
Also I think you can put an order through, then cancel it, and still get the upgrade?
How do you ensure the user really paid?
Code:
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result, final Purchase purchase) {
if (result.isFailure()) {
showMessage("Google Billing Purchase Error");
return;
} else if (purchase.getSku().equals(sku)) {
IabHelper.QueryInventoryFinishedListener mReceivedInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
if (result.isFailure()) {
showMessage("Google Billing Error");
return;
} else {
if (inventory.hasPurchase(sku)) {
showMessage("Thank you for upgrading");
grantUpgrade();
// ** This line gets call, but no payment occurs.
}
}
}
};
mHelper.queryInventoryAsync(mReceivedInventoryListener);
}
}
};
mHelper.launchPurchaseFlow(this, sku, 10001, mPurchaseFinishedListener, "");
*** updated to check "inventory.hasPurchase(sku)" but still see users who get the upgrade but don't pay.
** maybe the users are using Freedom hack? Anyway to prevent this?
if (result.isFailure()) {
//If the user aborts or any other problems it will jump here
}
else {
//The user purchased some item, check out which it is
mIsPremium = inventory.hasPurchase(SKU_ANY_ITEM);
}
So concerning your question, this code already verify whether the user really purchased the item !
Purchase premiumPurchase = inventory.getPurchase(SKU);
boolean mIsPremium = (premiumPurchase != null
&& verifyDeveloperPayload(premiumPurchase));
if(mIsPremium){
...
}
The Google Play Store keeps track of purchases for you, so you shouldn't assume that just because a purchase was successful, the item will stay purchased. It's possible for a user to get a refund for a purchase. For this reason, you need to query the user's inventory every time you launch and adjust your grants appropriately. You would need to do this check anyways in order to support users that expect to have the grant when they switch to a new device or uninstall/reinstall the app.
)
I'm trying to implement in-app-billing within my app. In the Google Play Developer Console I declared a managed item. Buying this item works really fine. But now, when I refund or cancel the purchase in the google wallet merchant center, my app takes very long (more days) to recognize that the item is not longer owned.
I've already read lots of other articles about this problem and think one logical explanation is that the purchase is saved in the cache of the Google Play Store. Although I know that this question has asked often before, I ask here again:
Is it possible to clear the cache or does anyone know how to tell my app, when the purchase is not longer owned?
I'm thankful for any hint, that helps me to solve this problem :D
In addition, my code where I ask, if the item is purchased. I'm using in-app-billing v3.
public boolean hasUserBoughtItem() {
try {
Bundle ownedItems = mService.getPurchases(mUsedAPI, mContext.getPackageName(),
mPurchaseType, null);
int response = ownedItems.getInt("RESPONSE_CODE");
if(response == 0) {
ArrayList<String> ownedSkus = ownedItems.getStringArrayList("INAPP_PURCHASE_ITEM_LIST");
if(!ownedSkus.isEmpty()) {
for(String sku : ownedSkus) {
if(sku.equals(Constants.ITEM_ID_ALL_RECIPES)) {
return true;
}
}
}
return false;
}
} catch(Exception e) {
e.printStackTrace();
}
return false;
}
The IAP purchase inventory isn't cached by the Play Store at all and should be queried regularly in your activities. It should only take approximately 15-30mins for order cancellations to propagate.
Are you using the IABHelper as per the sample app to connect to Google Play?
IabHelper.QueryInventoryFinishedListener mGotInventoryListener
= new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result,
Inventory inventory) {
if (result.isFailure()) {
// handle error here
}
else {
// does the user have the premium upgrade?
mIsPremium = inventory.hasPurchase(SKU_PREMIUM);
// update UI accordingly
}
}
};