I am trying to set up in-app-billing and test using a static product ID in Google Play.
Am following developer tutorial here.
When the launhPurcahseFlow method is called on the labHelper object I get the exception:
java.lang.IllegalStateException: IAB helper is not set up. Can't
perform operation: launchPurchaseFlow at
com.android.vending.billing.IabHelper.checkSetupDone(IabHelper.java:782)
Have been searching for hours and can't find a solution that works.
Any input appreciated.
My code is:
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
// compute your public key and store it in base64EncodedPublicKey
mHelper = new IabHelper(this, base64EncodedPublicKey);
// enable debug logging (for a production application, you should set this to false).
mHelper.enableDebugLogging(true);
//perform service binding to Google Bill ser and return ny errors with IabResult object and listener
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
Log.d(TAG, "Setup finished.");
if (!result.isSuccess()) {
// Oh noes, there was a problem.
alert("Problem setting up in-app billing: " + result);
return;
}
// Have we been disposed of in the meantime? If so, quit.
if (mHelper == null) return;
// IAB is fully set up. Now, let's get an inventory of stuff we own.
Log.d(TAG, "Setup successful. Querying inventory.");
//mHelper.queryInventoryAsync(mGotInventoryListener);
}
});
//ILLEGALSTAEEXCEPTION THROWN HERE
mHelper.launchPurchaseFlow(this, testProduct, RC_REQUEST,
new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result, Purchase purchase)
{
if (result.isFailure()) {
Log.d(TAG, "Error purchasing: " + result);
return;
}
else if (purchase.getSku().equals(testProduct)) {
// give user access to premium content and update the UI
//set the purchaesd booean to true
//when purcajsed add this code
editor.putBoolean("purchased", true);
editor.commit();
Toast.makeText(getApplicationContext(), "ADD FREE VERSION PURCAHSED!!!" +
" Details OrderID: "+purchase.getOrderId() +" Payload ID: "+purchase.mDeveloperPayload, Toast.LENGTH_LONG).show();
Log.d("CONNECT TO GOOGLE BILL", "ITEM PURCAHSED! : "+purchased);
}
}
}, "diveAppPurchase");
}//onCreate
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.d(TAG, "onActivityResult(" + requestCode + "," + resultCode + "," + data);
if (mHelper == null) return;
// 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.");
}
}
This be useful to those like me, who are dealing with in app purchasing first time.
The set up call is asynchronous, therefore this must complete before the launchPurchaseFlow is called. So i disabled a 'Purchase' button to make the launchPurchaseFlow call, which is enabled once the set up call is done. Works fine:
Set up call
//perform service binding to Google Bill ser and return ny errors with IabResult object and listener
mHelper.startSetup(...)
if successfully then enable button and call mHelper.launchPurchaseFlow(...) method
Also, when using the test product id provided by google you may note a signature exception is thrown during the launchPurchaseFlow with a subsequent result failure although the transaction is successful , this is apparently a known bug that google are aware of
Related
I built and android game which has a premium in app purchase, and I had it working fine for a while. (The game was in a beta testing branch, and users could purchase the premium upgrade fine.)
A few days ago I decided to rewrite the project, so I could clean up my code a bit. Everything seemed to work fine. (Play Game Services and AdMob Ads still work) However, in app billing no longer works. Every time my app start I receive an error: "Billing service unavailable on device. (response: 3:Billing Unavailable)" I've searched around everywhere, and found plenty of people having this issue, but none of their answers helped me.
I've tried:
Clearing the cache of the Google Play app, and accepting the terms by starting the app.
Trying multiple users on my phone. (All of which were added to the testers list in the developer console.)
Testing on at least 3 other (non-rooted) devices.
Double checking that the "com.android.vending.BILLING" permission is in my manifest file.
Using the same keystore to sign my new application.
Using the same exact namespace for my new project.
Trying 3 different play store versions.
Here is my iabHelper code.
String base64EncodedPublicKey = "KEY HERE";
iabHelper = new IabHelper(this, base64EncodedPublicKey);
iabHelper.enableDebugLogging(true);
iabHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
#Override
public void onIabSetupFinished(IabResult result) {
if (!result.isSuccess()) {
Log.e(TAG, "Problem setting up in-app billing: " + result.getMessage());
return;
}
if (iabHelper == null) return;
iabHelper.queryInventoryAsync(new IabHelper.QueryInventoryFinishedListener() {
#Override
public void onQueryInventoryFinished(IabResult result, Inventory inv) {
if (iabHelper == null) return;
if (result.isFailure()) {
Log.e(TAG, "Failed to query inventory: " + result);
return;
}
Purchase premiumPurchase = inv.getPurchase("premium");
isPremium = (premiumPurchase != null);
if (!premium())
loadInterstitial();
}
});
}
});
and my onActivityResult code
I'm pretty sure this isn't done correctly, however I don't think it's relevant to this error. (This isn't even called before I see the error while debugging.)
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.i(TAG, "onActivityResult(" + requestCode + "," + resultCode + "," + data);
// Pass on the activity result to the helper for handling
if (!iabHelper.handleActivityResult(requestCode, resultCode, data)) {
if (resultCode == GamesActivityResultCodes.RESULT_RECONNECT_REQUIRED)
gameHelper.disconnect();
gameHelper.onActivityResult(requestCode, resultCode, data);
super.onActivityResult(requestCode, resultCode, data);
} else {
Log.i(TAG, "onActivityResult handled by IABUtil.");
}
}
I don't normally like having to ask for help, as the answer is normally accessible online somewhere. However, I've been at this for a few days now, and I've already ripped out all the hair I have. Thanks for any help you can offer.
My best guess at the moment is that I forgot to copy something from the original project, which is causing it to fail, but I can't think of anything besides the base64EncodedPublic key, my keystore, and the use of the same namespace. Am I missing something?
Have looked at this link :
onIabPurchaseFinished never called.
and ....
I have looked at the following link in so - which is the exact issue that I am facing :
IabHelper PurchaseFinishedListener
The issue stated by 'kevinl' in one of the answers in the above second link - is the issue I have i.e.
I have the 'onActivityResult' coded in my activity
The control does come into this method - but yet - it does not go to - onIabPurchaseFinished
I am invoking the Purchase flow as below:
public void onClickIap(View v) {
if(isAlreadyPurchased){
Log.d(TAG, "In onClickIap >>> The app is purchased ");
}else{
Log.d(TAG, "In onClickIap >>> The app is NOT purchased: ");
/** we need to check if this item was purchased from google store details **/
/** makes synchronous call -return control to callback method in listener **/
/** this is the heart of in app - where all the activity starts **/
mHelper.launchPurchaseFlow(this, ITEM_SKU_TEST_4, 10001,mPurchaseFinishedListener, "mypurchasetoken");
mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result, Purchase purchase){
Log.d(TAG, "In onIabPurchaseFinished ");
if (result.isFailure()) {
Log.d(TAG, "In onIabPurchaseFinished >>> purchase error ");
return;
}else if (purchase.getSku().equals(ITEM_SKU_TEST_4)) {
Log.d(TAG, "In onIabPurchaseFinished purchase success ");
/**TODO update the db **/
DbClass dbDbClass = new DbClass(getApplicationContext());
dbHelper.updatePurchasedQuiz(2);
/** set the flag **/
isAlreadyPurchased = true;
Log.d(TAG, "The app after all the shebang: ");
Intent intent = new Intent(getApplicationContext(),ExamActivity.class);
intent.putExtra("quizId", "2");
startActivity(intent);
}else{
Log.d(TAG, " ");
Log.d(TAG, "In onIabPurchaseFinished in a weird condition ");
}
}
};
}
}
My implementation of onActivityResult ( where I can see the debug logs )
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.d(TAG, " ");
Log.d(TAG, "<<<<<< in IAP ONACTIVITYRESULT >>>>>>");
Log.d(TAG, "onActivityResult " + requestCode + "," + resultCode + "," + data);
if (!mHelper.handleActivityResult(requestCode, resultCode, data)) {
super.onActivityResult(requestCode, resultCode, data);
}
}
Dont know if this would be the cause but the way I am invoking / initiatlizing the mHelper is :
1 In my activity onCreate - I start a thread ( AsyncTask )
2 In the method - doInBackground - I am initializing this helper :
mHelper = new IabHelper(ExamHomeActivity.this, base64EncodedPublicKey);
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() { .... }
3 Then when this async task completes - I show some buttons on the screen
4 Clicking on any button invokes the method described above - onClickIap
Thanks
shankar
You are passing the mPurchaseFinishedListener before you have initialized it (which happens right after you call launchPurchaseFlow).
Also, initializing the listener in the onClick is not a very good idea. You will be doing this every time the button is pressed. Take that out from the onClickIap() method.
You can do that statically in your Activity, just like it is done in Google's sample app Trivial Drive:
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result, Purchase purchase){
Log.d(TAG, "In onIabPurchaseFinished ");
...
...
...
}
};
Initializing the IabHelper is probably best to be done in the onCreate() of the activity:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_subscribe);
mHelper = new IabHelper(this);
}
Then do not forget to dispose of it in onDestory():
#Override
protected void onDestroy() {
super.onDestroy();
// very important:
if (mHelper != null) {
mHelper.dispose();
mHelper = null;
}
}
Also, there is no need to set up the IabHelper from a background thread. Just call mHelper.startSetup from your UI thread whenever you are ready.
I have implemented in-app purchased in my application and my product type is Managed and i am using API version 3.
When i make purchase from my credit card it is successfully done.
But the problem is if i uninstall my application and want to purchase this with same account it will charge me again?
According to Google rules of managed product type we only purchase the product once? But why is this happening ?
any one help me please?
here is my PurchaseActivity.java class
public abstract class PurchaseActivity extends BlundellActivity implements OnIabSetupFinishedListener, OnIabPurchaseFinishedListener {
private IabHelper billingHelper;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_purchase);
setResult(RESULT_CANCELED);
billingHelper = new IabHelper(this, AppProperties.BASE_64_KEY);
billingHelper.startSetup(this);
}
#Override
public void onIabSetupFinished(IabResult result) {
if (result.isSuccess()) {
Log.d("In-app Billing set up" + result);
dealWithIabSetupSuccess();
} else {
Log.d("Problem setting up In-app Billing: " + result);
dealWithIabSetupFailure();
}
}
protected abstract void dealWithIabSetupSuccess();
protected abstract void dealWithIabSetupFailure();
protected void purchaseItem(String sku) {
billingHelper.launchPurchaseFlow(this, sku, 123, this);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
billingHelper.handleActivityResult(requestCode, resultCode, data);
}
*/
#Override
public void onIabPurchaseFinished(IabResult result, Purchase info) {
if (result.isFailure()) {
dealWithPurchaseFailed(result);
} else if (pmg.SKU.equals(info.getSku())) {
dealWithPurchaseSuccess(result, info);
}
finish();
}
protected void dealWithPurchaseFailed(IabResult result) {
Log.d("Error purchasing: " + result);
}
protected void dealWithPurchaseSuccess(IabResult result, Purchase info) {
Log.d("Item purchased: " + result);
// DEBUG XXX
// We consume the item straight away so we can test multiple purchases
billingHelper.consumeAsync(info, null);
// END DEBUG
}
#Override
protected void onDestroy() {
disposeBillingHelper();
super.onDestroy();
}
private void disposeBillingHelper() {
if (billingHelper != null) {
billingHelper.dispose();
}
billingHelper = null;
}
}
This is working as intended - in your code you are consuming the in-app purchase immediately, which means you can then purchase it again:
protected void dealWithPurchaseSuccess(IabResult result, Purchase info) {
Log.d("Item purchased: " + result);
// DEBUG XXX
// We consume the item straight away so we can test multiple purchases
billingHelper.consumeAsync(info, null);
// END DEBUG
}
There's nothing that says you can't purchase a managed product more than once. What you can't do is purchase a managed product before a previous purchase of the same managed item has been consumed. So this is working exactly as intended, and if you remove that call to consumeAsync, you'll see that you can't purchase it again.
Sample use case:
Imagine some game where you can purchase extra lives. First, the user would purchase the extra lives (a managed in app product), your game (client or server) would then add those lives to the user's profile, for example, and assuming that was successful, you'd tell Google Play that the purchase has been consumed.
This is important in order to handle error cases - for example say the user's device dies in between the initial purchase and the addition of lives to the user's profile. Your app can then, the next time it's launched, try again to add those lives, and consume the purchase on success. And, obviously you wouldn't want the user trying to purchase even more lives before you successfully grant them - which is why you can't purchase a managed product twice before it's been consumed.
I am currently working on in app purchase in my application, when it launches I have always the error code -1003 querying owned items response signature verification failed when I arrive in IabHelper.QueryInventoryFinishedListener method.
I currently uses the example version of Google "Trivial Drive", I guess my signature is correct because I get to buy much when I use android.app.purchassed ...
The key seems correct to me because when I click like to buy a product that tells me that the publisher can not buy the product which is normal in itself (If I put anything I have another error saying the product does not exist). For cons, I have the same error when I put the test product "android.test.purchasse" then I should be able to test with it.
I made a purchase with android.test.purchasse there and I can not reset if you have already succeeded I'm interested.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// load game data
loadData();
String base64EncodedPublicKey = "MY_KEY_BASE64";
PublicKey key = Security.generatePublicKey(base64EncodedPublicKey);
// Create the helper, passing it our context and the public key to verify signatures with
Log.d(TAG, "Creating IAB helper.");
mHelper = new IabHelper(this, base64EncodedPublicKey);
// 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.
Log.d(TAG, "Starting setup.");
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;
// IAB is fully set up. Now, let's get an inventory of stuff we own.
Log.d(TAG, "Setup successful. Querying inventory.");
mHelper.queryInventoryAsync(mGotInventoryListener);
}
});
}
// 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(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("Failed to query inventory: " + result);
return;
}
Log.d(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().
*/
if(inventory.hasPurchase(SKU_GAS))
{
Toast.makeText(getApplicationContext(),"PREMIUM",Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(getApplicationContext(),"NOT PREMIUM", Toast.LENGTH_SHORT).show();
}
// 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"));
// Do we have the infinite gas plan?
Purchase infiniteGasPurchase = inventory.getPurchase(SKU_INFINITE_GAS);
mSubscribedToInfiniteGas = (infiniteGasPurchase != null && verifyDeveloperPayload(infiniteGasPurchase));
Log.d(TAG, "User " + (mSubscribedToInfiniteGas ? "HAS" : "DOES NOT HAVE")
+ " infinite gas subscription.");
if (mSubscribedToInfiniteGas) mTank = TANK_MAX;
// Check for gas delivery -- if we own gas, we should fill up the tank immediately
Purchase gasPurchase = inventory.getPurchase(SKU_GAS);
if (gasPurchase != null && verifyDeveloperPayload(gasPurchase)) {
Log.d(TAG, "We have gas. Consuming it.");
mHelper.consumeAsync(inventory.getPurchase(SKU_GAS), mConsumeFinishedListener);
return;
}
updateUi();
setWaitScreen(false);
Log.d(TAG, "Initial inventory query finished; enabling main UI.");
}
};
Have you ever been concerned? I see no way out for this bug help from you will be appreciated.
thank you
You need to create a test account, and put it in the playstore admin page.
I am currently trying to configure my app for in app purchase, but it just won't work. Every time I try to start the purchase flow it says "This version of the applications is not configured for billing through Google Play". I am quite sure that I walked through al necessary steps to make it work. I also tested SKU-Id "android.test.purchased", which works fine.
base64EncodedPublicKey is def. correct and exactly teh same like the code from the developer console
I did not forget <uses-permission android:name="com.android.vending.BILLING" /> in my manifest
I have applied the google account that I am using on my testing device as a tester account in the developer console
I am using the exactly same APK on my device an in the developer console. I installed it on my device via adb -d install
So any ideas what I could have done wrong?
This is my code:
(onCreate)
base64EncodedPublicKey
iabHelper = new IabHelper(this, base64EncodedPublicKey);
iabHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
#Override
public void onIabSetupFinished(IabResult result) {
if (!result.isSuccess()) {
// Oh noes, there was a problem.
Log.d(PURCHASE_TAG, "Problem setting up In-app Billing: " + result);
} else if (result.isSuccess()){
// Hooray, IAB is fully set up!
Log.d(PURCHASE_TAG, "Setup completed: " + result);
iabHelper.queryInventoryAsync(true, null, queryFinishedListener);
}
}
});
(onActivityResult)
if (iabHelper.handleActivityResult(requestCode, resultCode, data)) {
Log.d("TAG", "onActivityResult handled by IABUtil.");
return;
}
(Button click for starting burchase flow)
if(isPremium){
saveImageToGallery(imageState.image);
} else if (iabHelper != null) {
iabHelper.flagEndAsync();
purchaseItem(SKU_TEST);
(purchaseItem())
private void purchaseItem(String sku) {
iabHelper.launchPurchaseFlow(this, sku, 10001,
purchaseFinishedListener);
}
(listeners)
IabHelper.QueryInventoryFinishedListener
queryFinishedListener = new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result, Inventory inventory)
{
if (result.isFailure()) {
// handle error
return;
} else if (result.isSuccess()){
Log.d("$$$$$$$$$$$$$$$", "" + result);
}
}
};
IabHelper.OnIabPurchaseFinishedListener purchaseFinishedListener
= new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result, Purchase purchase)
{
if (result.isFailure()) {
Log.d("ERROR_TAG", "Error purchasing: " + result);
return;
}
else if (purchase.getSku().equals(SKU_TEST)) {
// give user access to premium content and update the UI
isPremium = purchase.getSku().equals(SKU_TEST);
}
}
};
I think you should check the key store you signed your apk. You must use same key store for apk that has uploaded to Google Play and test.
I hade the same problem, I fixed it by:
Go to play store developer console
Settings > Manage Testers, then add your another test email, not the one you use in publishing apps.
Again under Developer Console, Account Details (scroll down) > License Testing and add the emails of your users who will test your
app.
On your device, login in play store with one of the test emails 3 you allowed.
Launch you app and make in app purchase. There should be fixed.
more https://developer.android.com/google/play/billing/billing_testing.html
Summary: