I am trying to remove AdMob ads in my app upon an in-app purchase. I already have the code to hide and disable the ads working. I also have in-app billing implemented. I just need to find a way to keep the ads hidden and disabled if the user has made a purchase.
I have a boolean stored in SharedPreferences, which, upon a successful in-app purchase, should permanently remove the ads in the app. This works, but when the app is closed and reopened, the ads are back and you cannot perform the purchase again.
In onCreate() method:
mHelper = new IabHelper(this, base64EncodedPublicKey);
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
if (!result.isSuccess()) {
} else {
}
}
});
prefs = this.getSharedPreferences("com.wsandhu.conjugation", Context.MODE_PRIVATE);
if (adFree) {
prefs.edit().putBoolean("adFree", true).commit();
} else {
adFree = prefs.getBoolean("adFree", false);
}
In-App Billing implementation:
/* FOR IN-APP BILLING */
#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)) {
// Sets purchased boolean to true
adFree = true;
// Restart app
Intent i = getBaseContext().getPackageManager()
.getLaunchIntentForPackage( getBaseContext().getPackageName() );
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(i);
}
}
};
IabHelper.QueryInventoryFinishedListener mReceivedInventoryListener
= new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result,
Inventory inventory) {
if (result.isFailure()) {
// Handle failure
} else {
Purchase purchase = inventory.getPurchase(ITEM_SKU);
if (purchase != null) {
} else {
}
}
}
};
public void buyFullVersion() {
mHelper.launchPurchaseFlow(this, ITEM_SKU, 10001,
mPurchaseFinishedListener, "mypurchasetoken");
}
I don't really know what I'm doing wrong... I've uploaded the app to Google Play alpha testing more than ten times now and I'm getting a bit frustrated each time I wait two hours and it doesn't work. :P Help?
Here:
adFree = true;
Causing issue when user opening app again after closing adFree variable value getting reset to default.
Do it as by updating status in SharedPreferences when purchase is successful :
...
}else if (purchase.getSku().equals(ITEM_SKU)) {
//save value in SharedPreferences here
prefs.edit().putBoolean("adFree", true).commit();
}
and in onCreate method check for adFree as:
prefs = this.getSharedPreferences("com.wsandhu.conjugation",Context.MODE_PRIVATE);
if(prefs.contains("adFree")){
if(prefs.getBoolean("adFree", false)){
// disable ads here
}else{
// enable ads here
}
}else{
// enable ads
}
Related
I'm working on one of my first android apps which has some in-app billing features. I've spent the last 3 days researching how to add these in-app purchases after purchasing my developer's licence. I stumbled across a guide that lead me to success on a test program :
http://www.techotopia.com/index.php/An_Android_Studio_Google_Play_In-app_Billing_Tutorial
The test program works great. But when i applied this guide to my NavigationDrawerFragment Program, I've ran into some issues. Like i said in the title, i'm using android.test.purchased as the SKU and it seems to only let me use the test purchase every now and then.
Other times i get "In-app billing error: Unable to buy item, Error response: 7:Item Already Owned"
I have added the billing permission in the manifest file, I have also made this alteration to the Security.java File (following the guide):
public static boolean verifyPurchase(String base64PublicKey,
String signedData, String signature) {
if (TextUtils.isEmpty(signedData) ||
TextUtils.isEmpty(base64PublicKey) ||
TextUtils.isEmpty(signature)) {
Log.e(TAG, "Purchase verification failed: missing data.");
if (BuildConfig.DEBUG) {
return true;
}
return false;
}
MainActivity.java - OnCreate - Log.d code is // on purpose
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String base64EncodedPublicKey =
"My key that i almost pasted in here :P";
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");
}
}
});
mNavigationDrawerFragment = (NavigationDrawerFragment)
getSupportFragmentManager().findFragmentById(R.id.navigation_drawer);
mTitle = getTitle();
// Set up the drawer.
mNavigationDrawerFragment.setUp(
R.id.navigation_drawer,
(DrawerLayout) findViewById(R.id.drawer_layout));
}
Rest of MainActivity.java that doesn't have to do with the drawerFragment:
private static final String TAG = "com.example.inappbilling";
static final String ITEM_SKU = "android.test.purchased";
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();
}
}
};
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);
Context.MODE_PRIVATE);
//Never had this code actually run before, could contain errors
//but those aren't the issue to my knowledge.
SharedPreferences pref = MainActivity.this.getSharedPreferences("MyPref",
SharedPreferences.Editor editor = pref.edit();
editor.putBoolean("BoughtTabard",true);
editor.commit();
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.container, TabardGuideNeedToBuy.newInstance(8))
.commit();
} else {
// handle error
}
}
};
#Override
public void onDestroy() {
super.onDestroy();
if (mHelper != null) mHelper.dispose();
mHelper = null;
}
Not sure if I'm just overlooking a minor issue or something, but any ideas/solutions are greatly appreciated!
And I have indeed added the IInAppBillingService.aidl to the correct spot, along with all 9 of the util files(Base64,Secruity,SkuDeatils ect...)
if for some reason android.test.purchased isnt consumed when it's supposed to be, add the consumeItem(); call from in the if (result.isFailure()), run the app, relaunch the purchase flow, it will then crash and consume android.test.purchased, then go back to the code and remove the consumeItem(); call from within the if (result.isFailure()). Re-run the app once again, and android.test.purchased should now be able to be consumed again.
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?
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");
}
}
});
i have almost finished my first app. I have my in-app purchasing working, but it turns out i have a problem with consuming the purchases, since it should be possible to buy the item endless times. I think i have edited the code to consume the purchased item correctly now, I will provide the code so you can correct me if I'm wrong. But even though this code might be correct now, I can't purchase the product on my test device again, since it wasn't consumed when bought in the first place. How can i consume the product now? Here is my updated code.
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()) {
Log.d("Unlock", "Error purchasing: " + result);
return;
} else if (purchase.getSku().equals(SKU_HINTS)) {
consumeItem();
}
}
};
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(SKU_HINTS),
mConsumeFinishedListener);
}
}
};
IabHelper.OnConsumeFinishedListener mConsumeFinishedListener = new IabHelper.OnConsumeFinishedListener() {
public void onConsumeFinished(Purchase purchase, IabResult result) {
if (result.isSuccess()) {
add_hints();
purchase1.setText("Purchase complete");
} else {
// handle error
}
}
};
public void onClick(View v) {
case R.id.bUnlockHints:
mHelper.launchPurchaseFlow(this, SKU_HINTS, 10001,
mPurchaseFinishedListener, "");
break;
}
I figured out the answer. I had to change the startSetup method so it queries the inventory right away. Here is my new code.
IabHelper.QueryInventoryFinishedListener mReceivedInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result,
Inventory inventory) {
if (result.isFailure()) {
// Handle failure
}
// This is the updated part
Purchase hintPurchase = inventory.getPurchase(SKU_HINTS);
if (hintPurchase != null) {
mHelper.consumeAsync(inventory.getPurchase(SKU_HINTS),
mConsumeFinishedListener);
}
}
};
Here I check if the hints have been purchased before consuming it. Otherwise i would receive an error.
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
if (!result.isSuccess()) {
// Oh noes, there was a problem.
Log.d("Ultimate Quiz",
"Problem setting up In-app Billing: " + result);
}
// Hooray, IAB is fully set up!
// Here I run the queryInventory to check right away if the product has been bought, and consume it if it has.
mHelper.queryInventoryAsync(mReceivedInventoryListener);
}
});
I am learning to implement an in-app billing for my app such that people can for example, donate $ when press the donate button.
The user is allowed to donate more than one time, i.e. the purchase is consumable.
The codes below are sourced from the TrivalDrive sample and some tutorials from the web:
Code:
IabHelper mHelper;
static final String ITEM_SKU = "android.test.purchased";
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_in_app_billing);
buy10Button = (Button) findViewById(R.id.buy10Button);
buy15Button = (Button) findViewById(R.id.buy15Button);
buy20Button = (Button) findViewById(R.id.buy20Button);
String base64EncodedPublicKey = "keykeykey";
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);
return;
}
if (mHelper == null)
{
return;
}
Log.d(TAG, "In-app Billing is set up OK");
}
});
}
public void buy10Click(View view)
{
mHelper.launchPurchaseFlow(this, ITEM_SKU, 10001, mPurchaseFinishedListener, "");
}
public void buy15Click(View view)
{
}
public void buy20Click(View view)
{
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
if (mHelper == null) return;
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 (mHelper == null) return;
if (result.isFailure())
{
// Handle error
return;
}
else if ((purchase.getSku().equals(ITEM_SKU)))
{
consumeItem();
}
}
};
public void consumeItem()
{
mHelper.queryInventoryAsync(mReceivedInventoryListener);
}
IabHelper.QueryInventoryFinishedListener mReceivedInventoryListener = new IabHelper.QueryInventoryFinishedListener()
{
public void onQueryInventoryFinished(IabResult result, Inventory inventory)
{
if (mHelper == null) return;
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 (mHelper == null) return;
if (result.isSuccess())
{
Toast.makeText(InAppBillingActivity.this, "Thank you for your donation!!", Toast.LENGTH_LONG).show();
}
else
{
// handle error
}
}
};
Question:
Yet I keep on receiving E/IabHelper(13392): In-app billing error: Unable to buy item, Error response: 7:Item Already Owned error and that the payment dialog of the Google Play just does not popup.
I have researched and found out many similar situations, some suggested to wait for a few minute and then the purchase will be reset by itself, but I have waited for almost an hour but it still sucks.
I have also found that someone suggest to change the IabResult public boolean isSuccess() { return mResponse == IabHelper.BILLING_RESPONSE_RESULT_OK; } to return also the BILLING_RESPONSE_RESULT_ITEM_ALREADY_OWNED as isSuccess = true, yet i dont know how to amend such...
How could the problem be fixed? Thanks!!
You purchased "android.test.purchased" but did not consume it. However, if you forgot to consume it immediately, it is not easy to consume it again. We can wait for 14 days. The fake purchase will be cleared automatically. But it is not acceptable.
I spent a lot of time finding the solution:
Add this line to get debug info.
_iabHelper.enableDebugLogging(true, "TAG");
Run the app. In LogCat, you will see a json string like
{"packageName":"com.example","orderId":"transactionId.android.test.purchased","productId":"android.test.purchased","developerPayload":"123","purchaseTime":0,"purchaseState":0,"purchaseToken":"inapp:com.example:android.test.purchased"}
Consume it manually (Replace THAT_JSON_STRING with your json string)
Purchase purchase;
try {
purchase = new Purchase("inapp", THAT_JSON_STRING, "");
_iabHelper.consumeAsync(purchase, new OnConsumeFinishedListener() {
#Override
public void onConsumeFinished(Purchase purchase, IabResult result) {
Log.d("TAG", "Result: " + result);
}
});
} catch (JSONException e) {
e.printStackTrace();
}
_iabHelper is mHelper.
Check my below code here:
I don't understand in your code why have you used query inventory in purchase finish listener. ConsumeAsync() method should be call while you getting the sku same as your requested sku.
// 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 (result.isFailure()) {
complain("Error purchasing: " + result);
return;
}
if (!verifyDeveloperPayload(purchase)) {
complain("Error purchasing. Authenticity verification failed.");
return;
}
Log.d(TAG, "Purchase successful.");
if (purchase.getSku().equals(SKU_GAS)) {
// remove query inventory method from here and put consumeAsync() directly
mHelper.consumeAsync(purchase, mConsumeFinishedListener);
}
}
};
startSetup method
// you have forgot to call query inventory method in startSetup method.
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;
}
// Hooray, 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);
}
});
QueryInventoryFinishedListener
And also check if condition purchase is same as you are requested is
not equals to null and developer payload is also same in your query
inventory finish listener.
if (gasPurchase != null && verifyDeveloperPayload(gasPurchase)){
//code
}
// 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.");
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().
*/
// // 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;
}
}
};
Explaination why it happends:
Whenever you purchased consumable item google play store will not be managed it's product purchased detail and other things in the Google play console. That's why we have to call consumeAsync() method. when we purchased item, Google play store keep record item has been purchased for the one time and allow you to purchased second time.
Hope it will solve your problem.
You can use Google Play "FINANCIAL REPORTS"->"Visit your merchant account for more details"->"Orders" to View and Cancel any order to "consume it".
Then you need to restart your device. =)
I managed to "consume the purchase" simply by restarting the device.
I'm using: implementation 'com.android.billingclient:billing:2.0.0' and had the same error on purchase process.
the point is: before of launching the purchase we should consume the pending purchases.
please see the code snippet below:
List<String> skuList = new ArrayList();
skuList.add(THE_IAP_ID);
BillingClient.Builder billingClientBuilder = BillingClient.newBuilder(context).setListener(new PurchasesUpdatedListener() {
#Override
public void onPurchasesUpdated(BillingResult billingResult, #Nullable List<Purchase> purchases) {
// here we have to check the result ...
});
billingClientBuilder.enablePendingPurchases();
billingClient = billingClientBuilder.build();
billingClient.startConnection(new BillingClientStateListener() {
#Override
public void onBillingSetupFinished(BillingResult billingResult) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
// The BillingClient is ready - query purchases.
Purchase.PurchasesResult pr = billingClient.queryPurchases(BillingClient.SkuType.INAPP);
List<Purchase> pList = pr.getPurchasesList();
for (Purchase iitem : pList) {
ConsumeParams consumeParams = ConsumeParams.newBuilder()
.setPurchaseToken(iitem.getPurchaseToken())
.build();
billingClient.consumeAsync(consumeParams, consumeResponseListener);
}
// process the purchase
} else {
// cancelled or s.e.
...
}
}
Best Regards, Have you fun :)
I'm trying to implement the inApp billing service with IabHelper. I manage to go through the full purchase process without problems.
//-----------------------------------------------
public void billingServiceLaunchPurchase(String item) {
//-----------------------------------------------
if (isNetworkAvailableSync(getBaseContext())) {
currBuyItem=item;
billingConsummeType=1;
mHelper.launchPurchaseFlow(BaseActivity.this, item, 10001, mPurchaseFinishedListener, "");
}
else {
onBillingServiceFailed();
}
}
//-----------------------------------------------
mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
//-----------------------------------------------
public void onIabPurchaseFinished(IabResult result, Purchase purchase)
{
if (result.isFailure()) {
// Handle error
onBillingServiceFailed();
return;
}
else if (purchase.getSku().equals(currBuyItem)) {
billingServiceConsumeItem();
}
}
};
#Override
//-----------------------------------------------------------------------
protected void onActivityResult(int requestCode, int resultCode, Intent data)
//-----------------------------------------------------------------------
{
if (billingServiceConnected) {
if (!mHelper.handleActivityResult(requestCode, resultCode, data)) {
super.onActivityResult(requestCode, resultCode, data);
}
else {
// onActivityResult handled by IABUtil.
}
}
else
super.onActivityResult(requestCode, resultCode, data);
}
However, I cannot detect the event when the user launches the purchase but then press the backspace on the Google confirmation screen with the button "BUY" to interrupt it.
It neither triggers a failure on onIabPurchaseFinished nor it triggers onActivityResult so my application stays in an intermediary status.
Please help me to solve my problem.
According to what I have understood your question, you are searching for purchase cancel event in app billing.
You can trigger this via onActivityResult() method.Put below code in onActivityResult() method. There is simple RESEULT_CANCEL type that shows you user has been cancel purchasing.
if (mHelper == null)
return;
if (requestCode == 10001) {
int responseCode = data.getIntExtra("RESPONSE_CODE", 0);
String purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA");
Log.d("INAPP_PURCHASE_DATA", ">>>" + purchaseData);
String dataSignature = data.getStringExtra("INAPP_DATA_SIGNATURE");
Log.d("INAPP_DATA_SIGNATURE", ">>>" + dataSignature);
String continuationToken = data
.getStringExtra("INAPP_CONTINUATION_TOKEN");
Log.d("INAPP_CONTINUATION_TOKEN", ">>>" + continuationToken);
if (resultCode == RESULT_OK) {
try {
Log.d("purchaseData", ">>>"+purchaseData);
JSONObject jo = new JSONObject(purchaseData);
String sku = jo.getString("productId");
alert("You have bought the " + sku
+ ". Excellent choice, adventurer!");
} catch (JSONException e) {
alert("Failed to parse purchase data.");
e.printStackTrace();
}
} else if (resultCode == RESULT_CANCELED) {
Toast.makeText(AppMainTest.this,
"Sorry, you have canceled purchase Subscription.",
Toast.LENGTH_SHORT).show();
} else if (resultCode == IabHelper.BILLING_RESPONSE_RESULT_ITEM_ALREADY_OWNED) {
Toast.makeText(AppMainTest.this, "Item already owned",
Toast.LENGTH_SHORT).show();
}
}
}
or
you can also handle manually by using your business logic. check if
user cancel purchase product then put flag user has been purchased or
not if not then call launchPurchaseFlow() method again.
EDIT
onDestroy() method
#Override
public void onDestroy() {
super.onDestroy();
// very important:
Log.d(TAG, "Destroying helper.");
if (mHelper != null)
mHelper.dispose();
mHelper = null;
}
if you have button then you can directly call launchPurchaseFlow()
method into onClick event so that every time your mHelper created as
new purchase.
or
if you are using it in onCreate method and you haven't any button
click event to purchase product then you have to give value as null
according to my knowledge.
Hope it will solve your problem.
When user press BACK or press outside the dialog, the purchase flow is still in process and if user press PURCHASE button again, there will be an exception "Can't start async operation because another async operation is in progress".
To fix this, I had manually create a flag to know if there is a purchase flow in progress. And since IABHelper don't provide a way to dispose a purchase flow, I have to dispose mHelper and recall initBilling()
boolean onPurchaseFlow = false;
public void purchaseItem() {
if (!onPurchaseFlow) {
mHelper.launchPurchaseFlow(mActivity, SKU_PREMIUM, RC_REQUEST, mPurchaseFinishedListener, "");
onPurchaseFlow = true;
} else {
//dispose mHelper
if (mHelper != null) {
mHelper.dispose();
mHelper = null;
}
initBilling(mActivity); // restart IABHelper, a code snippet will fire launchPurchaseFlow when onPurchaseFlow is true
}
}
The other important part is call launchPurchaseFlow in onQueryInventoryFinished() to ensure that it is called (in the second request of user) when all the init operation has completed:
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
// YOUR CODE HERE
if (onPurchaseFlow == true) {
mHelper.launchPurchaseFlow(mActivity, SKU_PREMIUM, RC_REQUEST, mPurchaseFinishedListener, "");
}
}
Remember to reset the flag onPurchaseFlow = false when finish in onIabPurchaseFinished()
public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
Log.d(TAG, "Purchase successful.");
if (purchase.getSku().equals(SKU_PREMIUM)) {
// bought the premium upgrade!
// YOUR CODE HERE
onPurchaseFlow = false;
}
}
You can access the result code through IabResult class, and compare it with the different result codes in IabHelper class, and use it in your OnIabPurchaseFinishedListener:
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener
= new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result, Purchase purchase)
{
if (result.isFailure()) {
if (result.getResponse() == IabHelper.BILLING_RESPONSE_RESULT_USER_CANCELED || result.getResponse() == IabHelper.IABHELPER_USER_CANCELLED){
// user cancelled purchase
} else {
// any oder reasult
}
return;
}
else if (purchase.getSku().equals(SKU_SPIRIT_LEVEL)) {
// no error, purchase succeeded
}
}
};
you can detect the back press overriding by this method
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK)) {
//do what you want to avoid going back while during transaction
Log.d("bak button pressed", "ture");
}
return super.onKeyDown(keyCode, event);
}
even check this out, it might help you
Check if back key was pressed in android?
Inside IabHelper there's a method called handleActivityResult(...).
If you override onActivityResult (Fragment or Activity) and call that method inside (call it with the same parameters). In this way, the helper manage all the callbacks and can redo the purchase flow without launching any exceptions.