I have been struggling with this for over a week. I am working on subscriptions on the android use their In-App Billing Version 3 I built the code and have it running for purchase but as it was done last year this version did not support subscriptions and since I put this together from the example I don't have code to build the code to use subscriptions.
Here is the code I have in place for purchases and as I understand it I need to alter these to work with subscriptions instead of purchases plus add the code for determining if a subscription is valid on the google server. I do not see code for that right now.
String base64EncodedPublicKey = "MI...QAB";
mHelper = new IabHelper(this, base64EncodedPublicKey);
// enable debug logging (for a production application, you should set
// this to false).
mHelper.enableDebugLogging(false);
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
if (!result.isSuccess()) {
// Oh noes, there was a problem.
// complain("1 "+"Problem setting up in-app billing: " +
// result);
return;
}
mHelper.queryInventoryAsync(mGotInventoryListener);
}
});
// Listener that's called when we finish querying the items 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("2 "+"Failed to query inventory: " + result);
return;
}
// ///Log.d(TAG, "Query inventory was successful.");
// place code here to proceess purchase without reaccessing database
if (inventory.hasPurchase(appSKU)) {
// ///Log.i("Purchase","purchase consumed here");
//mHelper.consumeAsync(inventory.getPurchase(appSKU),mConsumeFinishedListener);
return;
}
}
};
public void order(String appSKU) {
mHelper.launchPurchaseFlow(this, appSKU, RC_REQUEST,
mPurchaseFinishedListener);
}
#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.");
}
}
// 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(purchase == null) {
;
}else {
String tester1 = purchase.toString();
// ///Log.d(TAG, "Purchase successful.");
// mHelper.consumeAsync(purchase, mConsumeFinishedListener);
// myCallServer(udid, calltype, data, OrderID, ProductId, PurchaseToken) {
Bundle myBundle = new Bundle();
myBundle.putString("AppTitle", AppTitle);
Intent myIntent = new Intent(getBaseContext(),
IntroALevActivity.class);
myIntent.putExtras(myBundle);
startActivity(myIntent);
}
}
};
// Called when consumption is complete
IabHelper.OnConsumeFinishedListener mConsumeFinishedListener = new IabHelper.OnConsumeFinishedListener() {
public void onConsumeFinished(Purchase purchase, IabResult result) {
// ///Log.d(TAG, "Consumption finished. Purchase: " + purchase +
// ", result: " + result);
// We know this is the "gas" sku because it's the only one we
// consume,
// so we don't check which sku was consumed. If you have more than
// one
// sku, you probably should check...
if (result.isSuccess()) {
// successfully consumed, so we apply the effects of the item in
// our
// game world's logic, which in our case means filling the gas
// tank a bit
alert("You made a purchase");
} else {
;
}
}
};
I have looked online but I have only found broken pieces of code to help me determine what the exact method code would be to check for existing subscriptions. If some one could look at my code and supply the method I am missing it would be great. Google examples have usually been good but since subscriptions have just been added to V3 as of Feb 15th there are not a lot of coding examples to build from.
I am struggling so if you have any questions or need additional information just let me know and I will do the best I can to supply it.
Thanks to the stackoverflow members
Even though I'm not using subscriptions in my implementation you can check that in your IabHelper.QueryInventoryFinishedListener mGotInventoryListener function. So instead of this:
// ///Log.d(TAG, "Query inventory was successful.");
// place code here to proceess purchase without reaccessing database
if (inventory.hasPurchase(appSKU)) {
// ///Log.i("Purchase","purchase consumed here");
//mHelper.consumeAsync(inventory.getPurchase(appSKU),mConsumeFinishedListener);
return;
}
Use this:
Purchase haspurchase = inventory.getPurchase(appSKU);
if (haspurchase = null) {
// do something since there wasn't a purchase
}
else {
// do something if you have the purchase
}
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
Related
I am new to in-app purchase system. I just watched some tutorials, then implemented in-app purchase. However, when my in-app purchase was successful, it did not run my code. Does it have to run the following code?:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 1001) {
String purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA");
if (resultCode == RESULT_OK) {
try {
JSONObject jo = new JSONObject(purchaseData);
String sku = jo.getString(inappid);
Toast.makeText(
MainActivity.this,
"You have bought the " + sku
+ ". Excellent choice,adventurer!",
Toast.LENGTH_LONG).show();
writeInApp("1");
playGroundMain.bonus_pop_up(R.drawable.rich_big, "\"VIP Монстр\"");
playGroundMain.reWriteBonus("1", 6);
} catch (JSONException e) {
System.out.println("Failed to parse purchase data.");
e.printStackTrace();
}
}
}
}
I've made a fair few apps with the payment system, you should take a look at this listener
IabHelper.OnConsumeFinishedListener mConsumeFinishedListener = new IabHelper.OnConsumeFinishedListener()
{
public void onConsumeFinished(Purchase purchase, IabResult result) {
Log.d(TAG, "Consumption finished. JSON: " + purchase.getOriginalJson() + ", signature: " + purchase.getSignature());
if (mHelper == null) return;
if (result.isSuccess())
{
Log.i(TAG, "BOUGHT THE ITEM :-)")
}
else
{
Log.e(TAG, "Error while consuming: " + result);
}
Log.d(TAG, "End consumption flow.");
}
};
you could add this listener in the consumeAsync function of the IabHelper like so:
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener()
{
public void onIabPurchaseFinished(IabResult result, Purchase purchase)
{
if (purchase.getSku().equals(SKU_21500))
{
// bought 21500 credits
Log.d(TAG, "Purchase is 21500 credits. Starting credits consumption.");
try
{
mHelper.consumeAsync(purchase, mConsumeFinishedListener);
}
catch (IabHelper.IabAsyncInProgressException e)
{
Log.d(TAG, "Error consuming 21500 credits. Another async operation in progress.");
return;
}
}
}
}
And then you can put the "mPurchaseFinishedListener" in the "launchPurchaseFlow " function as so:
mHelper.launchPurchaseFlow(activity, item, REQUEST_CODE, mPurchaseFinishedListener);
REQUEST_CODE being 10001
Hope this helps!
If you are trying to do a non-consumable inapp purchase the code is fine.
When doing test purchases sometimes the onActivityResult() does not gets called. In a real purchase this may not happen.
To handle the purchase in that case you have to check result code == user already owns this item //resultCode 7, so that the user will not have to pay again in a situation like this.
I am trying to get back to the IabHelper.OnIabPurchaseFinishedListener when purchasing is over for my subscription , but the problem is Its not calling when purchase is done .
I had tried with onactivity result but still its not working
here Is my code block
I'm extending fragments as below
public class ConfirmationScreen extends Fragment {
mHelper = new IabHelper(getActivity(), base64EncodedPublicKey);
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);
}
});
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().
*/
Purchase gasPurchase = inventory.getPurchase(SKU);
if (gasPurchase != null && verifyDeveloperPayload(gasPurchase)) {
Log.d(TAG, "We have gas. Consuming it.");
mHelper.consumeAsync(inventory.getPurchase(SKU), mConsumeFinishedListener);
return;
}
}
};
//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;
Log.d(TAG, "Purchase successful.");
if (purchase.getSku().equals(SKU)) {
//bought 1/4 tank of gas. So consume it.
Log.d(TAG, "Purchase is gas. Starting gas consumption.");
}
}
};
I have referred to this link
You need to implement the onActivityResult method on your FragmentActivity
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent 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.");
}
}
You need to implement the onActivityResult method on your FragmentActivity
This has to be put in activity, which actually runs the:
mHelper.launchPurchaseFlow(...);
Sometimes my users report that an in-app purchase isn't being logged (even though it goes through and is authorized). I used the TrivialDrive sample and have the following code:
public class FragmentGoPro extends Fragment{
private static final String TAG = AppConstants.Keys.TAG;
private final String SKU_PREMIUM = "001";
private IabHelper mHelper;
private static final int RC_REQUEST = 546731;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
View rootView = inflater.inflate(R.layout.fragment_billing, container, false);
getActivity().getActionBar().setTitle(getActivity().getString(R.string.app_name));
setupHelper();
TextView descriptionView = (TextView)rootView.findViewById(R.id.pro_description);
String proDescription = getString(R.string.pro_description);
descriptionView.setText(proDescription);
Button goPro = (Button)rootView.findViewById(R.id.purchase);
goPro.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
makePurchase();
}
});
return rootView;
}
private void setupHelper() {
mHelper = new IabHelper(getActivity(), AppConstants.Keys.PRO);
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
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.
mHelper.queryInventoryAsync(mGotInventoryListener);
}
});
}
private void makePurchase() {
Log.i(TAG, "MakePurchase requested.");
mHelper.launchPurchaseFlow(getActivity(), SKU_PREMIUM, RC_REQUEST,
mPurchaseFinishedListener, null);
}
// Callback for when a purchase is finished
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
Log.i(TAG, "Purchase finished: " + result + ", purchase: " + purchase);
if (result.isFailure()) {
complain("Error purchasing: " + result);
return;
}
Log.i(TAG, "Purchase of " + purchase.getSku() + " Sku confirmed.");
if (purchase.getSku().equals(SKU_PREMIUM)) {
// bought the premium upgrade!
alert("Thank you for upgrading to premium!");
Log.i(TAG, "Premium features unlocked!");
setPreference("mIsPremium", true);
setPreference("Purchased", true);
updateMetrics();
updateNav();
}
}
};
protected IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
if (result.isFailure()) {
complain("Failed to query inventory: " + result);
return;
}
/*
* Check for items we own. Notice that for each purchase, we check
* the developer payload to see if it's correct! See
* verifyDeveloperPayload().
*/
// Do we have the premium upgrade?
Purchase premiumPurchase = inventory.getPurchase(SKU_PREMIUM);
boolean mIsPremium = (premiumPurchase != null);
if (mIsPremium) {
Log.i(TAG, "Premium purchased previously, restoring transactions.");
setPreference("mIsPremium", mIsPremium);
setPreference("Purchased", true);
updateNav();
}
}
};
private void updateMetrics() {
EasyTracker.getInstance().setContext(getActivity());
flurryEvent("state", "purchase_state", "purchased");
if (!checkIfTesting()) {
Log.i(TAG, "Purchase Logged");
Transaction myTrans = new Transaction.Builder(
"0_3164", // (String) Transaction Id, should be unique.
(long) 2.99 * 1000000) // (long) Order total (in micros)
.setAffiliation("In-App Google Play") // (String) Affiliation
.build();
myTrans.addItem(new Transaction.Item.Builder(
"001", // (String) Product SKU
"Pro License", // (String) Product name
(long) 2.99 * 1000000, // (long) Product price (in micros)
(long) 1 ) // (long) Product quantity
.build());
Tracker myTracker = EasyTracker.getTracker(); // Get reference to tracker.
myTracker.sendTransaction(myTrans); // Track the transaction.
}
}
protected void updateNav() {
Activity activity = getActivity();
if(activity instanceof MainActivity) {
((MainActivity) activity).initializeNavigation();
} else {
getActivity().finish();
}
}
#Override
public 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.");
}
}
It appears that my updateMetrics() and updateNav() are not being called but it looks to me like it always should. Again, this is only happening in a small percentage of users.
I believe it has to do with my orderID being static. I followed the guide here to use the updated analytics. Using System.currentTimeInMillis() should give a unique orderID and prevent clashes of data.
Additionally, I was using this in a fragment so onActivityResult was not being called (strangely it worked on my test devices) so I needed to modify the billing code.
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 :)