I've searched around for hours trying to figure this out. Here's what I have done so far.
(Note: I'm developing in Android Studio)
Generated a signed APK and uploaded to my developer console
Made an in-app product and activated it
Added the Billing permission to my manifest
Extensively combed Stack to try and find similar problems.
Basically in logcat I see that IABHelper starts setup, but never completes at any time. (the listener never gets a callback)
private static final String TAG = "Preference Activity";
private static final String SKU_PRO = "desk.clock.pro.license";
static final int RC_REQUEST = 10001;
IabHelper mHelper;
private boolean mIsPremium;
private ArrayList<Preference> proSettings;
IInAppBillingService mService;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
bindService(new
Intent("com.android.vending.billing.InAppBillingService.BIND"),
mServiceConn, Context.BIND_AUTO_CREATE);
proSettings = new ArrayList<Preference>();
ActionBar b = getActionBar();
b.setDisplayHomeAsUpEnabled(true);
colorListener();
textureListener();
bgColorListener();
onPresetListener();
gradListener();
String base64Key = "[my key from dev console]";
bindService(new
Intent("com.android.vending.billing.InAppBillingService.BIND"),
mServiceConn, Context.BIND_AUTO_CREATE);
mHelper = new IabHelper(this, base64Key);
mHelper.enableDebugLogging(true);
Log.d(TAG, "Starting setup");
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
if (!result.isSuccess()) {
// Oh noes, there was a problem.
Log.d(TAG, "Problem setting up In-app Billing: " + result);
}
Log.d(TAG, "Setting up success");
Log.d(TAG, "querying inventory");
mHelper.queryInventoryAsync(mGotInventoryListener);
Log.d(TAG, "queried");
}
});
IabHelper.QueryInventoryFinishedListener mGotInventoryListener
= new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result,
Inventory inventory) {
if (result.isFailure()) {
// handle error here
mIsPremium = false;
disableProAndRevertSettings();
}
else {
// does the user have the premium upgrade?
mIsPremium = inventory.hasPurchase(SKU_PRO);
if(mIsPremium) {
enableProSettings();
}else {
disableProAndRevertSettings();
}
}
}
};
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);
}
};
//rest of my activity
I get this line in my logcat but never anything after this
10-06 15:46:44.485 20787-20787/com.ssa.digitaldeskclock D/IabHelper﹕ Starting in-app billing setup.
Problem was fixed by obtaining a new version of all of the util classes for the IAB v3 sample. I added all the new files to my util directory and it went flawlessly. Hopefully this can help someone else out there. See this link for the source
https://code.google.com/p/marketbilling/source/browse/v3/src/com/example/android/trivialdrivesample/util/IabHelper.java?r=5f6b7abfd0534acd5bfc7c14436f4500c99e0358
Related
I want to include in-app purchases to my app, but I cannot bind my activity to the in-app billing service.
I have already done all the steps mentioned in the page https://developer.android.com/training/in-app-billing/preparing-iab-app.html
Debugging in a physical device I found that the issue is in the next command of the IabHelper class:
mContext.bindService(serviceIntent, mServiceConn, Context.BIND_AUTO_CREATE);
I noticed that it is not working because the program does not stop at either of the two methods of the ServiceConnection instance, that is to say, it does not stop at onServiceConnected() nor onServiceDisconnected()
I made a test using the same command directly in my Activity and the bind with the in-app billing service was successful.
So, the bind is working if it is requested from the Activity but it is not working when it is requested from the IabHelper class.
My question is, how can I bind my activity to the billing service from the IabHelper class?
Here is the code to call the startSetup method from IabHelper:
mHelper = new IabHelper(this, publicKey);
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener()
{
#Override
public void onIabSetupFinished(IabResult result)
{
if(!result.isSuccess())
{
mHelper=null;
return;
}
if (mHelper == null) return;
}
});
This is the code within IabHelper class to bind to service:
Intent serviceIntent = new Intent("com.android.vending.billing.InAppBillingService.BIND");
serviceIntent.setPackage("com.android.vending");
List<ResolveInfo> intentServices = mContext.getPackageManager().queryIntentServices(serviceIntent, 0);
if (intentServices != null && !intentServices.isEmpty())
{
// service available to handle that Intent
mContext.bindService(serviceIntent, mServiceConn, Context.BIND_AUTO_CREATE);
}
And here is the code that I used to bind to the service directly from the activity:
private IInAppBillingService mService;
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);
}
};
Intent serviceIntent = new Intent("com.android.vending.billing.InAppBillingService.BIND");
serviceIntent.setPackage("com.android.vending");
this.bindService(serviceIntent, mServiceConn, Context.BIND_AUTO_CREATE);
Thanks in advance for your help
I just realize my mistake, I was calling the queryInventoryAsync method before the startSetup get finished.
This was my mistake:
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener()
{
#Override
public void onIabSetupFinished(IabResult result)
{
if(!result.isSuccess())
{
mHelper=null;
return;
}
}
});
try {mHlpr.queryInventoryAsync(true, itemList, mQueryListener);}
catch (IabHelper.IabAsyncInProgressException e) {e.printStackTrace();}
To correct, I changed the code to:
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener()
{
#Override
public void onIabSetupFinished(IabResult result)
{
if(!result.isSuccess())
{
mHelper=null;
return;
}
else if(result.isSuccess())
{
try {mHlpr.queryInventoryAsync(true, itemList, mQueryListener);}
catch (IabHelper.IabAsyncInProgressException e) {e.printStackTrace();}
}
}
});
When I tested the bind directly from the activity, I had removed the command for the query, this is the reason it worked from the activity; when I was testing from the IabHelper class I was calling the query command
I am setting up in-app purchases for my application and am stuck at a particular point. The code I have used so far is as follows:
IabHelper mHelper;
protected void onCreate(Bundle savedInstanceState) {
mHelper = new IabHelper(this, base64EncodedPublicKey); //base64EncodedPublicKey is a string declared earlier and not reposted here.
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
if (!result.isSuccess()) {
Log.d(TAG, "Failed: " + result);
Toast.makeText(MainActivity.this, "IAB setup Failed", Toast.LENGTH_SHORT).show();
} else {
Log.d(TAG, "Worked");
Toast.makeText(MainActivity.this, "IAB setup Successful", Toast.LENGTH_SHORT).show();
final List additionalSkuList = new ArrayList();
additionalSkuList.add("remove_ad");
mHelper.queryInventoryAsync(true, additionalSkuList, mQueryFinishedListener);
}
}
});
At this point, everything seems to be going well. The "Worked" section of code triggers and is processed successfully. The code sends a request at this point to mHelper.queryInventoryAsync, which is configured as follows, outside of onCreate():
IabHelper.QueryInventoryFinishedListener mQueryFinishedListener = new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result, Inventory inventory)
{
if (result.isFailure()) {
Toast.makeText(MainActivity.this, "Query Listener Error", Toast.LENGTH_SHORT).show();
return;
}
String removalPrice =
inventory.getSkuDetails("remove_ad").getPrice();
Toast.makeText(MainActivity.this, removalPrice, Toast.LENGTH_SHORT).show();
// update the UI
}
};
At this point, Toast triggers to say "Query Listener Error", indicating that if (result.isFailure()) has triggered. This is where I am stuck. It is not giving me any clues as to why this might be happening.
From the Developer Console, these are the details of my In-App product:
Name/ID: Remove Ad (remove_ad)
Type: Managed product
Last Update: Jul 15, 2015
Status: Active
What have I done incorrectly? The only thing I am not too sure about is how I have declared and used my arrayList, and where I have submitted a string value to .getPrice();
mHelper.launchPurchaseFlow(Activity.this, SKU, 11,
mPurchaseFinishedListener, "mypurchasetoken");
Call this method and implement listner like below in that you will get result
public IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result,
Purchase purchase)
{
if (result.isFailure()) {
}
return;
}
};
Refer the below link:
http://www.techotopia.com/index.php/An_Android_Studio_Google_Play_In-app_Billing_Tutorial
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 am developing a app in android using in app subscription. i am trying to query my purchase items using IabHelper.QueryInventoryFinishedListener. but it always coming as a failure results. IabResult returns failure. i added in app products in developer console. can any one help me on this?
here is some of my code,
bindService(new Intent("com.android.vending.billing.InAppBillingService.BIND"),
mServiceConn, Context.BIND_AUTO_CREATE);
String base64EncodedPublicKey = "my key";
mHelper = new IabHelper(this, base64EncodedPublicKey);
mHelper.enableDebugLogging(true);
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
if (!result.isSuccess()) {
System.out.println("Not Success");
Log.d("In APP Billing", "Problem setting up In-app Billing: " + result);
return;
} else {
System.out.println("Success");
Log.d(" In APP Billing", "Setting up In-app Billing Success: " + result);
}
List<String> additionalSkuList = new ArrayList<String>();
additionalSkuList.add(SKU_ID);
mHelper.queryInventoryAsync(true, additionalSkuList,
mQueryFinishedListener);
}
});
IabHelper.QueryInventoryFinishedListener mQueryFinishedListener = new IabHelper.QueryInventoryFinishedListener() {
#Override
public void onQueryInventoryFinished(IabResult result, Inventory inv) {
// TODO Auto-generated method stub
if (result.isFailure()) {
// handle error
System.out.println("mQueryFinishedListener is Failure"); // i am always getting this
return;
}
System.out.println("mQueryFinishedListener is Success");
Boolean hasPur = inv.hasPurchase(SKU_ID);
if (hasPur) {
System.out.println("Query - - subscribed ");
isSubscribed = true;
} else {
System.out.println("Query - not subscribed ");
isSubscribed = false;
}
System.out.println("Purchase panic:"+inv.getPurchase(SKU_ID));
}
};
any idea why its not working? thanks in advance.
To work with in app subscription i think you will gave to call "launchSubscriptionPurchaseFlow()" method on IabHelper instance you create.
mHelper.launchSubscriptionPurchaseFlow(Activity act, String sku, int requestCode,
OnIabPurchaseFinishedListener listener, String extraData);
OR
mHelper.launchPurchaseFlow(Activity act, String sku, String itemType, int requestCode,
OnIabPurchaseFinishedListener listener, String extraData);
Where itemType = IabHelper.ITEM_TYPE_SUBS
I've wrote an in app purchase test app to learn how to implement it in an application i've made.
I adapted the code from the TrivialDrive sample provided by google.
But it doesn't work, after my friend makes the payment the app crashes.
The code looks like this
public class MainActivity extends Activity {
String TAG = "AppPurchaseTest";
IabHelper mHelper;
boolean mIsPremium = false;
static final String SKU_PREMIUM = "premium";
static final int RC_REQUEST = 10001;
// User clicked the "Upgrade to Premium" button.
public void onUpgradeAppButtonClicked(View arg0) {
Log.d(TAG, "Upgrade button clicked; launching purchase flow for upgrade.");
// setWaitScreen(true);
mHelper.launchPurchaseFlow(this, SKU_PREMIUM, RC_REQUEST, mPurchaseFinishedListener);
}
//this is not working
// 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);
int duration = Toast.LENGTH_SHORT;
if (result.isFailure()) {
// Oh noes!
// complain("Error purchasing: " + result);
// setWaitScreen(false);
Toast.makeText(getBaseContext(), "Fail :(", duration).show();
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("Thank you for upgrading to premium!");
mIsPremium = true;
Toast.makeText(getBaseContext(), "Successo: adesso sei premium", duration).show();
Button test = (Button) findViewById(R.id.test);
test.setVisibility(View.INVISIBLE);
// updateUi();
// setWaitScreen(false);
}
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String base64EncodedPublicKey = null;
// compute your public key and store it in base64EncodedPublicKey
mHelper = new IabHelper(this, base64EncodedPublicKey);
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
if (!result.isSuccess()) {
// Oh noes, there was a problem.
Log.d(TAG, "Problem setting up In-app Billing: " + result);
}
// Hooray, IAB is fully set up!
}
});
}
#Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
if (mHelper != null) {
Log.d(TAG, "mHelper doesn't = null ");
mHelper.dispose();
mHelper = null;
}
}
#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 boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
}
Can you spot something wrong? Something i forgot?
Also this tutorial https://developer.android.com/google/play/billing/billing_integrate.html
looks much simpler, but i don't understand how to implement it, is there a sample or something from which i can see how it's implemented? I just need a simple upgrade to premium purchase
It's very hard to make it work since i can't test it personally and everytime i test it i lose money :(
Took a long time to figure out on my project but you have to understand that your mPurchaseFinishedListener is called on a non rendering thread and before your application onResume() is called (just check the IabHelper code.
So if you try to do any rendering there it CAN crash because the rendering context is not restored yet.
In your case it is likely that:
Toast.makeText(getBaseContext(), "Successo: adesso sei premium", duration).show();
will crash, the same goes for
Button test = (Button) findViewById(R.id.test);
if you check the trivia example the textButton visibility is set to true, but the button itself is a class property that is assigned in the onCreate() method.
Let me know if this helps..
Per TrivialDrive You also need (this will fix it):
// We're being destroyed. It's important to dispose of the helper here!
#Override
public void onDestroy() {
super.onDestroy();
// very important:
if (mBroadcastReceiver != null) {
unregisterReceiver(mBroadcastReceiver);
}
// very important:
Log.d(TAG, "Destroying helper.");
if (mHelper != null) {
mHelper.disposeWhenFinished();
mHelper = null;
}
}