Android inapp billing responseList is empty - android

I have defined some in app products in my app. I've uploaded the apk to the Google Play and added the inapp purchase products on the Google play.
I've got my ServiceConnection defined as followed:
ServiceConnection mServiceConn = new ServiceConnection() {
#Override
public void onServiceDisconnected(ComponentName name) {
mService = null;
}
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
mService = IInAppBillingService.Stub.asInterface(service);
connect();
}
};
The onServiceConnected function is called, the bindService returns true.
Next is the connect function.
public void connect() {
new Thread(new Runnable() {
public void run() {
try {
// Purchase type is "inapp", as required by API v3
Bundle skuDetails = mService.getSkuDetails(3, PACKET, "inapp", querySkus);
}
int response = skuDetails.getInt("RESPONSE_CODE");
Log.e("IAP connect", response + "");
if (response == 0) {
ArrayList<String> responseList = skuDetails.getStringArrayList("DETAILS_LIST");
Log.e("size list", responseList.size()+"");
...
}
}
} catch (RemoteException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
}
}).start();
}
PACKET here is set to the getPackageName().
The response code is 0 but the Log prints that the size of the list is 0. I have no idea why the list is empty, as I have entered 5 items in total to the Google Play and each of them are active. I have waited 2 days now and tested with three devices, but still no items get through.
I pretty much tried everything I can think of so any suggestions are welcome.

You need to publish your application to Beta/Alpha to access inapp billing functionality.
It has recenty changed, but they did not announce it:)
http://developer.android.com/google/play/billing/billing_testing.html#draft_apps
It is worth to mention that you don't have to upload every new build to be able to test it. Just use the same versionCode and versionName, and it will work if the app is published.

Your Code looks ok.
Make sure you query the right SKUs. The single items in querySkus must match exactly the id of the in app product.
Second make sure to query the correct type if items. If you configured "in app products" as a car to buy in the developer console, use "inapp" as you did. If you have subscriptions, use "subs" as type of your query against Google Play.
Hope this helps.

I had the problem on an old device. Deleting Play Services app data fixed the problem.
So we simply need to get ready for the bad reviews...

I had the same problem, the fix was setting my subscription status to "active".
After a few minutes it finally worked.

Related

Android Google Billing Client - onSkuDetailsResponse Always Empty List

I was wondering could you help. I followed the instructions at https://developer.android.com/google/play/billing/integrate, but I cannot seem to get the purchase flow working. The billing seems to setup ok, but when I try to query for my in-app products, the list is always returning empty. Can someone please help?
In my app level build.gradle file, I have included the Google Billing SDK:
implementation 'com.android.billingclient:billing:3.0.0'
Then I have created an activity to test out the code. It first initialises the BillingClient and starts the connection. The connection seems to finish the setup correctly. Once setup correctly, I then try to query the products that I have available in my Google Play Console under 'Store presence' > 'In-app products' > 'Manage products'
The following is then the code in the Activity that should kick off the process and return the SkuDetails list, but unfortunately it is returning back empty.
private BillingClient billingClient;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_billing);
this.billingClient = BillingClient.newBuilder(this)
.enablePendingPurchases()
.setListener(this.purchaseUpdateListener)
.build();
this.billingClient.startConnection(billingClientStateListener);
}
private PurchasesUpdatedListener purchaseUpdateListener = new PurchasesUpdatedListener() {
#Override
public void onPurchasesUpdated(#NonNull BillingResult billingResult, #Nullable List<Purchase> list) {
Log.d("Billing", "onPurchasesUpdated - List Size: " + list.size());
}
};
private BillingClientStateListener billingClientStateListener = new BillingClientStateListener() {
#Override
public void onBillingSetupFinished(#NonNull BillingResult billingResult) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
Log.d("Billing", "onBillingSetupFinished - OK");
queryProducts();
} else {
Log.d("Billing", "onBillingSetupFinished - Something wrong response Code: " + billingResult.getResponseCode());
}
}
#Override
public void onBillingServiceDisconnected() {
Log.d("Billing", "Service disconnected");
}
};
private void queryProducts() {
List<String> productIdsList = new ArrayList<>();
productIdsList.add("test.billing.001");
SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
params.setSkusList(productIdsList).setType(BillingClient.SkuType.INAPP);
this.billingClient.querySkuDetailsAsync(params.build(), new SkuDetailsResponseListener() {
#Override
public void onSkuDetailsResponse(#NonNull BillingResult billingResult, #Nullable List< SkuDetails > list) {
Log.d("Billing", "onSkuDetailsResponse - List Size: " + list.size());
}
});
}
So for anyone who is having similar issues, it seems that (well in my case anyways) that my app needed to be successfully published before I could retrieve the in-app products from the app. Once my app was published, I was then able to query and use the in-app products.
According to Maxim Alov comment for Billing 5.0.0, the problem is in invitations for testers.
My steps to fix this problem:
Open tester's invitation link and accept
Open app via link from previous step
After two these steps (in my case second helped) all products started to come
I'm using billing-lib-5.0.0 and also had the same issue -
queryProductDetails() was always empty on my release builds, let alone
debug builds. I'd actually added all my test gmail emails to list of
testers for Closed Testing, and also LICENSED all of them. No effect.
Eventually, I recognized that the link to my test app is not generated
on Play Console Closed Testing track page. I recreated the track, this
time for Internal Testing, and the link has appeared. Then I logged in
to each of my gmails and accepted to be a tester from that link. After
doing that, products started to come, in IDE

