2

I'm implementing in-app billing where the user shall be able to buy access to premium content. This is typical non-consumable items. (Let's say the premium content is extra questions or categories in an question-app)

I have used this tutorial to create the first version. The problem is how I shall implement the non-consumable part..

How do I know that the user has bought the premium content? One solution I'm think of is to have a column in my question-table that is initially "0". When the user buy premium access, the this column is set to "1". Is this a way to go?

Where in my code do I get the message from the billing API that the content is already bought? (If it is..)

My code.. (From tutorial, "buy the possibility to click a button")

public class BuyExtras extends Activity {

private static final String TAG = "inAppBilling";
static final String ITEM_SKU = "android.test.purchased";
IabHelper mHelper;
private Button clickButton;
private Button buyButton;

@Override
protected void onCreate(Bundle savedInstanceState) {
    Log.i(TAG, "onCreate CALLED");
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_buy_extras);


    buyButton = (Button)findViewById(R.id.buyButton);
    clickButton = (Button)findViewById(R.id.clickButton);   
    clickButton.setEnabled(false);



    String base64EncodedPublicKey = 
            "<myKey>";

    mHelper = new IabHelper(this, base64EncodedPublicKey);

    mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
        public void onIabSetupFinished(IabResult result) 
        {
            if (!result.isSuccess()) {
                Log.d(TAG, "In-app Billing setup failed: " + 
                        result);
            } else {             
                Log.d(TAG, "In-app Billing is set up OK");
            }
        }
    });


}

public void buyClick(View view) {
    mHelper.launchPurchaseFlow(this, ITEM_SKU, 10001, mPurchaseFinishedListener, "mypurchasetoken");
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) 
{
    if (!mHelper.handleActivityResult(requestCode, resultCode, data)) {     
        super.onActivityResult(requestCode, resultCode, data);
    }
}


IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
    public void onIabPurchaseFinished(IabResult result, Purchase purchase) 
    {
        if (result.isFailure()) {
            // Handle error
            return;
        }      
        else if (purchase.getSku().equals(ITEM_SKU)) {
            consumeItem();
            buyButton.setEnabled(false);
        }

    }
};

public void consumeItem() {
    mHelper.queryInventoryAsync(mReceivedInventoryListener);
}

IabHelper.QueryInventoryFinishedListener mReceivedInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
    public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
        if (result.isFailure()) {
            // Handle failure
        } else {
            mHelper.consumeAsync(inventory.getPurchase(ITEM_SKU), mConsumeFinishedListener);
        }
    }
};


IabHelper.OnConsumeFinishedListener mConsumeFinishedListener = new IabHelper.OnConsumeFinishedListener() {
    public void onConsumeFinished(Purchase purchase, IabResult result) {

        if (result.isSuccess()) {         
            clickButton.setEnabled(true);
        } else {
            // handle error
        }
    }
};

public void buttonClicked (View view)
{
    clickButton.setEnabled(false);
    buyButton.setEnabled(true);
}


@Override
public void onDestroy() {
    super.onDestroy();
    if (mHelper != null) mHelper.dispose();
    mHelper = null;
}

}

I read this question indicating that I not should call the

mHelper.consumeAsync(purchase, mConsumeFinishedListener);

But is just removing it ok? Where should I handle the case that the user tries a second buy?

Community
  • 1
  • 1
EirikO
  • 617
  • 8
  • 19
  • If you don't consume it, then attempting a second purchase would raise the exception that the item has already been purchased. That should address the issue for you. Try it out. – 323go Apr 29 '14 at 16:51
  • Would that be in "onIabPurchaseFinished" (result.isFailure())? – EirikO Apr 29 '14 at 16:52

1 Answers1

2

If you don't want consume, then don't use consumeAsync.

 @Override
public void onQueryInventoryFinished(IabResult result, Inventory inv)
{
    if (result.isFailure())
    {
        Log.e(TAG, "In-app Billing query failed: " + result);
        return;
    } else
    {
        boolean hasPurchased_ITEM_SKU_PURCHASE_1 = inv.hasPurchase(ITEM_SKU_PURCHASE_1);
        SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getActivity());
        SharedPreferences.Editor editor = sharedPref.edit();
        editor.putBoolean(KEY_PREF_PURCHASE_1_AVAILABLE, !hasPurchased_ITEM_SKU_PURCHASE_1);
        editor.commit();

        // You can update your UI here, ie. Buy buttons.
    }
}

You can use the sharedpref to store the purchase info, and also check every onCreate of the activity and update the sharedpref accordingly. The key part on how to check if a SKU is purchased is:

     boolean hasPurchased_ITEM_SKU_PURCHASE_1 = inv.hasPurchase(ITEM_SKU_PURCHASE_1);

Do your query sync in your IAP setup and update your UI accordingly.

 mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
    public void onIabSetupFinished(IabResult result) 
    {
        if (!result.isSuccess()) {
            Log.d(TAG, "In-app Billing setup failed: " + 
                    result);
        } else {             
              mHelper.queryInventoryAsync(mReceivedInventoryListener);
            Log.d(TAG, "In-app Billing is set up OK");
        }
    }
 });
uDevel
  • 1,891
  • 1
  • 13
  • 14
  • Shouldn't your code-suggestion be placed in "onIabPurchaseFinished()" instead of "onQueryInventoryFinished()"? I get an error (Item already owned) in this function, which is called before onQueryInventroyFinished()... – EirikO Apr 29 '14 at 17:58
  • No, you should do the inv query check at the onCreate of the activity or fragment, then change your UI accordingly. The user should never be able to buy the same item again from your UI, so it should never go into onIabPurchaseFinished() for the same item to begin with. – uDevel Apr 29 '14 at 18:04
  • I hate when people do it without explanation. Here, take +1 vote ;) – Jakub Turcovsky Feb 24 '15 at 11:59