问题
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:

The documentation indicates that "To retrieve the product details, call queryInventoryAsync(boolean, List, QueryInventoryFinishedListener)
on your IabHelper instance."
In my own code I callmHelper.queryInventoryAsync(true, iabItemSkus, mQueryFinishedListener)
where:mHelper
is my IabHelper instanceiabItemSkus
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.
回答1:
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.”
回答2:
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.
回答3:
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.
回答4:
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);
回答5:
I have tried above solution but it won't work for me and i got the solution from my another code, so i just want to share so may other will get help. For google in-app code please check this google github project
The difference is call queryInventoryAsync()
with some more parameter that you have to pass and make sure that below method is call inside onIabSetupFinished()
after IabHelper
successfully initialized.
runOnUiThread(new Runnable() {
@Override
public void run() {
refreshItemList();
}
});
Prepare skurequest
private void refreshItemList() {
List<String> itemSku = new ArrayList<>();
List<String> subSku = new ArrayList<>();
subSku.add(AppConstant.InApp.SKU_ONE_ID);
mHelper.queryInventoryAsync(true, itemSku, subSku, mQotInventoryListener);
}
Below is the listener where you receive your SkuDetails
IabHelper.QueryInventoryFinishedListener mQotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
@Override
public void onQueryInventoryFinished(IabResult result, Inventory inv) {
try {
Log.d(TAG, "mQotInventoryListener Query inventory finished.");
// Have we been disposed of in the meantime? If so, quit.
if (mHelper == null) return;
// Is it a failure?
if (result.isFailure()) {
Log.e(TAG, "mQotInventoryListener Failed to query inventory: " + result);
return;
}
Log.d(TAG, "mQotInventoryListener Query inventory was successful.");
try {
//Here you just pass SKU_ID that you want its detail
SkuDetails skuDetails = inv.getSkuDetails(AppConstant.InApp.SKU_ONE_ID);
if (skuDetails != null) {
Log.d(TAG, "skuDetails are received");
} else {
Log.e(TAG, "skuDetails are null");
}
} catch (Exception e) {
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
回答6:
In order to have In-App Billing working, follow these steps:
- Add
<uses-permission android:name="com.android.vending.BILLING" />
permission to the AndroidManifest.xml . - Create a signed APK and upload it to the developer console, as alpha, beta or production.
- Publish the APK or create a google group and add an alpha list of testers.
- You have to wait some hours in order to see the in-app purchases in your app
来源:https://stackoverflow.com/questions/23629148/in-app-billing-query-items-available-for-purchase-returns-0-items