In App Purchase : Two devices with same account not getting same purchased items

What I am trying?
Purchasing items in one android device having configured X email address. When I check in another device having the same email address configured. but items purchased on first device is not getting available in another device.
What I have tried?
I have tried using :
inappBillingService.getPurchases(InAppBuyActivity.INAPPVERSION,
getPackageName(), "inapp", null);
and also tried using :
IabHelper.QueryInventoryFinishedListener mGotInventoryListener
= new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result,
Inventory inventory) {
if (result.isFailure()) {
// handle error here
}
else {
// Not getting same purchased on both devices
}
}
};
I am generating signed apk having the same version name and version code as of playstore version.
I have tested the app using two devices. It takes sometime to reflect the purchases.
If i delete the app and reinstall it as a fresh then new purchased items are there. But, if i purchase one item and check it on second device at same time its not reflected there.
App shows items already purchased in second device when user try to purchase it. But, its not available in inappBillingService.getPurchases and IabHelper.QueryInventory.
The In-app Billing Version 3 API makes it easier for you to integrate In-app Billing into your applications.The features in this version include improved synchronous purchase flow, APIs to let you easily track ownership of consumable goods, and local caching of in-app purchase data.
1. Check in your code, if you used consumable or not
if you used consumable, user can purchase product multiple time, google store only one time purchase detail and again getting blank response, so remove consumable related code from your app.
2. Check user already purchased item or not using below code
private IInAppBillingService mService = null;
//onCreare
try {
Intent serviceIntent = new Intent("com.android.vending.billing.InAppBillingService.BIND");
serviceIntent.setPackage("com.android.vending");
bindService(serviceIntent, mServiceConn, Context.BIND_AUTO_CREATE);
// bindService(new Intent("com.android.vending.billing.InAppBillingService.BIND"), mServiceConn, Context.BIND_AUTO_CREATE);
} catch (Exception e) {
e.printStackTrace();
}
// Method
ServiceConnection mServiceConn = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
mService = IInAppBillingService.Stub.asInterface(service);
Log.d("TEST", "mService ready to go!");
checkownedItems();
}
#Override
public void onServiceDisconnected(ComponentName name) {
mService = null;
}
};
private void checkownedItems() {
try {
Bundle ownedItems = mService.getPurchases(3, getPackageName(), "inapp", null);
if (ownedItems.getInt("RESPONSE_CODE") == 0) {
ArrayList<String> ownedSkus = ownedItems.getStringArrayList("INAPP_PURCHASE_ITEM_LIST");
ArrayList<String> purchaseDataList = ownedItems.getStringArrayList("INAPP_PURCHASE_DATA_LIST");
if (purchaseDataList.size() > 0) {
// Item already Purchased....
// Manage your in-app view
}else{
// Item not purchased
}
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
Used above logic before purchase item, if item not purchase then call in-app getPurchases() otherwise hide or manage in-app view.

Android: In App purchase Query does not return any products(skuDetails)

I am working on my first android app. I really learned a lot on stack overflow. But I with my current problem I did not find a solution yet.
I tried to implement "in app purchase". I did the following things:
1) In the developer console I uploaded a signed apk in alpha and beta test. I also added a test user to the account and for testing I am using that account. Additionally I added some "In app products(managed)" with the status active.
2) Like described on http://developer.android.com/training/in-app-billing/index.html I downloaded the necessary lib field, copied code from the example project TrivialDrive and followed all the steps which result to the coding:
// called when the app is started
public void setupConnectionToGooglePlay(final Context context) {
this.context = context;
mHelper = new IabHelper(context, base64EncodedPublicKey);
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
if (!result.isSuccess()) {
// Oh no, there was a problem. --> no error message shown to
// the user!
Toast.makeText(context, "No connection to google:" + result, Toast.LENGTH_LONG).show();
} else {
Toast.makeText(context, "connection to google:" + result, Toast.LENGTH_LONG).show();
// If connection is established:
get List of all in App Purchase products
getListOfInAppPurchaseProducts();
}
}
});
}
When executing this part of code I get the result, that the connection was established successfully. Hence the method getListOfInAppPurchaseProducts() is called.
private void getListOfInAppPurchaseProducts() {
IabHelper.QueryInventoryFinishedListener mQueryFinishedListener = new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
if (result.isFailure()) {
// no error message is shown to the user
Toast.makeText(context, "Query not successful", Toast.LENGTH_LONG).show();
} else {
String message = result.getMessage();
boolean isSuccess = result.isSuccess();
// no success message is shown to the user
List<String> skus = inventory.getAllOwnedSkus();
Map<String,SkuDetails> map = inventory.getSKUMap();
int size = map.size();
Toast.makeText(
context,
"Message: " + message + "Query successful. Mapsize:" + size, Toast.LENGTH_LONG).show();
WMGooglePlayConnection.this.inventory = inventory;
}
}
};
ArrayList<String> list = new ArrayList<String>();
list.add("test");
list.add("test2");
mHelper.queryInventoryAsync(true, list, mQueryFinishedListener);
}
Unfortunately the returned inventory does not contain any SKU Details (even the query is successful). I checked the following:
productIds
status of the product ids (active)
base64EncodedPublicKey
For testing I used a real device (Samsung ACE 2). I copied the apk file directly to the device (not downloaded from google play). Can this be a problem?
Is it somehow possible to get the SKU Details also with the emulator?
Do you have any idea what I can check?
It would be really nice if someone could help me...
I just ran into this as well. Our in app billing stuff broke right around when Google Play Service 4.4 went out. A week or two back I think.
To get your in app billing products to show up now you'll have to publish your Alpha builds but have them only visible to people on your tester list/group.
After one publishes the app then testers can download it from the Google Play Store with a link similar to this.
https://play.google.com/apps/testing/
I hope this helps.

Android In-App-Billing refund/cancel takes long

)
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
}
}
};

In-App Billing test: android.test.purchased already owned

I am currently testing In-App Billing for a future app, and after I successfully "bought" the test item "android.test.purchased" the first time, I now receive the response code 7 every time I try to buy it again, which means that I already own this item.
12-15 23:02:14.149: E/IabHelper(19829): In-app billing error: Unable
to buy item, Error response: 7:Item Already Owned
From what I understand, this purchase is supposed to always be possible, right? So that the developer can test his/her app?
If not, how can I "reset" its state to not owned? I am using the util package from the Google In-App Billing Sample.
Add this code to a thread to initiate consume request.
int response = mService.consumePurchase(3, getPackageName(), purchaseToken);
Here for the purchase test, purchaseToken is
purchaseToken = "inapp:" + getPackageName() + ":android.test.purchased";
And
if (response == 0)
then the consumption is successful.
also don't forget to make mService public in
IabHelper.Java
then it would be possible to access like this:
int response = mHelper.mService.consumePurchase(3, getPackageName(), purchaseToken);
No need to write any special consumption code. Just use the adb command for clearing the Google Play Store data:
adb shell pm clear com.android.vending
It turns out that the android.test.purchased item behaves like a regular ID. It means that if you want be able to buy it again, you have to consume it somewhere in your code. I think that the Google documentation is misleading on this matter, and that they should add another static ID that you can buy endlessly for test purposes.
In-app version 3:
IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
.....................
if (inventory.hasPurchase(SKU_CONTENT)) {
mHelper.consumeAsync(inventory.getPurchase(SKU_CONTENT), null);
}
}
};
Version 3 - Fastest way to solve : Clearing the cache of Google Play Store will let "android.test.purchased" available again.
This is how we can consume the Item
consume.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Thread t = new Thread(new Runnable() {
#Override
public void run() {
String purchaseToken = "inapp:" + getPackageName() + ":android.test.purchased";
try {
Log.d("","Running");
int response = mService.consumePurchase(3, getPackageName(), purchaseToken);
if(response==0)
{
Log.d("Consumed","Consumed");
}else {
Log.d("","No"+response);
}
}catch (RemoteException e)
{
Log.d("Errorr",""+e);
}
}
});
t.start();
}
});
In my opinion if your program is not designed to consume the item you do not need to tweak the code in order to clear the memory of an outside vendor. This will make your code more fragile and you will have then to spend a lot of time to add and remove code that does not belong to your software so it is a bad design to implement a solution like that.
The best solution that worked for me to clear android.test.purchased was
adb uninstall com.yourapp.name
and then
adb shell pm clear com.android.vending
I did not need to clear cash and to browse my apps setting or to change code for that. I did need to add the adb to path variables of windows system which was pretty straight forward. So yes you need to use adb which you probably need anyway so..
You just add your C:\ ...\android-sdk\platform-tools; in windows path in environment variables, and I imagine that it is pretty simple in mac and linux os as well. Hope it helps someone to spend few days less with implementing android in app billings.
Go to the Google Play Developer Console, open Order Management menu item from the left side and select the order you want to refund. Also make sure to remove the entitlement.
The main issue is you have to consume the android.test.purchased item. But this item won't be available in your query inventory, so you can't consume using the normal flow.
So, if you are using IabHelper, in IabHelper class, you can temporarily change the IInAppBillingService mService to public so that it is accessible from your IabHelper.
Then in your class, you can consume like this,
int response = mHelper.mService.consumePurchase(3, getPackageName(), "inapp:"+getPackageName()+":android.test.purchased");
If success, the response is going to be 0.
Hope this helps.
For testing purposes I also suggest you to insert a piece of code that will be clearing all the products that you've bought before calling a method that initializes gp purchase flow. That is especially comfortable, when you test just one item at the moment. E.g. like this:
PurchasesResult purchasesResult = mBillingClient.queryPurchases(BillingClient.SkuType.INAPP);
for (Purchase sourcePurchase : purchasesResult.getPurchasesList()) {
if(sourcePurchase != null){
ConsumeResponseListener listener = new ConsumeResponseListener() {
#Override
public void onConsumeResponse(String outToken, #BillingResponse int responseCode) {
System.out.println("all consumed");
}
};
mBillingClient.consumeAsync(sourcePurchase.getPurchaseToken(), listener);
}else{
System.out.println("null");
}
}
// and then initiate whole process with clear "shoping basket"
BillingFlowParams.Builder builder = new BillingFlowParams.Builder()
.setSku(itemName).setType(BillingClient.SkuType.INAPP);
If you are in test environment
1) In the case of android.test.purchased, I can reset the fake payment by restarting android device(consumed the inventory).
2) In InApp util there is a file called Security.java make it as following, for temporary. Since the testing payment(fake) always return false due to security exception.
public static boolean verifyPurchase(String base64PublicKey,
String signedData, String signature) {
return true; }
Then in your OnIabPurchaseFinishedListener call fechInvForconsumeItem()
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener
= new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result,
Purchase purchase)
{
if (result.isFailure()) {
// Handle error
Log.e("123","Failure");
return;
}
else if (purchase.getSku().equals(ITEM_SKU)) {
Log.e("123","PURCAsed");
fechInvForconsumeItem(); // Restart device if not consume
}
}
};
The fechInvForconsumeItem() is
public void fechInvForconsumeItem() {
mHelper.queryInventoryAsync(mReceivedInventoryListener);
}
IabHelper.QueryInventoryFinishedListener mReceivedInventoryListener
= new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result,
Inventory inventory) {
if (result.isFailure()) {
// Handle failure
Log.e("11","Failure");
} else {
Log.e("11","suc");
mHelper.consumeAsync(inventory.getPurchase(ITEM_SKU),
mConsumeFinishedListener);
}
}
};
Consume Listener is
IabHelper.OnConsumeFinishedListener mConsumeFinishedListener =
new IabHelper.OnConsumeFinishedListener() {
public void onConsumeFinished(Purchase purchase,
IabResult result) {
if (result.isSuccess()) {
} else {
// handle error
Log.e("11","sucConsume");
}
}
};
IabHelper.QueryInventoryFinishedListener
mQueryFinishedListener = new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result, Inventory inventory)
{
if (result.isFailure()) {
return;
}
try {
if(inventory.hasPurchase("product_sku_id"))
{
isItemEnable= true;
mHelper.consumeAsync(inventory.getPurchase("product_sku_id"),null);
}
else
{
isItemEnable = false;
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
In my case, it appears that Google does not record a purchase for the item. Rather, the local copy of Google Play Services caches the purchase. That way, when a second request is made on the same device, android.test.purchased already owned appears. However, using another device or resetting the device clears the cache, and allows the purchase to be repeated.
In my case, I just needed to clear the apps cache. After clearing the cache, I was able to initiate the purchase flow again.
From my device (4.4.2), I navigated to "Settings->Application manager". Next, I selected the app from the "DOWNLOADED" tab, and then "Clear cache".
This is the difference between consumable and non-consumable items; non-consumable items (what you seem to be dealing with here) have their state tracked persistently, while consumable items can be purchased multiple times. You'll have to go into your Play management console and cancel/refund the sale to test it again.

Categories

Resources