I could manage in my code to perform an in-app-billing. However, if I install the app, and redo the purchase, it does purchase again.
Where is the problem?
Here is the code:
case R.id.action_disable_ads:
try {
mHelper.launchPurchaseFlow(this, ITEM_SKU, 10001,
mPurchaseFinishedListener);
} catch (IabHelper.IabAsyncInProgressException e) {
//e.printStackTrace();
}
return true;
}
return super.onOptionsItemSelected(item);
}
//functions special 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)) {
consumeItem();
//buyButton.setEnabled(false);
}
}
};
public void consumeItem() {
try {
mHelper.queryInventoryAsync(mReceivedInventoryListener);
} catch (IabHelper.IabAsyncInProgressException e) {
//e.printStackTrace();
}
}
IabHelper.QueryInventoryFinishedListener mReceivedInventoryListener
= new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result,
Inventory inventory) {
if (result.isFailure()) {
// Handle failure
} else {
try {
mHelper.consumeAsync(inventory.getPurchase(ITEM_SKU),
mConsumeFinishedListener);
} catch (IabHelper.IabAsyncInProgressException e) {
//e.printStackTrace();
}
}
}
};
IabHelper.OnConsumeFinishedListener mConsumeFinishedListener =
new IabHelper.OnConsumeFinishedListener() {
public void onConsumeFinished(Purchase purchase,
IabResult result) {
if (result.isSuccess()) {
//first save purchase state
SharedPreferences settings = getPreferences(0);
SharedPreferences.Editor settingsEditor = settings.edit();
settingsEditor.putString("adfree", "purchased");
settingsEditor.apply();
//then, hide the disable ads button
MenuItem disableAds = mainMenu.findItem(R.id.action_disable_ads);
disableAds.setVisible(false);
//then display a message that the transaction is successful
Toast.makeText(getApplicationContext(), "AdFree bundle purchased successfully!", Toast.LENGTH_SHORT).show();
//then change the state of ad-free bundle for current session:
adFreeBundle = "purchased";
//then disable ads
interstitialAd = null;
mAdView.setVisibility(View.GONE);
} else {
//display an error message that the transaction was not successful
AlertDialog.Builder builder = new AlertDialog.Builder(getApplicationContext());
builder.setTitle("TRANSACTION FAILED!");
builder.setMessage("Unfortunately, the transaction was unsuccessful!");
builder.setPositiveButton("CLOSE", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// Do nothing but close the dialog
dialog.dismiss();
}
});
final AlertDialog alert = builder.create();
alert.setOnShowListener(new DialogInterface.OnShowListener() {
#Override
public void onShow(DialogInterface arg0) {
alert.getButton(AlertDialog.BUTTON_POSITIVE).setTextColor(Color.parseColor("#000000"));
}
});
alert.show();
}
}
};
#Override
public void onDestroy() {
super.onDestroy();
if (mHelper != null) try {
mHelper.dispose();
} catch (IabHelper.IabAsyncInProgressException e) {
//e.printStackTrace();
}
mHelper = null;
}
The process of in-app-billing functioned just fine. However, I would be happy to try to get the purchased item upon reinstalling the app.
This is because you are consuming the purchase, and setting a flag in sharedPreferences.
Don't do this anyone can change the sharedPreferences using the right tools.
When you uninstall your app your sharedPreferences will reset and you will lose your 'purchase'.
Because you consume the item, in the 2nd try your inventory won't have the right SKU available, this is why you are prompt to buy again.
If you make the buy button always visible you will be asked to pay every click.
To fix this
Don't consume the item, and the most important thing don't set any flag in sharedPreferences. this also apply's to sql databases.
Consuming is not meant to be used in a non-consumable item, this is your case since you are charging the user to remove the ads, if you try to consume this your users won't be very happy
When your app starts check your inventory if your inventory has the right SKU then give the user the product.
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
#Override
public void onIabSetupFinished(IabResult result) {
if (!result.isSuccess()) {
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.");
try {
mHelper.queryInventoryAsync(new IabHelper.QueryInventoryFinishedListener() {
#Override
public void onQueryInventoryFinished(IabResult result, Inventory inv) {
handleInventory(result, inv);
}
});
} catch (IabHelper.IabAsyncInProgressException e) {
complain("Error querying inventory. Another async operation in progress.");
e.printStackTrace();
}
}
});
public void handleInventory(IabResult result, Inventory inventory) {
if (mHelper == null) return;
// Is it a failure?
if (result.isFailure()) {
return;
}
Log.d(TAG, "Query inventory was successful.");
Purchase premiumPurchase = inventory.getPurchase(YOUR SKU);
//verify Developer Payload here, it's recommended but not mandatory.
//mIsPremium = (premiumPurchase != null && verifyDeveloperPayload(premiumPurchase));
mIsPremium = premiumPurchase != null;
if (mIsPremium) {
//Enable feature
}else{
//Disable feature
}
}
Related
I am new to the coding and i mostly copy and paste from the google in app billing
Can anyone guide me in what is wrong in the code. Refer this
Android check if in app purchase was bought before
Here is my purchaseActivity
public class PurchaseActivity extends CustomMenu {
tk.myessentialoils.ideasapp.util.IabHelper mHelper;
Button buyButton;
String product1SKU;
Boolean VIP=false;
//String product1SKU = "vip_member";//getResources().getString(R.string.product1SKU);
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_purchase);
buyButton = findViewById(R.id.buyButton);
product1SKU = getString(R.string.product1SKU);
mHelper = new tk.myessentialoils.ideasapp.util.IabHelper(this, getString(R.string.billing64basecode));
mHelper.startSetup(new tk.myessentialoils.ideasapp.util.IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(tk.myessentialoils.ideasapp.util.IabResult result) {
if (!result.isSuccess()) {
// Oh no, there was a problem.
Log.d("TAG", "Problem setting up In-app Billing: " + result);
}
// Hooray, IAB is fully set up!
}
});
/*
try {
mHelper.queryInventoryAsync(mGotInventoryListener);
} catch (IabHelper.IabAsyncInProgressException e) {
Log.d("TAG","Error querying inventory. Another async operation in progress.");
}
SharedPreferences.Editor editor = this.getSharedPreferences("Name", MODE_PRIVATE).edit();
Log.d("TAG", "checkVIPStatus: " +VIP);
editor.putBoolean("VIP",VIP);
editor.apply();
*/
}
#Override
public void onDestroy() {
super.onDestroy();
if (mHelper != null) try {
mHelper.dispose();
} catch (tk.myessentialoils.ideasapp.util.IabHelper.IabAsyncInProgressException e) {
e.printStackTrace();
}
mHelper = null;
}
public void buyClick(View view) {
try {
mHelper.launchPurchaseFlow(this, product1SKU, 10001,
mPurchaseFinishedListener, "mypurchasetoken");
} catch (tk.myessentialoils.ideasapp.util.IabHelper.IabAsyncInProgressException e) {
e.printStackTrace();
Toast.makeText(this, "Please set up your google account", Toast.LENGTH_SHORT).show();
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode,
Intent data)
{
if (!mHelper.handleActivityResult(requestCode,
resultCode, data)) {
super.onActivityResult(requestCode, resultCode, data);
}
}
tk.myessentialoils.ideasapp.util.IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener
= new tk.myessentialoils.ideasapp.util.IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(tk.myessentialoils.ideasapp.util.IabResult result,
tk.myessentialoils.ideasapp.util.Purchase purchase)
{
if (mHelper == null) return;
if (result.isFailure()) {
// Handle error
Log.d("TAG", "onQueryInventoryFinished: Failed");
}
else if (purchase.getSku().equals(product1SKU)) {
buyButton.setEnabled(false);
VIP=true;
storeVIP();
}
}
};
tk.myessentialoils.ideasapp.util.IabHelper.QueryInventoryFinishedListener mGotInventoryListener
= new tk.myessentialoils.ideasapp.util.IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(tk.myessentialoils.ideasapp.util.IabResult result,
tk.myessentialoils.ideasapp.util.Inventory inventory) {
if (result.isFailure()) {
// handle error here
Log.d("TAG", "onQueryInventoryFinished: Failed");
}
else {
// does the user have the premium upgrade?
boolean mIsPremium = inventory.hasPurchase(product1SKU);
buyButton.setEnabled(false);// update UI accordingly
VIP=true;
storeVIP();
}
}
};
private void storeVIP(){
SharedPreferences.Editor editor = getSharedPreferences("Name", MODE_PRIVATE).edit();
Log.d("TAG", "checkVIPStatus: " +VIP);
editor.putBoolean("VIP",VIP);
editor.apply();
}
}
Question 1
My purchase activity crashed when I enable the commented code.
mHelper.queryInventoryAsync(mGotInventoryListener);
With the error IAB is not set up.
Can anyone show me what is wrong with this?
Question 2
Should I use queryInventoryAsync or use the method as described in here?
Android: in-app: check if an item has been purchased
Which method is easier or more efficient?
Question 1 answer
Place inside the IAB
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_purchase);
buyButton = findViewById(R.id.buyButton);
product1SKU = getString(R.string.product1SKU);
mHelper = new tk.myessentialoils.ideasapp.util.IabHelper(this, getString(R.string.billing64basecode));
mHelper.startSetup(new tk.myessentialoils.ideasapp.util.IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(tk.myessentialoils.ideasapp.util.IabResult result) {
if (result.isSuccess()) {
Log.d("TAG", "onIabSetupFinished: ");
// Hooray, IAB is fully set up!
try {
mHelper.queryInventoryAsync(mGotInventoryListener);
} catch (IabHelper.IabAsyncInProgressException e) {
Log.d("TAG","Error querying inventory. Another async operation in progress.");
}
}
else {
// Oh no, there was a problem.
Log.d("TAG", "Problem setting up In-app Billing: " + result);
}
}
});
}
Question 2 answer
It depend of type of item we are selling.
For one time transactions, and the item cannot be bought further, i used queryInventoryAsync.
I have some problems with in app billing, I wanted to create an product inside the app that can be bought several times. But google made the in app billing in a way that a product first must be consumed before you can buy it again. I tried it with the following code:
public class HomeFragment extends Fragment {
private WebView homesite;
private String homeTabUrl;
private ProgressBar pBar;
private Bundle webViewBundle;
private Boolean buddyProfile = false;
private Boolean buddyProfile2 = false;
String url="";
private Context mContext;
final static String REQUEST_SUCCESS = "COMPLETED";
//Amount point
private static final int CREDIT_AMOUNT_100 = 100;
private static final int CREDIT_AMOUNT_500 = 500;
public static final String PAY_UID_100 = "messages";
public static final String PAY_UID_500 = "messages2";
private int mCreditAmount;
private IabHelper mHelper;
private static final int PAYINAPP_REQUESTCODE = 10001;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
setRetainInstance(true);
StaticMembers.loadHomeTab = true;
purchaseCredit(CREDIT_AMOUNT_100);
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
//Logger.error("YES-HERE IN ONACTIVITY RESULT");
if (checkReultPayInApp(requestCode,resultCode, data)) {
return;
}
if (!mHelper.handleActivityResult(requestCode, resultCode, data)) {
super.onActivityResult(requestCode, resultCode, data);
}
}
#Override
public void onStart() {
super.onStart();
}
private void loadData(View v) {
initPayinApp();
}
private void initPayinApp() {
// TODO Auto-generated method stub
String base64EncodedPublicKey = "n9goJ2waiwGS3F0E+XpjlMJRnn6rKtaH3lWxGUZQjNJeAAdL78mFeUTAGaZLgX/YOIuERWL5IzLaTXNi69c60oeh489wi3lyGtWbNvXR5EXVNhazti2mZgwvjhUdzW7/73mV0rHZn0f24G3Dpy0zLaTXNi69c60oeh489wi3lyGtWbNvXR5EXVNhazti2mZgwvjhUdzW7/73mV0rHZn0f24G3Dpy0wkIFWt51OnnusIVlJHrwJ8dYz4mUZ6SLFhkXL8NhrRAcZKvUV3WySB55SA5uu1+IoGG7mJw0QPn9goJ2waiwGS3F0E+XpjlMJRnn6rKtaH3lWxGUZQjNJeAAdL78mFeUTAGaZLgX/YOIuERWL5I7uInsqWH+ny1HFDr2wIDAQAB";
mHelper = new IabHelper(getActivity(), base64EncodedPublicKey);
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
if (!result.isSuccess()) {
Log.d("YES", "In-app Billing setup failed: " + result);
} else {
Log.d("YES", "In-app Billing is set up OK");
}
}
});
}
IabHelper.OnConsumeFinishedListener mConsumeFinishedListener =
new IabHelper.OnConsumeFinishedListener() {
public void onConsumeFinished(Purchase purchase, IabResult result) {
Logger.error("YES- Only in consume Listener");
if (result.isSuccess()) {
Logger.error("YES-in success consume Listener");
}
else {
Logger.error("YES-!in success consume Listener");
}
}
};
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener
= new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result, Purchase purchase)
{
Logger.error("YES-IN SUCCESS: " + result.getMessage());
if (result.isFailure()) {
Logger.error("YES-"+result.getMessage());
return;
}
else if (purchase.getSku().equals(PAY_UID_100)) {
Logger.error("YES-100 purchased");
// remove query inventory method from here and put consumeAsync() directly
Toast.makeText(getActivity(), "100 points added successfully", Toast.LENGTH_SHORT).show();
mHelper.consumeAsync(purchase, mConsumeFinishedListener);
} else if ( purchase.getSku().equals(PAY_UID_500)) {
Toast.makeText(getActivity(), "500 points added successfully", Toast.LENGTH_SHORT).show();
Logger.error("YES-500 purchased");
mHelper.consumeAsync(purchase, mConsumeFinishedListener);
}
}
};
private void purchaseCredit(int creditAmount) {
mCreditAmount = creditAmount;
try {
if (mHelper != null) mHelper.flagEndAsync();
mHelper.launchPurchaseFlow(getActivity(), getIdPayInAppByAmount(creditAmount), PAYINAPP_REQUESTCODE,
mPurchaseFinishedListener, "mypurchasetoken");
} catch (Exception e) {
Toast.makeText(getActivity(), e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
protected String getIdPayInAppByAmount(int creditAmount) {
switch (creditAmount) {
case CREDIT_AMOUNT_100:
return PAY_UID_100;
case CREDIT_AMOUNT_500:
return PAY_UID_500;
}
return null;
}
private boolean checkReultPayInApp(int requestCode, int resultCode, Intent data) {
// TODO Auto-generated method stub
if (requestCode!= PAYINAPP_REQUESTCODE) {
return false;
}
int responseCode = data.getIntExtra("RESPONSE_CODE", 0);
String purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA");
String dataSignature = data.getStringExtra("INAPP_DATA_SIGNATURE");
if (resultCode == Activity.RESULT_OK) {
// Purchased Status = Purchased
try {
// Product Details JSON
JSONObject jo = new JSONObject(purchaseData);
// Purchased Product ID
String sku = jo.getString("productId");
addCredit(mCreditAmount);
} catch (Exception e) {
e.printStackTrace();
}
} else {
}
return true;
}
public void addCredit(final int creditAmount) {
TaskPost.depositPoint(getActivity(), new User().getEmail(), creditAmount, new MyHttpClient.OnRequestListener() {
public void OnStart() {
// TODO Auto-generated method stub
}
#Override
public void OnFinish(String result) {
if (result.equalsIgnoreCase(REQUEST_SUCCESS)) {
Toast.makeText(mContext,"You have just add " + creditAmount+ " success to your account",Toast.LENGTH_SHORT).show();
}
}
});
}
}
Please let me know what wrong I am doing while consuming the product so I can make changes accordingly.
you should consume the previously purchased items.
private void initPayinApp() {
// TODO Auto-generated method stub
String base64EncodedPublicKey = "";
mHelper = new IabHelper(getActivity(), base64EncodedPublicKey);
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
if (!result.isSuccess()) {
Log.d("YES", "In-app Billing setup failed: " + result);
} else {
Log.d("YES", "In-app Billing is set up OK");
//comment this function once all the previously purchased items are consumed successfully
reset();
}
}
});
}
void reset() {
mHelper.queryInventoryAsync(mGotInventoryListener);
}
IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
#Override
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
Log.d(TAG, "Query inventory finished.");
if (result.isFailure()) {
Log.d(TAG, "Failed to query inventory: " + result);
return;
}
Log.d(TAG, "Query inventory was successful.");
if(inventory.hasPurchase(PAY_UID_100))
{
Log.d(TAG, "already purchased : " + PAY_UID_100);
mHelper.consumeAsync(inventory.getPurchase(PAY_UID_100), mConsumeFinishedListener);
}
if(inventory.hasPurchase(PAY_UID_500))
{
Log.d(TAG, "already purchased : " + PAY_UID_500);
mHelper.consumeAsync(inventory.getPurchase(PAY_UID_500), mConsumeFinishedListener);
}
}
};
In my Android-libGDX game, I have the GameHelper and the IabHelper. I need to have the onActivityResult method setup for both the classes. Below mentioned are the scenarios in which I have issues. Could you please assist me to resolve this and have the onActivityResult menthod setup for both GameHelper and IabHelper.
Scenario 1
When I setup the onActivityResult exclusively for GameHelper like the below one. The GameHelper related sign-in / sign-out works perfect but no the IabHelper. The IabHelper related process like purchase fails on 2nd product stating that there is already a asynch process is going on.
#Override
public void onActivityResult(int request, int response, Intent data) {
super.onActivityResult(request, response, data);
Log.i("AndroidLauncher", "onActivityResult - response :" + response);
gameHelper.onActivityResult(request, response, data);
}
Scenario 2When I setup the onActivityResult exclusively for IabHelper. The above mentioned issue in Scenario 1 is not happening for IabHelper. But the sign-in / sign-out related functions are not working in GameHelper.
#Override
public void onActivityResult(int request, int response, Intent data) {
if (!mHelper.handleActivityResult(request, response, 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(request, response, data);
}
}
Additional Code Details
private void iabSetup() {
// load game data
loadData();
String base64EncodedPublicKey ="KeyKeyKey";
Log.d(TAG, "Creating IAB helper.");
mHelper = new IabHelper(this, base64EncodedPublicKey);
mHelper.enableDebugLogging(true);
Log.d(TAG, "Starting setup.");
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
Log.d(TAG, "Setup finished.");
if (!result.isSuccess()) {
complain("Problem setting up in-app billing: " + result);
return;
}
if (mHelper == null) return;
Log.d(TAG, "Setup successful. Querying inventory.");
mHelper.queryInventoryAsync(mGotInventoryListener);
}
});
}
// 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.");
// 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.");
// Do we have the additional 1 life?
Purchase life1Purchase = inventory.getPurchase(SKU_1_Life);
life1Purchased = (life1Purchase != null);
Log.d(TAG, "User life1Purchased : " + life1Purchased);
// Do we have the additional 2 life?
Purchase life2Purchase = inventory.getPurchase(SKU_2_Life);
life2Purchased = (life2Purchase != null);
Log.d(TAG, "User life2Purchased : " + life2Purchased);
// Do we have the basket Speed Purchased ?
Purchase basketSpeedPurchase = inventory.getPurchase(SKU_Increase_Bucket_Speed);
basketSpeedPurchased = (basketSpeedPurchase != null);
Log.d(TAG, "User basketSpeedPurchased : " + basketSpeedPurchased);
// Do we have the basket Speed Purchased ?
Purchase basketSizePurchase = inventory.getPurchase(SKU_Increase_Bucket_Size);
basketSizePurchased = (basketSizePurchase != null);
Log.d(TAG, "User basketSizePurchased : " + basketSizePurchased);
// Do we have the basket Speed Purchased ?
Purchase adFreeGamePurchase = inventory.getPurchase(SKU_Ads_Free_Game);
adFreeGamePurchased = (adFreeGamePurchase != null);
Log.d(TAG, "User adFreeGamePurchased : " + adFreeGamePurchased);
}
};
public void onAdd1lifeButtonClicked() {
if (!mHelper.subscriptionsSupported()) {
complain("Subscriptions not supported on your device yet. Sorry!");
return;
}
Log.d(TAG, "Launching purchase flow for Add 1 life.");
try {
new Thread(new Runnable() {
#Override
public void run() {
mHelper.launchPurchaseFlow(AndroidLauncher.this, SKU_1_Life, RC_REQUEST, mPurchaseFinishedListener, payloadString);
}
}).start();
} catch (Exception e) {
Log.d(TAG, "buyItem Exception: " + e);
}
}
public void onAdd2livesButtonClicked() {
if (!mHelper.subscriptionsSupported()) {
complain("Subscriptions not supported on your device yet. Sorry!");
return;
}
Log.d(TAG, "Launching purchase flow for Add 2 lives.");
try {
new Thread(new Runnable() {
#Override
public void run() {
mHelper.launchPurchaseFlow(AndroidLauncher.this, SKU_2_Life, RC_REQUEST, mPurchaseFinishedListener, payloadString);
}
}).start();
} catch (Exception e) {
Log.d(TAG, "buyItem Exception: " + e);
}
}
public void onIncreaseBucketSpeedButtonClicked() {
if (!mHelper.subscriptionsSupported()) {
complain("Subscriptions not supported on your device yet. Sorry!");
return;
}
Log.d(TAG, "Launching purchase flow for increasing bucket speed.");
try {
new Thread(new Runnable() {
#Override
public void run() {
mHelper.launchPurchaseFlow(AndroidLauncher.this, SKU_Increase_Bucket_Speed, RC_REQUEST, mPurchaseFinishedListener, payloadString);
}
}).start();
} catch (Exception e) {
Log.d(TAG, "buyItem Exception: " + e);
}
}
public void onIncreaseBucketSizeButtonClicked() {
if (!mHelper.subscriptionsSupported()) {
complain("Subscriptions not supported on your device yet. Sorry!");
return;
}
Log.d(TAG, "Launching purchase flow for increasing bucket speed.");
try {
new Thread(new Runnable() {
#Override
public void run() {
mHelper.launchPurchaseFlow(AndroidLauncher.this, SKU_Increase_Bucket_Size, RC_REQUEST, mPurchaseFinishedListener, payloadString);
}
}).start();
} catch (Exception e) {
Log.d(TAG, "buyItem Exception: " + e);
}
}
public void onAdsFreeGameButtonClicked() {
if (!mHelper.subscriptionsSupported()) {
complain("Subscriptions not supported on your device yet. Sorry!");
return;
}
Log.d(TAG, "Launching purchase flow for Ads Free Game.");
try {
new Thread(new Runnable() {
#Override
public void run() {
mHelper.launchPurchaseFlow(AndroidLauncher.this, SKU_Ads_Free_Game, RC_REQUEST, mPurchaseFinishedListener, payloadString);
}
}).start();
} catch (Exception e) {
Log.d(TAG, "buyItem Exception: " + e);
}
}
// 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()) {
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_1_Life)) {
Log.d(TAG, "Purchase is SKU_1_Life.");
}
if (purchase.getSku().equals(SKU_2_Life)) {
Log.d(TAG, "Purchase is SKU_2_Life.");
}
if (purchase.getSku().equals(SKU_2_Life)) {
Log.d(TAG, "Purchase is SKU_Increase_Bucket_Speed.");
}
if (purchase.getSku().equals(SKU_2_Life)) {
Log.d(TAG, "Purchase is SKU_Increase_Bucket_Size.");
}
if (purchase.getSku().equals(SKU_2_Life)) {
Log.d(TAG, "Purchase is SKU_Ads_Free_Game.");
}
}
};
I'm trying to use in app purchase, after I want to purchase my item, I get the following error in the result of " IabHelper.OnConsumeFinishedListener mConsumeFinishedListener "
my Item is not a subscription !
the result.getmessage is as following: "Items of type 'subs' can't be consumed. (response: -1010:Invalid consumption attempt)"
what shall I do ?
my full code:
public class BuyCoins extends Activity{
String TAG="TESTPURCHASE";
IabHelper mHelper;
static final String ITEM_100_SKU = "ir.e_rundev.brainwars.test";
static final String ITEM_500_SKU = "ir.e_rundev.brainwars.test2";
static final String ITEM_1000_SKU = "ir.e_rundev.brainwars.1000coins";
String ITEM_SKU="";
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.buy_coins);
Swarm.setActive(this);
String base64EncodedPublicKey =
"(I HAVE MY CODE HERE)";
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");
}
}
});
Button btn100 = (Button) findViewById(R.id.btn_Buy_100);
Button btn500 = (Button) findViewById(R.id.btn_Buy_500);
Button btn1000 = (Button) findViewById(R.id.btn_Buy_1000);
btn100.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
ITEM_SKU=ITEM_100_SKU;
buyCoin(view);
}
});
btn500.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
ITEM_SKU=ITEM_500_SKU;
buyCoin(view);
}
});
btn1000.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// ITEM_SKU=ITEM_1000_SKU;
// buyCoin(view);
}
});
}
public void buyCoin (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_100_SKU)) {
consumeItem();
Log.d(TAG, "Here"+purchase.getItemType());
} else if (purchase.getSku().equals(ITEM_500_SKU)) {
consumeItem();
} else if (purchase.getSku().equals(ITEM_1000_SKU)) {
consumeItem();
}
}
public void consumeItem() {
mHelper.queryInventoryAsync(mReceivedInventoryListener);
Log.d(TAG,"here 3");
}
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);
Log.d(TAG,"here 4"+inventory.getPurchase(ITEM_SKU).getItemType());
}
}
};
IabHelper.OnConsumeFinishedListener mConsumeFinishedListener =
new IabHelper.OnConsumeFinishedListener() {
public void onConsumeFinished(Purchase purchase,
IabResult result) {
if (result.isSuccess()) {
//clickButton.setEnabled(true);
// Log.d(TAG,"SUCCESSFUL");
purchaseFinished(ITEM_SKU);
Log.d(TAG,"here2");
SharedPreferences settings = getSharedPreferences("MYSETTINGS", 0);
SharedPreferences.Editor editor = settings.edit();
editor.putBoolean("isPremium", true);
editor.apply();
Adad.setDisabled(true);
} else {
// handle error
Log.d(TAG,"here 5");
Log.d(TAG,result.getMessage());
Log.d(TAG,purchase.toString());
}
}
};
};
#Override
public void onDestroy() {
super.onDestroy();
if (mHelper != null) {
mHelper.dispose();
mHelper = null;
}
}
public void onResume() {
super.onResume();
Swarm.setActive(this);
TextView tvCoins = (TextView) findViewById(R.id.tv_Buy_Coins);
GameInventory gameInventory = new GameInventory(getApplicationContext());
tvCoins.setText(String.valueOf(gameInventory.getCoinsCount()));
}
public void onPause() {
super.onPause();
Swarm.setInactive(this);
}
private void purchaseFinished(String SKU){
GameInventory gameInventory = new GameInventory(getApplicationContext());
if (SKU==ITEM_100_SKU) {
gameInventory.updateCoinsCount(gameInventory.getCoinsCount() + 100);
}else if(SKU==ITEM_500_SKU){
gameInventory.updateCoinsCount(gameInventory.getCoinsCount() + 520);
}else if (SKU==ITEM_1000_SKU){
gameInventory.updateCoinsCount(gameInventory.getCoinsCount() + 1100);
}
ITEM_SKU = "";
}
}
Hey I'm also working on InApp Purchase since 10 days and I've successfully integrated in my existing app and ready to make it live. Initially when i had started doing this I've downloaded google InApp Billing Example called "Trivial Drive" from here.
But it didn't help me much as it has lots of issues and bugs, So I've decided do it on my own from scratch using new v3 api which you can find here. This tutorial has clear explanation that would help you and also if you have time, see this youtube video where google employee had explained clearly how to integrate it.
Also if you want quick example, I've a sample app which you can download from here.
The following video also explains how to integrate InApp Purchase. Please go through it.
https://www.youtube.com/watch?v=-h2ESH71hAI
Thank you
I'm going to implement the Android In-App Billing V3 using IabHelper (trivialdrivesample).
I made the game with cocos2d-x.
In the google developer console there are already ten in-app products.
They almost work well, But some of the items don't work. There is no shown the In-App bundle activity.
I don't know why some work well, some don't work.
It is very strange.
Here is some my code block.
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()) {
JSONObject prmsToSend = null;
String jsonStr = "{\"Message\":\"Failed 1\"}";
try {
prmsToSend = new JSONObject(jsonStr);
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
AndroidNDKHelper.SendMessageWithParameters("failTrans", prmsToSend);
return;
}
Log.d(TAG, "Purchase successful.");
// bought 1/4 tank of gas. So consume it.
Log.d(TAG, "Purchase is gas. Starting gas consumption.");
mHelper.consumeAsync(purchase, mConsumeFinishedListener);
}
};
// 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);
// if we were disposed of in the meantime, quit.
if (mHelper == null) return;
// 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
Log.d(TAG, "Consumption successful. Provisioning.");
}
else {
}
AndroidNDKHelper.SendMessageWithParameters("completeTrans", null);
Log.d(TAG, "End consumption flow.");
}
};
// 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.");
// Have we been disposed of in the meantime? If so, quit.
if (mHelper == null) return;
// Is it a failure?
if (result.isFailure()) {
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().
*/
Log.d(TAG, "Initial inventory query finished; enabling main UI.");
}
};
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
myAndroidContext = EasyAsPi.getContext();
//
AndroidNDKHelper.SetNDKReciever(this);
// Try to get your hash key
try {
PackageInfo info = getPackageManager().getPackageInfo("co.easyaspi.piguys", PackageManager.GET_SIGNATURES);
for (Signature signature : info.signatures) {
MessageDigest md = MessageDigest.getInstance("SHA");
md.update(signature.toByteArray());
Log.e("MY KEY HASH:", Base64.encodeToString(md.digest(), Base64.DEFAULT));
System.out.println("MY KEY HASH:"+ Base64.encodeToString(md.digest(), Base64.DEFAULT));
}
}
catch (NameNotFoundException e)
{
e.printStackTrace();
}
catch (NoSuchAlgorithmException e)
{
e.printStackTrace();
}
// Initialize EziSocial Plugin Manager for Android.
// ToDo Make sure you add the Facebook App ID in strings.xml file.
EziSocialManager.initWithActivity(this,
this.getApplicationContext().getString(R.string.app_id),
false, // Set to see ezisocial plugin logs
savedInstanceState
);
String base64EncodedPublicKey =
"My 64bit key";
mHelper = new IabHelper(this, 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.
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);
}
});
}
#Override
protected void onResume()
{
super.onResume();
EziSocialManager.applicationResumed();
}
#Override
public void onPause()
{
super.onPause();
EziSocialManager.applicationPaused();
}
#Override
public void onDestroy()
{
super.onDestroy();
EziSocialManager.applicationDestroyed();
Log.d(TAG, "Destroying helper.");
if (mHelper != null) {
mHelper.dispose();
mHelper = null;
}
}
public void requestProductData(final JSONObject prms)
{
Log.v("requestProductData", "Passed params are : " + prms.toString());
String param = null;
try {
param = prms.getString("item");
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String item = null;
if(param.equals("1"))
{
item = strItem1;
}
else if(param.equals("2"))
{
item = strItem2;
}
else if(param.equals("3"))
{
item = strItem3;
}
else if(param.equals("4"))
{
item = strItem4;
}
else if(param.equals("5"))
{
item = strItem5;
}
else if(param.equals("6"))
{
item = strItem6;
}
else if(param.equals("7"))
{
item = strItem7;
}
else if(param.equals("8"))
{
item = strItem8;
}
else if(param.equals("9"))
{
item = strItem9;
}
else if(param.equals("10"))
{
item = strItem10;
}
else if(param.equals("11"))
{
item = strItem11;
}
else if(param.equals("12"))
{
item = strItem12;
}
Log.d(TAG, "Buy gas button clicked.");
Log.d(TAG, "Launching purchase flow for gas.");
strItem = item;
mHelper.launchPurchaseFlow(this, item, 10001,
mPurchaseFinishedListener, "mypurchasetoken");
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
EziSocialManager.onActivityResult(requestCode, resultCode, data);
Log.d(TAG, "onActivityResult(" + requestCode + "," + resultCode + "," + data);
if (mHelper == null) return;
// 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.");
}
}
As I have suffered from in-app billing version 3. It looks like complicated solution provided by google if you want to create test app for version 3.
I have found best library on github.
https://github.com/anjlab/android-inapp-billing-v3
You can achieve simplicity using it.