I am able to successfully purchase in-app billing items in my app, but I have not yet been able to successfully check which items the user has purchased, as I am getting a a null pointer exception on this line:
ownedItems = mService.getPurchases(3, getPackageName(), "inapp", null);
I did see the other posts on this topic, but those solutions either did not work in my case, or I don't fully understand the solution:
getPurchases() NullPointerException initializing mService
In App Null Pointer Exception
Android In-App billing: Null Pointer Exception
Here is the entire method:
private void checkOwnedItems() throws RemoteException {
Bundle ownedItems;
String sku = "";
ownedItems = mService.getPurchases(3, getPackageName(), "inapp", null);
int response = ownedItems.getInt("RESPONSE_CODE");
if (response == 0) {
ArrayList<String> ownedSkus = ownedItems.getStringArrayList("INAPP_PURCHASE_ITEM_LIST");
ArrayList<String> purchaseDataList = ownedItems.getStringArrayList("INAPP_PURCHASE_DATA_LIST");
if (purchaseDataList.size() > 0) {
//user owns 1 or more items
for (int i = 0; i < purchaseDataList.size(); ++i) {
sku = ownedSkus.get(i);
}
Toast.makeText(SettingsActivity.this, "You own these features: " + sku, Toast.LENGTH_LONG).show();
} else {
//user owns zero items, launch purchase flow
MACAddress = UniqueID.getMACAddress("wlan0");
int requestCode = 22222;
mHelper.launchPurchaseFlow(SettingsActivity.this, productID, requestCode, mPurchaseFinishedListener, MACAddress);
}
}
}
I ran the debugger, and it appears that mService is null. Where and how am I supposed to initialize mService?
Currently I am trying to initialize mService in the onServiceConnected method, but perhaps onServiceConnected is never getting called. When and how should I be calling the onServiceConnected method?
ServiceConnection mServiceConn = new ServiceConnection()
{
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
mService = IInAppBillingService.Stub.asInterface(service);
}
#Override
public void onServiceDisconnected(ComponentName name) {
mService = null;
}
};
I am able to successfully purchase in-app billing items in my app, but
I have not yet been able to successfully check which items the user
has purchased
You should use the Inventory instance to get the list of ownedItems purchased by the current user. For example, on app start I'm checking whether the user has purchased My_APP_DEMO_PRODUCT or not and based on that I'm changing the UI of the screen. For the above use case, I'm using QueryInventoryFinishedListener .
Note The following code is taken from the Google's IAB sample project.
// Listener that's called when we finish querying the items and subscriptions we own
IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
Log.d(DEBUG_TAG, "Query inventory finished.");
// if we were disposed of in the meantime, quit.
if (mHelper == null) {
return;
}
// Is it a failure?
if (result.isFailure()) {
complain("Failed to query inventory: " + result);
return;
}
Log.d(DEBUG_TAG, "Query inventory was successful.");
/*
* Check for items we own. Notice that for each purchase, we check
* the developer payload to see if it's correct! See
* verifyDeveloperPayload().
*/
// Do we have the My_APP_DEMO_PRODUCT purchase.
Purchase premiumPurchase = inventory.getPurchase(My_APP_DEMO_PRODUCT
);
boolean isPurchased = (premiumPurchase != null && verifyDeveloperPayload(premiumPurchase));
if (isPurchased) {
Log.d(DEBUG_TAG, “User had purchased this My_APP_DEMO_PRODUCT product”);
// Do whatever you want
changeAppUI();
}
Log.d(DEBUG_TAG, "Initial inventory query finished; enabling main UI.");
}
};
And in Activity's onCreate() method,
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
Log.d(DEBUG_TAG, "Setup finished.");
if (!result.isSuccess()) {
// Oh noes, there was a problem.
complain("Problem setting up in-app billing: " + result);
return;
}
if (mHelper == null) return;
// IAB is fully set up. Now, let's get an inventory of stuff we own.
Log.d(DEBUG_TAG, "Setup successful. Querying inventory.");
mHelper.queryInventoryAsync(mGotInventoryListener);
}
});
Related
First time implementing in-app billing. i was able to complete first transaction, but second time while purchasing same item i am getting "Error response: 7:Item Already Owned" on onIabPurchaseFinished Method, how to make it re-purchase-able? any help would be appreciated.
screenshot of code structure is also attached.
Onclick purchase i am calling following method:
initilizeInAppPurchasePakages();
public void initilizeInAppPurchasePakages()
{
String base64EncodedPublicKey=getString(R.string.inAppBillingKey);
mHelper = new IabHelper(getActivity(), base64EncodedPublicKey);
mPurchaseFinishedListener
= new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result, Purchase purchase)
{
if (result.isFailure()) {
Log.d(TAG, "Error purchasing: " + result);
// Toast.makeText(getActivity(),"fail to purchase"+result, Toast.LENGTH_SHORT).show();
return;
}
else if (purchase.getSku().equals(purchaseItemId)) {
transactionId=purchase.getOrderId();
PackageFragment.isNeedToUpdate = true;
// consume the gas and update the UI
// Toast.makeText(getActivity(), "purchase successfully", Toast.LENGTH_SHORT).show();
mHelper.consumeAsync(purchase,
mConsumeFinishedListener);
}
else if (purchase.getSku().equals(purchaseItemId)) {
// give user access to premium content and update the UI
// Toast.makeText(getActivity(), "purchase successfully", Toast.LENGTH_SHORT).show();
}
}
};
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
if (!result.isSuccess()) {
// Oh no, there was a problem.
// Toast.makeText(getActivity(), "connection Error", Toast.LENGTH_SHORT).show();
}
else
{
// Toast.makeText(getActivity(), "connected successfully", Toast.LENGTH_SHORT).show();
Log.d(TAG, "Problem setting up In-app Billing: " + result);
mHelper.launchPurchaseFlow(getActivity(), purchaseItemId, 10001,
mPurchaseFinishedListener, "testing");
}
// Hooray, IAB is fully set up!
}
});
mConsumeFinishedListener =
new IabHelper.OnConsumeFinishedListener() {
public void onConsumeFinished(Purchase purchase, IabResult result) {
if (result.isSuccess()) {
serverUtilities.savePayment(pakageId,pakagePrice,transactionId);
// Toast.makeText(getActivity(), "consumed", Toast.LENGTH_SHORT).show();
// provision the in-app purchase to the user
// (for example, credit 50 gold coins to player's character)
}
else {
// handle error
}
}
};
}
Alright,
After spending time on reading documentation of google in app billing i found that i need to test in app billing through testing so what i did
test billing:
mHelper.launchPurchaseFlow(getActivity(), purchaseItemIdSKU, 10001,
mPurchaseFinishedListener, "testing");
Calling only first time to remove that already purchased entry record above the test billing line that i used above:
ArrayList skusToBeListed = null;
skusToBeListed = new ArrayList<String> ();
skusToBeListed.add (SKU_PREMIUM);
skusToBeListed.add (SKU_PREMIUM_ELITE);
skusToBeListed.add (SKU_PREMIUM_PLUS);
mHelper.queryInventoryAsync(true, skusToBeListed, mGotInventoryListener);
and once its done you can do many test bookings without any headache.
mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
#Override
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
if (result.isFailure()) {
//complain("Failed to query inventory: " + result);
return;
}
Purchase currentPurchase = inventory.getPurchase(purchaseItemIdSKU);
if(currentPurchase != null)
{
//Boolean mIsPremium = (currentPurchase != null && verifyDeveloperPayload(currentPurchase));
mHelper.consumeAsync(currentPurchase,mConsumeFinishedListener);
}
}
} ;
Note:
In case of test billing transaction id will not be returned so make sure how to handle that, i did tested my in app billing by hard coding it to following format: "GPA.1234-5678-9012-34567"
In short above code of mine was fine if i would i have used it on production mode, but in case of testing mode it was giving me errors that i explained and given solution how i solved it.
I have just implemented the in app billing functionality in my app.
I only have one product to unlock all premium features.
The purchase itself works, but after the purchase flow, my code obviously isn't able to recognise the success of the purchase, because there appears a complain window right after purchase saying "Articel already owned". So somehow the purchase flow is not recogniced to be successful. After i restart the app, the pro features are unlocked, because the querying of the inventory says, the pro features have been bought.
I am out of ideas, so here is my code:
// SKUs for our products: the premium upgrade (non-consumable)
String SKU_PREMIUM;
// (arbitrary) request code for the purchase flow
final int RC_REQUEST = 10001;
// The helper object
IabHelper mHelper;
// Provides purchase notification while this app is running
public IabBroadcastReceiver mBroadcastReceiver;
IabHelper.QueryInventoryFinishedListener mGotInventoryListener;
AppCompatActivity activity;
private String payload;
public InAppBillingHelper(AppCompatActivity context, final IabBroadcastReceiver.IabBroadcastListener contextBroadcastListener, String base64EncodedPublicKey,String skuPremium, boolean debug){
//String base64EncodedPublicKey = "VALENTIN_HERR_SCHAFKOPF_RECHENHELFER";
this.activity = context;
SKU_PREMIUM = skuPremium;
// Create the helper, passing it our context and the public key to verify signatures with
Log.d(TAG, "Creating IAB helper.");
mHelper = new IabHelper(context, base64EncodedPublicKey);
// enable debug logging (for a production application, you should set this to false).
mHelper.enableDebugLogging(debug);
// Start setup. This is asynchronous and the specified listener
// will be called once setup completes.
Log.d(TAG, "Starting setup.");
// Listener that's called when we finish querying the items and subscriptions we own
mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
Log.d(TAG, "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()) {
complain("Fehler bei der Bestimmung der App-Lizenz: " + result);
// TODO analyse results and output it to the user
return;
}
Log.d(TAG, "Query inventory was successful.");
// Do we have the premium upgrade?
Purchase premiumPurchase = inventory.getPurchase(SKU_PREMIUM);
mIsPremium = (premiumPurchase != null && verifyDeveloperPayload(premiumPurchase));
Log.d(TAG, "User is " + (mIsPremium ? "PREMIUM" : "NOT PREMIUM"));
}
updateUi();
setWaitScreen(false);
Log.d(TAG, "Initial inventory query finished; enabling main UI.");
}
};
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
Log.d(TAG, "Setup finished.");
if (!result.isSuccess()) {
// Oh noes, there was a problem.
complain("Problem setting up in-app billing: " + result);
return;
}
// Have we been disposed of in the meantime? If so, quit.
if (mHelper == null) return;
mBroadcastReceiver = new IabBroadcastReceiver(contextBroadcastListener);
IntentFilter broadcastFilter = new IntentFilter(IabBroadcastReceiver.ACTION);
try {
activity.registerReceiver(mBroadcastReceiver, broadcastFilter);
}catch (Exception e)
{}
// IAB is fully set up. Now, let's get an inventory of stuff we own.
Log.d(TAG, "Setup successful. Querying inventory.");
try {
mHelper.queryInventoryAsync(mGotInventoryListener);
} catch (IabHelper.IabAsyncInProgressException e) {
complain("Error querying inventory. Another async operation in progress.");
}
}
});
}
public void receivedBroadcast() {
// Received a broadcast notification that the inventory of items has changed
Log.d(TAG, "Received broadcast notification. Querying inventory.");
try {
mHelper.queryInventoryAsync(mGotInventoryListener);
} catch (IabHelper.IabAsyncInProgressException e) {
complain("Error querying inventory. Another async operation in progress.");
}
}
// User clicked the "Upgrade to Premium".
public void handlePremiumPurchase() {
Log.d(TAG, "Upgrade button clicked; launching purchase flow for upgrade.");
setWaitScreen(true);
payload = "";
try {
mHelper.launchPurchaseFlow(activity, SKU_PREMIUM, RC_REQUEST,
mPurchaseFinishedListener, payload);
} catch (IabHelper.IabAsyncInProgressException e) {
complain("Error launching purchase flow. Another async operation in progress.");
setWaitScreen(false);
}catch (NullPointerException e)
{
complain("Fehler beim Kaufversuch. Stelle sicher, dass mit einem Google Konto im PlayStore angemeldet bist und eine stabile Internetverbindung besteht." +
"Versuche es nach noch einmal." +
"");
setWaitScreen(false);
}
}
public boolean isPremium() {
return mIsPremium;
}
public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
Log.d(TAG, "onActivityResult(" + requestCode + "," + resultCode + "," + data);
if (mHelper == null) return false;
// Pass on the activity result to the helper for handling
if (!mHelper.handleActivityResult(requestCode, resultCode, data)) {
// Todo handle
}
else {
Log.d(TAG, "onActivityResult handled by IABUtil.");
}
return true;
}
/** Verifies the developer payload of a purchase. */
boolean verifyDeveloperPayload(Purchase p) {
String payload = p.getDeveloperPayload();
// Todo payload string verification
return true;//payload.equals(this.payload);
}
// Callback for when a purchase is finished
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
Log.d(TAG, "Purchase finished: " + result + ", purchase: " + purchase);
// if we were disposed of in the meantime, quit.
if (mHelper == null) return;
if (result.isFailure()) {
if(result.getResponse() == IabHelper.BILLING_RESPONSE_RESULT_ITEM_ALREADY_OWNED)
{
mIsPremium = true;
updateUi();
//complain("Du besitzt bereits das Premium Paket auf einem deiner angemeldeten Google Konten.");
}
else {
complain("Fehler beim Kauf: " + result);
}
setWaitScreen(false);
return;
}
if (!verifyDeveloperPayload(purchase)) {
complain("Error purchasing. Authenticity verification failed.");
setWaitScreen(false);
return;
}
Log.d(TAG, "Purchase successful.");
if (purchase.getSku().equals(SKU_PREMIUM)) {
// bought the premium upgrade!
Log.d(TAG, "Purchase is premium upgrade. Congratulating user.");
alert("Danke für das Premium Upgrade! Ich wünsche dir weiterhin viel Spaß bei der Nutzung!");
mIsPremium = true;
updateUi();
setWaitScreen(false);
}
}
};
Btw: The code is a modified version of the **trivial drive* example.
Thanks in advance!
Although it's not the cleanest solution, just query the inventory again if you have a "already owned error". Then update the UI only according to the inventory query result
Thanks #DerAdler . I implemented it just the way you said and it worked.
in my app user can buy ad removal, I keep this item (no consume). So I have fragment in my main activity that check if user bought item.
public class BillingInventoryFragment extends Fragment {
// Helper billing object
private IabHelper mHelper;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
initialiseBilling();
}
private void initialiseBilling() {
if (mHelper != null) {
return;
}
// Create the helper, passing it our context and the public key to verify signatures with
mHelper = new IabHelper(getActivity(), BillingUtils.getApplicationKey());
// Enable debug logging (for a production application, you should set this to false).
mHelper.enableDebugLogging(true);
// Start setup. This is asynchronous and the specified listener will be called once setup completes.
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
#Override
public void onIabSetupFinished(IabResult result) {
// Have we been disposed of in the meantime? If so, quit.
if (mHelper == null) {
return;
}
// Something went wrong
if (!result.isSuccess()) {
Log.e(getActivity().getApplicationInfo().name, "Problem setting up in-app billing: " + result.getMessage());
return;
}
// IAB is fully set up. Now, let's get an inventory of stuff we own.
mHelper.queryInventoryAsync(iabInventoryListener());
}
});
}
/**
* Listener that's called when we finish querying the items and subscriptions we own
*/
private IabHelper.QueryInventoryFinishedListener iabInventoryListener() {
return new IabHelper.QueryInventoryFinishedListener() {
#Override
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
// Have we been disposed of in the meantime? If so, quit.
if (mHelper == null) {
return;
}
// Something went wrong
if (!result.isSuccess()) {
Log.d(TAG, String.format("result not success, result = %s", result) );
return;
}
// Do your checks here...
// Do we have the premium upgrade?
Purchase purchasePro = inventory.getPurchase(BillingUtils.SKU_PRO); // Where BillingUtils.SKU_PRO is your product ID (eg. permanent.ad_removal)
Log.d(TAG, String.format("Purchase pro = %s", purchasePro));
BillingUtils.isPro = (purchasePro != null && BillingUtils.verifyDeveloperPayload(purchasePro));
// After checking inventory, re-jig stuff which the user can access now
// that we've determined what they've purchased
BillingUtils.initialiseStuff();
}
};
}
/**
* Very important!
*/
#Override
public void onDestroy() {
super.onDestroy();
if (mHelper != null) {
mHelper.dispose();
mHelper = null;
}
}
}
Everything works on one device but when I tested it on second device:
inventory.getPurchase(BillingUtils.SKU_PRO);
returns null.
When I try buy item again on this second device, I can't because I own it.
If you are sure that you bought this item successfully.It may be a problem related with used Gmail
You should use the same Gmail in the two devices because Google is identifying its users using the registered Gmail on device.
I have a situation where I tested on a test device with the item id: android.test.purchased
And things worked. Then once I changed that string with the real item id, I started getting Item Not Found error when I pressed the buy button.
Also the buy item is a subscription and not a 1-time purchase. Does that make a difference?
I was not sure which part of the code may be at fault, or what may be missing so here is the class for this:
public class SubscribeIntroActivity extends BaseActivity
{
IabHelper mHelper;
// Does the user have the premium upgrade?
boolean isSubscriber = false;
// (arbitrary) request code for the purchase flow
static final int RC_REQUEST = 105;
// Subscribe SKU
static final String SUBSCRIBE_SKU = "11";
// Subscribe SKU
static final String TAG = "BILLING";
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.subscribe_intro);
String base64EncodedPublicKey = "my_real_key";
// Create the helper, passing it our context and the public key to verify signatures with
mHelper = new IabHelper(this, base64EncodedPublicKey);
mHelper.enableDebugLogging(true);
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result)
{
if (!result.isSuccess())
{
// Oh noes, there was a problem.
//complain("Problem setting up in-app billing: " + result);
return;
}
// Hooray, IAB is fully set up. Now, let's get an inventory of stuff we own.
mHelper.queryInventoryAsync(mGotInventoryListener);
}
});
// Listener that's called when we finish querying the items we own
IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
if (result.isFailure())
{
//complain("Failed to query inventory: " + result);
return;
}
Log.d(TAG, "Query inventory was successful.");
// Do we have the premium upgrade?
isSubscriber = inventory.hasPurchase(SUBSCRIBE_SKU);
Log.d(TAG, "User is " + (isSubscriber ? "SUBSCRIBER" : "NOT SUBSCRIBER"));
// Check for gas delivery -- if we own gas, we should fill up the tank immediately
if (inventory.hasPurchase( SUBSCRIBE_SKU ))
{
Log.d(TAG, "HAS SUBSCRIPTION");
return;
}
//updateUi();
// TODO: TELL USER HE IS SUBSCIBED AND TAKE THEM TO THE QUESTION
//setWaitScreen(false);
Log.d(TAG, "Initial inventory query finished; enabling main UI.");
}
};
Button subscribe = (Button)findViewById(R.id.subscribe);
subscribe.setOnClickListener(new Button.OnClickListener()
{
public void onClick(View v)
{
// FIRST CHECK IF THE USER IS ALREADY A SUBSCRIBER.
mHelper.launchPurchaseFlow(SubscribeIntroActivity.this, SUBSCRIBE_SKU, RC_REQUEST, mPurchaseFinishedListener);
// Send me an email that a comment was submitted on a question.
//sendEmail("My Questions -> Add Question", "Someone clicked on add-question from my questions. User id: " + user_id );
//Intent myIntent = new Intent(MotivationActivity.this, WePromoteActivity.class);
//MotivationActivity.this.startActivity(myIntent);
}
});
// mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
// public void onIabSetupFinished(IabResult result)
// {
// if (!result.isSuccess())
// {
// // Oh noes, there was a problem.
//
// Log.d("INAPP BILLING", "Problem setting up In-app Billing: " + result);
// }
//
// // Hooray, IAB is fully set up!
//
//
// // First arg is whether product details should be retrieved
// // The List argument consists of one or more product IDs (also called SKUs) for the products that you want to query.
// // the QueryInventoryFinishedListener argument specifies a listener is
// // notified when the query operation has completed
// // and handles the query response.
//// mHelper.queryInventoryAsync(false, new ArrayList ( ).add("1"),
//// mQueryFinishedListener);
//
// //mHelper.queryInventoryAsync(mGotInventoryListener);
//
//
//// mHelper.launchPurchaseFlow(SubscribeIntroActivity.this, "11" , 105, mPurchaseFinishedListener, "" );
// }
// });
}
IabHelper.QueryInventoryFinishedListener
mQueryFinishedListener = new IabHelper.QueryInventoryFinishedListener()
{
public void onQueryInventoryFinished(IabResult result, Inventory inventory)
{
if (result.isFailure()) {
// handle error
return;
}
String applePrice =
inventory.getSkuDetails("1").getPrice();
String bananaPrice =
inventory.getSkuDetails(SUBSCRIBE_SKU).getPrice();
}
// update the UI
};
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener
= new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result, Purchase purchase)
{
if (result.isFailure())
{
Log.d("ERROR", "Error purchasing: " + result);
return;
}
else if (purchase.getSku().equals("1"))
{
// consume the gas and update the UI
}
else if (purchase.getSku().equals(SUBSCRIBE_SKU))
{
// give user access to premium content and update the UI
Log.d(TAG, "PURCHASED: " + result);
}
}
};
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?
isSubscriber = inventory.hasPurchase(SUBSCRIBE_SKU);
// update UI accordingly
}
}
};
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.d(TAG, "onActivityResult(" + requestCode + "," + resultCode + "," + data);
// Pass on the activity result to the helper for handling
if (!mHelper.handleActivityResult(requestCode, resultCode, data)) {
// not handled, so handle it ourselves (here's where you'd
// perform any handling of activity results not related to in-app
// billing...
super.onActivityResult(requestCode, resultCode, data);
}
else {
Log.d(TAG, "onActivityResult handled by IABUtil.");
}
}
#Override
public void onDestroy()
{
super.onDestroy();
if (mHelper != null) mHelper.dispose();
mHelper = null;
}
}
Since this is a subscription I believe you must replace this line:
mHelper.launchPurchaseFlow(SubscribeIntroActivity.this, SUBSCRIBE_SKU, RC_REQUEST, mPurchaseFinishedListener);
with this one:
mHelper.launchSubscriptionPurchaseFlow(SubscribeIntroActivity.this, SUBSCRIBE_SKU, RC_REQUEST, mPurchaseFinishedListener);
At least this is what I use in my code and I have successfully tested it.
I don't think the new in-app billing version 3 currently supports subscriptions. I could be wrong, but I haven't seen any mention of subscriptions in the sample code or documentation, so that could be why you're receiving the the "Item not found" error.
In general, to test purchasing a real item, you need to upload a version of your app to the developer console (but you don't need to publish it), and you have to be using the same (signed) version to test with. It also takes Google Play some time to process/propagate newly added items...so sometimes you unfortunately just need to wait. Check out developer.android.com/training/in-app-billing/test-iab-app.html if you haven't already.
One more thing to know. Purchases doesn't appear immediately. You can test them after 2-3 hours from moment, when you publish it.
Sorry for my English =)
There's a limit of 20 items per request.
Even if you split them in groups of 20 items, it still does
skuList.addAll(inv.getAllOwnedSkus(itemType));
which guarantees an error if number_of_owned_items + number_of_queried_items is greater than 20, in particular, if number_of_owned_items is 21.
Now I understand why someone suggested rewriting the code from scratch from the very beginning.
can anyone please tell me how to get list of products available for purchase?I am using trivial drive example for reference.suppose on developer console in my app i listed two products as product1 & product 2.How to get list of available products in my app.Please help.
I think this should be what you're looking for:
http://developer.android.com/training/in-app-billing/list-iab-products.html#QueryDetails
Try this,
IabHelper billingHelper = new IabHelper(this, AppPublicKey.BASE_64_KEY);
billingHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
#Override
public void onIabSetupFinished(IabResult result) {
if (!result.isSuccess()) {
// Oh noes, there was a problem.
complain("Problem setting up in-app billing: " + result);
return;
}
// Have we been disposed of in the meantime? If so, quit.
if (billingHelper == null) return;
List<String> aListSKU = new ArrayList<String>();
aListSKU.add(ProductId.SKU100);
aListSKU.add(ProductId.SKU200);
aListSKU.add(ProductId.SKU400);
billingHelper.queryInventoryAsync(true, aListSKU, mGotInventoryListener);
}
});
Create QueryInventoryFinishedListener Interface
// Listener that's called when we finish querying the items and subscriptions we own
IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
#Override
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
// Have we been disposed of in the meantime? If so, quit.
if (billingHelper == null) return;
// Is it a failure?
if (result.isFailure()) {
Toast.makeText(ProfileCreditActivity.this, "Failed to query inventory: " + result, Toast.LENGTH_SHORT).show();
return;
}
Log.d(TAG, "Query inventory was successful.");
SkuDetails sku100Details = inventory.getSkuDetails(ProductId.SKU100);
sku100Price = sku100Details.getPrice();
sku100Title = sku100Details.getTitle();
SkuDetails sku200Details = inventory.getSkuDetails(ProductId.SKU200);
sku200Price = sku200Details.getPrice();
sku200Title = sku100Details.getTitle();
SkuDetails sku400Details = inventory.getSkuDetails(ProductId.SKU400);
sku400Price = sku400Details.getPrice();
sku400Title = sku100Details.getTitle();
updateUI();
}
};
Update your UI here
private void updateUI() {
}