I'm having an issue with my apps in-app billing. I thought it was working well for the last week, but I'm having an unexpected result.
I have about 10 items for sale. Each item sets a shared pref value to true when purchased/querying inventory if it is bought. One item, is a "Buy All" button, when bought, it's suppose to set the values for all the others to true. This was working well, the problem arises when I add new items to buy. The "buy all" should give access to those as well, but it seems it's not.
I'll try to make my code as simple as possible while still showing the needed info:
BaseActivity.java(where all in-app purchases set up):
//SKU FOR our products
static final String SKU_W31 = "workout_31";
static final String SKU_W32 = "workout_32";
static final String SKU_W37 = "workout_37";
static final String SKU_ALL = "all_workouts";
//is paid?
public boolean m31paid = false;
public boolean m32paid = false;
public boolean m37paid = false;
public boolean mAllPaid = false;
IabHelper mHelper;
IabBroadcastReceiver mBroadcastReceiver;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedPreferences pref = getApplicationContext().getSharedPreferences("MyPref", 0);
mHelper = new IabHelper(this, base64EncodedPublicKey);
//SET FALSE FOR LIVE APP
mHelper.enableDebugLogging(false);
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
if (!result.isSuccess()) {
Log.d(LOG, "Problem setting up in app billing: " + result);
} else Log.d(LOG, "set up correctly!");
if (mHelper == null) return;
mBroadcastReceiver = new IabBroadcastReceiver(BaseActivity.this);
IntentFilter broadcastFilter = new IntentFilter(IabBroadcastReceiver.ACTION);
registerReceiver(mBroadcastReceiver, broadcastFilter);
// IAB is fully set up. Now, let's get an inventory of stuff we own.
Log.d(LOG, "Setup successful. Querying inventory.");
try {
mHelper.queryInventoryAsync(mGotInventoryListener);
} catch (IabHelper.IabAsyncInProgressException e) {
complain("Error querying inventory. Another async operation in progress.");
}
}
});
}
IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
Log.d(LOG, "Query inventory finished.");
SharedPreferences.Editor editor = getSharedPreferences("my_pref", MODE_PRIVATE).edit();
// Have we been disposed of in the meantime? If so, quit.
if (mHelper == null) return;
Purchase w37Purchase = inventory.getPurchase(SKU_W37);
m37paid = (w37Purchase != null && verifyDeveloperPayload(w37Purchase));
Log.d(LOG, "User has workout 37" + (m37paid ? "BOUGHT" : "NOT BOUGHT"));
if (w37Purchase != null) {
editor.putBoolean("workout37", true);
editor.apply();
}
Purchase w32Purchase = inventory.getPurchase(SKU_W32);
m32paid = (w32Purchase != null && verifyDeveloperPayload(w32Purchase));
Log.d(LOG, "User has workout 32" + (m32paid ? "BOUGHT" : "NOT BOUGHT"));
if (w32Purchase != null) {
editor.putBoolean("workout32", true);
editor.apply();
}
Purchase w31Purchase = inventory.getPurchase(SKU_W31);
m31paid = (w31Purchase != null && verifyDeveloperPayload(w31Purchase));
Log.d(LOG, "User has workout 31" + (m31paid ? "BOUGHT" : "NOT BOUGHT"));
if (w31Purchase != null) {
editor.putBoolean("workout31", true);
editor.apply();
}
Purchase wAllPurchase = inventory.getPurchase(SKU_ALL);
mAllPaid = (wAllPurchase != null && verifyDeveloperPayload(wAllPurchase));
Log.d(LOG, "User has " + (mAllPaid ? "BOUGHT" : "NOT BOUGHT"));
if (wAllPurchase != null) {
editor.putBoolean("workout31", true);
editor.putBoolean("workout32", true);
editor.putBoolean("workout37", true);
editor.apply();
}
}};
I then have the methods for buying that I put in the onClick of the corresponding buttons:
public void onBuy31ButtonClicked (View arg0) {
Log.d(LOG, "Buy 31 button clicked.");
if (m31paid) {
Toast.makeText(getApplicationContext(), R.string.already_bought, Toast.LENGTH_LONG).show();
return;
}
Log.d(LOG, "launching purchase for 31");
String payload = "";
try {
mHelper.launchPurchaseFlow(this, SKU_W31, RC_REQUEST, mPurchaseFinishedListener, payload);
} catch (IabHelper.IabAsyncInProgressException e) {
Toast.makeText(getApplicationContext(), R.string.purchase_error, Toast.LENGTH_LONG).show();
}
}
public void onBuy32ButtonClicked (View arg0) {
Log.d(LOG, "Buy 32 button clicked.");
if (m32paid) {
Toast.makeText(getApplicationContext(), R.string.already_bought, Toast.LENGTH_LONG).show();
return;
}
Log.d(LOG, "launching purchase for 32");
String payload = "";
try {
mHelper.launchPurchaseFlow(this, SKU_W32, RC_REQUEST, mPurchaseFinishedListener, payload);
} catch (IabHelper.IabAsyncInProgressException e) {
Toast.makeText(getApplicationContext(), R.string.purchase_error, Toast.LENGTH_LONG).show();
}
}
public void onBuy37ButtonClicked (View arg0) {
Log.d(LOG, "Buy 37 button clicked.");
if (m37paid) {
Toast.makeText(getApplicationContext(), R.string.already_bought, Toast.LENGTH_LONG).show();
return;
}
Log.d(LOG, "launching purchase for 37");
String payload = "";
try {
mHelper.launchPurchaseFlow(this, SKU_W37, RC_REQUEST, mPurchaseFinishedListener, payload);
} catch (IabHelper.IabAsyncInProgressException e) {
Toast.makeText(getApplicationContext(), R.string.purchase_error, Toast.LENGTH_LONG).show();
}
}
public void onBuyAllButtonClicked (View arg0) {
Log.d(LOG, "Buy all button clicked.");
if (m32paid) {
Toast.makeText(getApplicationContext(), R.string.already_bought, Toast.LENGTH_LONG).show();
return;
}
Log.d(LOG, "launching purchase for all");
String payload = "";
try {
mHelper.launchPurchaseFlow(this, SKU_ALL, RC_REQUEST, mPurchaseFinishedListener, payload);
} catch (IabHelper.IabAsyncInProgressException e) {
Toast.makeText(getApplicationContext(), R.string.purchase_error, Toast.LENGTH_LONG).show();
}
}
My mPurchaseFinishedListener:
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
Log.d(LOG, "Purchase finished: " + result + ", purchase: " + purchase);
SharedPreferences.Editor editor = getSharedPreferences("my_pref", MODE_PRIVATE).edit();
// if we were disposed of in the meantime, quit.
if (mHelper == null) return;
if (result.isFailure()) {
complain("Error purchasing: " + result);
return;
}
if (purchase.getSku().equals(SKU_W30)) {
editor.putBoolean("workout30", true);
editor.apply();
return;
}
if (purchase.getSku().equals(SKU_W31)) {
editor.putBoolean("workout31", true);
editor.apply();
return;
}
if (purchase.getSku().equals(SKU_W32)) {
editor.putBoolean("workout32", true);
editor.apply();
return;
}
if (purchase.getSku().equals(SKU_W37)) {
editor.putBoolean("workout37", true);
editor.apply();
return;
if(purchase.getSku().equals(SKU_ALL)) {
editor.putBoolean("workout31", true);
editor.putBoolean("workout32", true);
editor.putBoolean("workout37", true);
editor.apply();
return;
}
Then where the data is, I simply have an if statement to check the boolean value as such:
xpandableListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
SharedPreferences pref = getApplicationContext().getSharedPreferences("my_pref", MODE_PRIVATE);
boolean w31 = pref.getBoolean("workout31", false);
boolean w32 = pref.getBoolean("workout32", false);
boolean w37 = pref.getBoolean("workout37", false);
if (groupPosition == 2) {
if(w31 == true) {
if (childPosition == 0) {
Intent intent = new Intent(getApplicationContext(), WorkoutDaysActivity.class);
intent.putExtra("workout", "w31w1");
startActivity(intent);
}
if (childPosition == 1) {
Intent intent = new Intent(getApplicationContext(), WorkoutDaysActivity.class);
intent.putExtra("workout", "w31w2");
startActivity(intent);
}
}else Toast.makeText(getApplicationContext(), "Sorry, but you need to purchase these workouts from the menu.", Toast.LENGTH_LONG).show();
}
All child items have the same code as above, switching out the w31 with w32 and w37.
I have taken out most purchases to try to cut back on code and still get the point across, but basically what is happening is 31 and 32 were added before, and then I used the purchase all button, they work. But I added 37 in a later update, and my theory was that the boolean value would change for it when it queries for all workouts and see it was purchased. In reality though, when I click 37 in the expandable list view, I get the toast saying it needs to be purchased, and when I go to the purchase page and click "Purchase All", I get the toast saying it was already purchased.
Does anyone see anything wrong with my code? Thanks a lot, this is causing huge problems!
Cause of async operations you may have a race condition. Your activity with expandable list view should be registered to all changes in your SharedPreferences.
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.
I get this complaint every so often that a user buys my premium features with IAP and that it works fine on the device they bought but their other device won't get the features, I ask them to reboot the other device and that usually fixes it. So the other day I tried to see if I could do something about this so I purchased on one of my devices (test account) and then I went to the other, killed my app to make sure it was starting from zero, and then stepped the code. The query of purchases was actually returning no purchases. It did not tell me about purchases until I tried to buy premium from that device. Trying to buy premium from that device didn't create a dialog or anything, it just returned as if nothing had happened but after that the device had that purchase on its inventory.
Here are the methods I have, I am using the IABHelper from Google's example and I think I updated it just a month ago.
public void createIABHelper(final Context ac) {
mHelper = new IabHelper(ac, myid);
try {
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);
} else {
iabSetup = true;
//queryIAB(ac);
queryIABPurchases(ac);
}
}
});
} catch (Throwable e) {
final String desc = "Error starting IABHelper";
Log.w(TAG, desc, e);
}
}
public void queryIAB(final Context ctx) {
List<String> additionalSkuList = new ArrayList<String>();
additionalSkuList.add(Constants.PREMIUM_UPGRADE);
IabHelper.QueryInventoryFinishedListener
mQueryFinishedListener = new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
if (result.isFailure()) {
// handle error
return;
} else {
Purchase p = inventory.getPurchase(Constants.PREMIUM_UPGRADE);
if (p != null) {
setHasPremiumPref(p.getPurchaseState() == 0);
} else {
setHasPremiumPref(false);
}
}
// inventory.hasDetails(PREMIUM_UPGRADE);
// update the UI
}
};
mHelper.queryInventoryAsync(true, additionalSkuList,
mQueryFinishedListener);
}
#Override
public void purchaseIAB(final Activity ac) {
if (iabSetup) {
try {
final String finalPayload = ....;
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener
= 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(Constants.PREMIUM_UPGRADE)) {
Log.w(TAG, "Purchased " + purchase.getPurchaseState());
if (purchase.getDeveloperPayload().equals(finalPayload) && purchase.getPurchaseState() == 0) {
setHasPremiumPref(true);
} else {
Log.w(TAG, "Something went wrong verifying purchase.");
setHasPremiumPref(false);
}
}
}
};
mHelper.launchPurchaseFlow(ac, Constants.PREMIUM_UPGRADE, 10001,
mPurchaseFinishedListener, finalPayload);
} catch (Throwable ex) {
final String msg = "Error starting in-app purchase";
Log.w(TAG, msg, ex);
}
} else {
Log.w(TAG, "IAB not setup.");
}
}
public void queryIABPurchases(Context ctx) {
if (iabSetup) {
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?
Purchase p = inventory.getPurchase(Constants.PREMIUM_UPGRADE);
if (p != null) {
setHasPremiumPref(p.getPurchaseState() == 0);
} else {
setHasPremiumPref(false);
}
// update UI accordingly
}
}
};
List<String> additionalSkuList = new ArrayList<String>();
additionalSkuList.add(Constants.PREMIUM_UPGRADE);
mHelper.queryInventoryAsync(false, additionalSkuList, mGotInventoryListener);
} else {
Log.w(TAG, "IAB not setup.");
}
}
Anyways, basically I use queryIABPurchases but p is always null on that second device. Is this an issue with test accounts or maybe something to do with time, do I maybe need to wait a few hours before testing on the second device?
Thanks.
i am using in-app purchase for my quiz app to buy coins.And i have added 2 coins category 100 coins and 500 coins respectively.I have also added 2 SKU.My both coin purchase works well.While i purchase 100 coins and consume.100 coins gets added to app but when i purchase 500 coins once again 100 coins get added and not 500.I really need a helping hand as i am stuck with this for weeks.Here is the consume app purchase code
private void update() {
ArrayList<String> moreSkus = new ArrayList<String>();
moreSkus.add(SKU);
moreSkus.add(SKU_500);
buyHelper.queryInventoryAsync(true, moreSkus, new QueryInventoryFinishedListener() {
#Override
public void onQueryInventoryFinished(IabResult result, Inventory inv) {
if(result.isSuccess()) {
// SkuDetails details = inv.getSkuDetails(SKU);
//String price = details.getPrice();
//TextView tvPrice = (TextView)GameActivity.this.findViewById(R.id.textview_price);
// tvPrice.setText(price);
purchase = inv.getPurchase(SKU);
// purchase = inv.getPurchase(SKU_500);
purchase=inv.getPurchase(SKU_500);
if(purchase!=null) {
// buy100coins.setEnabled(false);
//coins_one_hundred.setVisibility(View.GONE);
// buy100coins.setVisibility(View.GONE);
buy100coins.setEnabled(false);
buy500coins.setEnabled(false);
//boughtcoins.setEnabled(true);
//boughtcoins.setVisibility(View.VISIBLE);
boughtcoins.setEnabled(true);
} else {
// buy100coins.setEnabled(true);
//coins_one_hundred.setVisibility(View.VISIBLE);
// buy100coins.setVisibility(View.VISIBLE);
buy100coins.setEnabled(true);
buy500coins.setEnabled(true);
//boughtcoins.setEnabled(false);
//boughtcoins.setVisibility(View.INVISIBLE);
boughtcoins.setEnabled(false);
}
Toast.makeText(GameActivity.this, "Successful got inventory!", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(GameActivity.this, "Error getting inventory!", Toast.LENGTH_SHORT).show();
}
}
});
}
and button to consume
boughtcoins = (Button) buycoinsdialog.findViewById(R.id.bought_coins);
//boughtcoins.setVisibility(View.GONE);
// if button is clicked, close the custom dialog
boughtcoins.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Helper.playSound(getApplicationContext(), "click");
buyHelper.consumeAsync(purchase, new OnConsumeFinishedListener() {
#Override
public void onConsumeFinished(Purchase purchase, IabResult result) {
if(result.isSuccess()) {
Toast.makeText(GameActivity.this, "Coins consumed!", Toast.LENGTH_SHORT).show();
Helper.playSound(getApplicationContext(), "cash");
money += 100;
money_text.setText( "$" + money);
try {
// Small HACK: Give the system some time to realize the consume... without the sleep here,
// you have to press "Update" to see that the item can be bought again...
Thread.sleep(600);
update();
} catch(Exception e) {
// ignored
}
} else {
Toast.makeText(GameActivity.this, "Error consuming: "+result.getMessage(), Toast.LENGTH_SHORT).show();
}
}
});
buycoinsdialog.dismiss();
}
});
It looks like regardless of what the user selects your increasing the money/coin count by 100 as per this line:
money += 100;
Where are you handling the 500 coin purchase?
you have to do as follow
if(purchase.getSKU() == SKU_500){
money += 500;
}else if(purchase.getSKU() == SKU_100){
money += 100;
}
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener2
= new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result,
Purchase purchase)
{
if (result.isFailure()) {
// Handle error
return;
}else {
if (purchase.getSku().equals(ITEM_SKU1)) {
// do things
}else if(purchase.getSku().equals(ITEM_SKU2){}
}
}
};
check this it may help you
// on clicking your button call this method
if (mHelper!=null) mHelper.flagEndAsync();
mHelper.launchPurchaseFlow(this, ITEM_SKU, 10001,
mPurchaseFinishedListener, "");
} catch (Exception e) {
Toast.makeText(getApplicationContext(), "Please try after a few seconds"+e, Toast.LENGTH_SHORT).show();
implement listener as folloew:
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener
= new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result,
Purchase purchase)
{
if (result.isFailure()) {
// Handle error
return;
}
else{
mHelper.consumeAsync(purchase, mConsumeFinishedListener);
}
}
};
// implement listener for "mConsumeFinishedListener" as follows:
IabHelper.OnConsumeFinishedListener mConsumeFinishedListener =
new IabHelper.OnConsumeFinishedListener()
{
public void onConsumeFinished(Purchase purchase,
IabResult result)
{
if (result.isSuccess())
{
if(purchase.getSKU().equals("android.test.purchase")){
}else if(purchase.getSKU().equals("android.test.cancel")){
}
}
}
};
I got a code from internet for inapp billing and I want to use that code in my application but I am getting an error that when I click the the buy button of my app it redirect me to the another layout of the code where I get an another Button and after that click my in-app billing starts.
I want that when I click my buy button then the in-app billing should start. without any another button clicks.
This is the code from where the the in-app billing start.
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mSP = PreferenceManager.getDefaultSharedPreferences(this);
Log.i("BillingService", "Starting");
setContentView(R.layout.contact_market);
mContext = this;
mPurchaseButton = (Button) findViewById(R.id.main_purchase_yes);
mPurchaseButton.setOnClickListener(this);
mPreview = (TextView) findViewById(R.id.chakkde);
startService(new Intent(mContext, BillingService.class));
BillingHelper.setCompletedHandler(mTransactionHandler);
}
public Handler mTransactionHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
Log.i(TAG, "Transaction complete");
Log.i(TAG, "Transaction status: "
+ BillingHelper.latestPurchase.purchaseState);
Log.i(TAG, "Item purchased is: "
+ BillingHelper.latestPurchase.productId);
if (BillingHelper.latestPurchase.isPurchased()) {
showItem();
}
};
};
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.main_purchase_yes:
if (BillingHelper.isBillingSupported()) {
BillingHelper.requestPurchase(mContext,
"android.test.purchased");
} else {
Log.i(TAG, "Can't purchase on this device");
mPurchaseButton.setEnabled(false);
}
break;
default:
Log.i(TAG, "default. ID: " + v.getId());
break;
}
}
private void showItem() {
mPreview.setVisibility(View.VISIBLE);
SharedPreferences.Editor prefEditor = mSP.edit();
prefEditor.putBoolean(DroidSugarPreference.KEY_ENABLE,
true);
prefEditor.commit();
startActivity(new Intent(InAppMain.this, Setup.class));
InAppMain.this.finish();
}
#Override
protected void onPause() {
Log.i(TAG, "onPause())");
super.onPause();
}
#Override
protected void onDestroy() {
BillingHelper.stopService();
super.onDestroy();
}
}
this if from where I call the above class
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()) {
case R.id.gtask_button:
startActivity(new Intent(getActivity(), InAppMain.class));
default:
break;
}
but now i want that from case R.id.gtask_button: i should start the in-app billing activity that i was starting from R.id.main_purchase_yes.
thnx in advance...
From what i see, this is called when you click the button
BillingHelper.requestPurchase(mContext, "android.test.purchased");
So maybe thats where it changes your layout to something else...
Post the method so we can take a look.
EDIT:
Ok, here's the code
protected static void requestPurchase(Context activityContext, String itemId){
if (amIDead()) {
return;
}
Log.i(TAG, "requestPurchase()");
Bundle request = makeRequestBundle("REQUEST_PURCHASE");
request.putString("ITEM_ID", itemId);
try {
Bundle response = mService.sendBillingRequest(request);
//The RESPONSE_CODE key provides you with the status of the request
Integer responseCodeIndex = (Integer) response.get("RESPONSE_CODE");
//The PURCHASE_INTENT key provides you with a PendingIntent, which you can use to launch the checkout UI
PendingIntent pendingIntent = (PendingIntent) response.get("PURCHASE_INTENT");
//The REQUEST_ID key provides you with a unique request identifier for the request
Long requestIndentifier = (Long) response.get("REQUEST_ID");
Log.i(TAG, "current request is:" + requestIndentifier);
C.ResponseCode responseCode = C.ResponseCode.valueOf(responseCodeIndex);
Log.i(TAG, "REQUEST_PURCHASE Sync Response code: "+responseCode.toString());
startBuyPageActivity(pendingIntent, new Intent(), activityContext);
} catch (RemoteException e) {
Log.e(TAG, "Failed, internet error maybe", e);
Log.e(TAG, "Billing supported: "+isBillingSupported());
}
}
and we find the culprit -
startBuyPageActivity(pendingIntent, new Intent(), activityContext);