Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In-App Billing: "Query items available for purchase" returns 0 items

I am trying to implement in-app-billing for my app.
I am following the implementation used in google's TriviaDrive sample app, and the relevant documentation on the Developer website.
My code is working as expected but when I try to "Query Items Available for Purchase", the resulting Inventory object contains 0 objects, even though I have created a product.

I have created a Managed Product with the id paid_version using the Google Play Developer Console, as shown in the image below: In-App Products - Screenshot

The documentation indicates that "To retrieve the product details, call queryInventoryAsync(boolean, List, QueryInventoryFinishedListener) on your IabHelper instance."

In my own code I call
mHelper.queryInventoryAsync(true, iabItemSkus, mQueryFinishedListener)
where:
mHelper is my IabHelper instance
iabItemSkus is a List containing a single item with the value "paid_version"
mQueryFinishedListener is my listener defined below.

IabHelper.QueryInventoryFinishedListener mQueryFinishedListener = new IabHelper.QueryInventoryFinishedListener() {
        @Override
        public void onQueryInventoryFinished(IabResult result, Inventory inv) {
            if (result.isFailure()) {
                Log.d(TAG, "Querying Inventory Failed: " + result);
                return;
            }

            Log.d(TAG, "Title: " + inv.getSkuDetails(SKU_PAID).getTitle());
            Log.d(TAG, "Description: " + inv.getSkuDetails(SKU_PAID).getDescription());
            Log.d(TAG, "Price = " + inv.getSkuDetails(SKU_PAID).getPrice());
        }
    };

But on debugging I can see that the Inventory object passed back in the QueryInventoryFinishedListener contains 0 items, and so calls like inv.getSkuDetails(SKU_PAID).getTitle() give a null pointer exception.

I can't work out where I'm going wrong. I was expecting the Inventory object to contain the details for my paid_version in-app product.

Below are just the parts of my code and LogCat I think are relevant to this problem (trying to avoid giving you code overload!), but if more detail on some other part of the code would be helpful, let me know.

From my activity:

...
private static final String SKU_PAID = "paid_version";
private static final String TAG = "MyActivity";
private IabHelper mHelper;
...

IabHelper.QueryInventoryFinishedListener mQueryFinishedListener = new IabHelper.QueryInventoryFinishedListener() {
    @Override
    public void onQueryInventoryFinished(IabResult result, Inventory inv) {
        if (result.isFailure()) {
            Log.d(TAG, "Querying Inventory Failed: " + result);
            return;
        }

        Log.d(TAG, "Title: " + inv.getSkuDetails(SKU_PAID).getTitle()); // <-- Line 266 of MyActivity.java
        Log.d(TAG, "Description: " + inv.getSkuDetails(SKU_PAID).getDescription());
        Log.d(TAG, "Price = " + inv.getSkuDetails(SKU_PAID).getPrice());
    }
};
...

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ...

    final List<String> iabItemSkus = new ArrayList<String>();
    iabItemSkus.add(SKU_PAID);
    // In App Billing
    String base64EncodedPublicKey = "... My Public Key ...";
    mHelper = new IabHelper(this, base64EncodedPublicKey);
    mHelper.enableDebugLogging(true);
    mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
       @Override
       public void onIabSetupFinished(IabResult result) {
           if (!result.isSuccess()) {
               Log.d(TAG, "Problem setting up In-app Billing: " + result);
           }
           // Have we been disposed of in the meantime? If so, quit.
           if (mHelper == null) return;

           // IAB is fully set up. Now, let's get list of available items
           Log.d(TAG, "Setup successful. Querying inventory.");
           mHelper.queryInventoryAsync(true, iabItemSkus, mQueryFinishedListener);
       }
    });
    ...
}

From my LogCat:

...
05-13 19:46:59.609  22390-22390/xxx.xxxxxx.xxxxxx D/IabHelper﹕ Starting in-app billing setup.
05-13 19:46:59.629  22390-22390/xxx.xxxxxx.xxxxxx D/IabHelper﹕ Billing service connected.
05-13 19:46:59.629  22390-22390/xxx.xxxxxx.xxxxxx D/IabHelper﹕ Checking for in-app billing 3 support.
05-13 19:46:59.629  22390-22390/xxx.xxxxxx.xxxxxx D/IabHelper﹕ In-app billing version 3 supported for xxx.xxxxxx.xxxxxx
05-13 19:46:59.639  22390-22390/xxx.xxxxxx.xxxxxx D/IabHelper﹕ Subscriptions AVAILABLE.
05-13 19:46:59.639  22390-22390/xxx.xxxxxx.xxxxxx D/MyActivity﹕ Setup successful. Querying inventory.
05-13 19:46:59.639  22390-22390/xxx.xxxxxx.xxxxxx D/IabHelper﹕ Starting async operation: refresh inventory
05-13 19:46:59.649  22390-22596/xxx.xxxxxx.xxxxxx D/IabHelper﹕ Querying owned items, item type: inapp
05-13 19:46:59.649  22390-22596/xxx.xxxxxx.xxxxxx D/IabHelper﹕ Package name: xxx.xxxxxx.xxxxxx
05-13 19:46:59.649  22390-22596/xxx.xxxxxx.xxxxxx D/IabHelper﹕ Calling getPurchases with continuation token: null
05-13 19:46:59.659  22390-22596/xxx.xxxxxx.xxxxxx D/IabHelper﹕ Owned items response: 0
05-13 19:46:59.659  22390-22596/xxx.xxxxxx.xxxxxx D/IabHelper﹕ Continuation token: null
05-13 19:46:59.659  22390-22596/xxx.xxxxxx.xxxxxx D/IabHelper﹕ Querying SKU details.
05-13 19:46:59.689  22390-22596/xxx.xxxxxx.xxxxxx D/IabHelper﹕ Querying owned items, item type: subs
05-13 19:46:59.689  22390-22596/xxx.xxxxxx.xxxxxx D/IabHelper﹕ Package name: xxx.xxxxxx.xxxxxx
05-13 19:46:59.689  22390-22596/xxx.xxxxxx.xxxxxx D/IabHelper﹕ Calling getPurchases with continuation token: null
05-13 19:46:59.699  22390-22596/xxx.xxxxxx.xxxxxx D/IabHelper﹕ Owned items response: 0
05-13 19:46:59.699  22390-22596/xxx.xxxxxx.xxxxxx D/IabHelper﹕ Continuation token: null
05-13 19:46:59.699  22390-22596/xxx.xxxxxx.xxxxxx D/IabHelper﹕ Querying SKU details.
05-13 19:46:59.829  22390-22596/xxx.xxxxxx.xxxxxx D/IabHelper﹕ Ending async operation: refresh inventory
05-13 19:46:59.829  22390-22390/xxx.xxxxxx.xxxxxx D/AndroidRuntime﹕ Shutting down VM
05-13 19:46:59.829  22390-22390/xxx.xxxxxx.xxxxxx W/dalvikvm﹕ threadid=1: thread exiting with uncaught exception (group=0x41b31ba8)
05-13 19:46:59.839  22390-22390/xxx.xxxxxx.xxxxxx E/AndroidRuntime﹕ FATAL EXCEPTION: main
    Process: xxx.xxxxxx.xxxxxx, PID: 22390
    java.lang.NullPointerException
            at xxx.xxxxxx.xxxxxx.MyActivity$1.onQueryInventoryFinished(MyActivity.java:266)
            at xxx.xxxxxx.xxxxxx.util.IabHelper$2$1.run(IabHelper.java:630)
            at android.os.Handler.handleCallback(Handler.java:733)
            at android.os.Handler.dispatchMessage(Handler.java:95)
            at android.os.Looper.loop(Looper.java:136)
            at android.app.ActivityThread.main(ActivityThread.java:5017)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:515)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
            at dalvik.system.NativeStart.main(Native Method)
...

PS: I've just updated my code to highlight which is line 266.


Additional

I was experiencing this problem 12 hours after uploading my APK, and as you can see from the LogCat, it indicates "In-app billing version 3 supported" for my app.
The problem persisted regardless of whether I set the Status of the IN-APP PRODUCT as active or inactive.

Now 24 hours later it magically decides to work correctly.

From this I can only determine that it was a problem with Google Play, not with my code.

like image 225
Sound Conception Avatar asked May 13 '14 10:05

Sound Conception


4 Answers

I'm in the same boat. I'm waiting to see what happens.

In google documentation http://developer.android.com/training/in-app-billing/test-iab-app.html

there is a warning, perhaps this is the problem:

Warning: It may take up to 2-3 hours after uploading the APK for Google Play to recognize your updated APK version. If you try to test your application before your uploaded APK is recognized by Google Play, your application will receive a ‘purchase cancelled’ response with an error message “This version of the application is not enabled for In-app Billing.”

like image 83
Francisco Fernandes Avatar answered Oct 24 '22 08:10

Francisco Fernandes


I'm having the same problem. If you look at the IabHelper code, I think the issue is this:

Inside queryInventory:

if (querySkuDetails) {
    r = querySkuDetails(ITEM_TYPE_INAPP, inv, moreItemSkus);
    if (r != BILLING_RESPONSE_RESULT_OK) {
        throw new IabException(r, "Error refreshing inventory (querying prices of items).");
    }
}

But then the first few lines of querySkuDetails:

int querySkuDetails(String itemType, Inventory inv, List<String> moreSkus)
        throws RemoteException, JSONException {
    logDebug("Querying SKU details.");
    ArrayList<String> skuList = new ArrayList<String>();
    skuList.addAll(inv.getAllOwnedSkus(itemType));

Notice the last line populates the skuList with all owned skus. Not all available ones.

So the answer is that the IabHelper doesn't really support querying available purchases.

like image 2
user3690202 Avatar answered Oct 24 '22 10:10

user3690202


Late to answer, I too ran into same problem. After reading whole documentation for 3 days, finally I found the culprit.

According to documentation:

Draft Apps are No Longer Supported.

Previously, you could publish a "draft" version of your app for testing. This functionality is no longer supported. Instead, there are two ways you can test how a pre-release app functions on the Google Play store:

You can publish an app to the alpha or beta distribution channels. This makes the app available on the Google Play store, but only to the testers you put on a "whitelist". In a few cases, you can test Google Play functionality with an unpublished app. For example, you can test an unpublished app's in-app billing support by using static responses, special reserved product IDs that always return a specific result (like "purchased" or "refunded").

Google should ease this process.

like image 1
Chintan Soni Avatar answered Oct 24 '22 08:10

Chintan Soni


To get a list of available purchases with prices, you need to call queryInventoryAsync with the parameters - lists of shopping identifiers:

ArrayList<String> skuList = new ArrayList<String> ();
skuList.add("purchase1");
ArrayList<String> subsList = new ArrayList<String> ();
subsList.add("subscribe1");
mHelper.queryInventoryAsync(true, skuList, subsList, mGotInventoryListener);
like image 1
Alexander Ivanov Avatar answered Oct 24 '22 09:10

Alexander Ivanov