I am working on an app which has IAB, and that when the purchase is finished through the OnConsumeFinishedListener, it will save through SharePreference that the user has paid for the additional function (the function wont be depleted).
Users are able to buy again for the same product.
Code:
// 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 (mHelper == null) return;
if (result.isSuccess())
{
Toast.makeText(InAppBillingActivity.this, "Thank you!!", Toast.LENGTH_SHORT).show();
if ((purchase.getSku().equals(ITEM_SKU10))) {countQ(10);}
buy10Button.setEnabled(true);
}
else
{
// handle error
}
Log.d(TAG, "End consumption flow.");
}
};
public void countQ(int Q)
{
SharedPreferences settings = this.getSharedPreferences("MyApp",0);
if (Q==10)
{
SharedPreferences settings1 = this.getSharedPreferences("MyApp",0);
SharedPreferences.Editor e1 = settings1.edit();
e1.putBoolean("userpaid", true);
e1.commit();
}
Question:
I see that users are able to cancel their purchase. In that way, saving through Sharepreference that the user has paid for additional functions will have loophole? The user can first purchase to make the sharepreference to become true and then cancel the purchase order?
In that way, how can be the process be improved? Checking the inventory seem not possible as the additional function is consumed in the IAB process.
It seems a little odd that you have a non-consumable item that can be purchased multiple times. The idea is generally that an item that can be purchased multiple times is consumable.
To answer your question IAB purchases cannot be cancelled like an app purchase can, the 15 minute refund window is removed and the purchase is non-refundable unless they contact you and you decide to issue a refund manually.
Related
I am using Google's In-App Billing for my Android app.
I used the IabHelper class from Google's how to, as their billing seems extremely complicated.
My issue is I want to know if the purchase is successful or not. I think I'm following the process correctly, but in my logs I see a lot of users that get the upgrade, but whose purchase never shows up in my Google Play payments account. (i.e. they get the upgrade for free).
I'm logging the GP order ids, sometimes its a number like,
GPA.1234-5678-9123-1234
But sometimes its like,
1234567891234.1234567891234
Normally I think its the non GPA orders that don't get charged.
Also I think you can put an order through, then cancel it, and still get the upgrade?
How do you ensure the user really paid?
Code:
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result, final Purchase purchase) {
if (result.isFailure()) {
showMessage("Google Billing Purchase Error");
return;
} else if (purchase.getSku().equals(sku)) {
IabHelper.QueryInventoryFinishedListener mReceivedInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
if (result.isFailure()) {
showMessage("Google Billing Error");
return;
} else {
if (inventory.hasPurchase(sku)) {
showMessage("Thank you for upgrading");
grantUpgrade();
// ** This line gets call, but no payment occurs.
}
}
}
};
mHelper.queryInventoryAsync(mReceivedInventoryListener);
}
}
};
mHelper.launchPurchaseFlow(this, sku, 10001, mPurchaseFinishedListener, "");
*** updated to check "inventory.hasPurchase(sku)" but still see users who get the upgrade but don't pay.
** maybe the users are using Freedom hack? Anyway to prevent this?
if (result.isFailure()) {
//If the user aborts or any other problems it will jump here
}
else {
//The user purchased some item, check out which it is
mIsPremium = inventory.hasPurchase(SKU_ANY_ITEM);
}
So concerning your question, this code already verify whether the user really purchased the item !
Purchase premiumPurchase = inventory.getPurchase(SKU);
boolean mIsPremium = (premiumPurchase != null
&& verifyDeveloperPayload(premiumPurchase));
if(mIsPremium){
...
}
The Google Play Store keeps track of purchases for you, so you shouldn't assume that just because a purchase was successful, the item will stay purchased. It's possible for a user to get a refund for a purchase. For this reason, you need to query the user's inventory every time you launch and adjust your grants appropriately. You would need to do this check anyways in order to support users that expect to have the grant when they switch to a new device or uninstall/reinstall the app.
I am testing my app with a managed item that I purchased.
Then I wanted to try to purchase the product with the same id again (with purchase flow). I therefore have cleared all play store data. After that I was able to purchase the product again.
Now I am wondering that if a user cleans the play store data accidentally (or looses his/her purchase for any other reason), will he/she then loose his/her purchase?
I didn't expect that behavior, because I thought that the purchase information is stored on google's remote servers.
Code example:
IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
if (mHelper == null)
return;
if (result.isFailure()) {
// handle error here
return;
} else {
if (inventory.hasPurchase(PremiumUtils.SKU_AD_FREE)){
// User paid to remove the Ads - so hide 'em
hideAd();
}
else{
// Free user - annoy him with ads ;)
showAd();
}
return;
}
}
};
I need to add a subscription support to my application, so that the user can buy a subscription for a year of service.
I just have created the subscription on Google Developer Console.
My problem is: I don't have any idea how I can check the user subscription.
I'm working to do this:
When my app is started by the user, if there is network, the app contacts the Play Store and checks if the user has bought the subscription and the payment date. These data are always saved on locale file, so if there is no network the app will use the local data for checking;
If the user has bought the subscription I check if it's been over a year. In fact I have read on internet that Play Store provides only the payment date and not the finish subscription date;
If the checking is true the app will work in Premium mode and not in Standard mode;
Now the problems:
How do I check the purchase? Can I use hasPurchase() method like in normal in-app?
If I need to use hasPurchase() on point 1) does this method return False if the user don't renew the subscription after a year?
How can I know the purchase date?
I copy a piece of code, this is a valid code to check normal in-app and I'd like to edit it to use it in subscription checking:
private void checkForPremium() {
final IabHelper buyHelper = new IabHelper(this, "MYKEY");
// initialize the InApp Billing system
buyHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
#Override
public void onIabSetupFinished(IabResult result) {
if (!result.isSuccess()) {
buyHelper.dispose();
Log.e("MYAPP", "Error: " + result);
return;
}
// Get a list of all products the user owns
buyHelper.queryInventoryAsync(new IabHelper.QueryInventoryFinishedListener() {
#Override
public void onQueryInventoryFinished(IabResult result, Inventory inv) {
if (result.isFailure()) {
buyHelper.dispose();
Log.e("MYAPP", "Error: " + result);
} else {
boolean isPremium = inv.hasPurchase("MYSKU");
inv.getSkuDetails().
buyHelper.dispose();
// Forward to the currect activity depending on premium / demo mode
if (isPremium) {
if(menu != null){
MenuItem item = menu.findItem(R.id.action_premium);
item.setVisible(false);
}
Log.w("MYAPP", "PREMIUM");
} else {
if(menu != null){
MenuItem item = menu.findItem(R.id.action_premium);
item.setVisible(true);
}
Log.w("MYAPP", "NO PREMIUM");
}
}
}
});
}
});
}
After reading some other posts on the subject I have not been able to find a solution to this issue. When a user buys a product (managed in my case) and then he/her cancels the purchase how can my game get notified about it? It seems the only solution would be query the product states every time the user runs the application but this will make really hard to revert all purchases he have made for example if he bought virtual currency.
How do you handle this?.
Thanks in advance.
You can consume item using the following code
mHelper.consumeAsync(inventory.getPurchase("item_id"),
mConsumeFinishedListener);
and check consumption succeed or not here :
IabHelper.OnConsumeFinishedListener mConsumeFinishedListener =
new IabHelper.OnConsumeFinishedListener() {
public void onConsumeFinished(Purchase purchase, IabResult result) {
if (result.isSuccess()) {
// provision the in-app purchase to the user
// you can mark here that item has been consumed so you can purchase it again
}
else {
// handle error
}
}
};
Since one year I develop android apps. I thought it’s time now to do the next step and implement the billing system. Before I implement this new feature in my main app I thought it’s better to test it.
Last week i developed a test app the same app (with v3 billing system) like my main app (without v3 billing system). I published it and installed on my Samsung Note via Google Play. I was being able to buy and to subscribe. And it worked well.
So I deactivated the test app, copied the whole billing methods in my main app, changed the Base64 coded public RSA key and published it.
After installation and start, the app crashed every time. And the reason is only the billing system because the recent version works without the billing system well. Of course I could replace my main app with a new app which do the same like my main app, but I would lose all my user, my statistics and comments until now.
Do you know why my app cannot install the billing system with the RSA key?
Which other reasons could be responsible for this Situation?
what happens in onCreate:
mHelper = new IabHelper(this, base64EncodedPublicKey);
// enable debug logging (for a production application, you should set this to false).
mHelper.enableDebugLogging(false);
// Start setup. This is asynchronous and the specified listener
// will be called once setup completes.
Log.d(TAG, "Starting setup.");
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);
}
});
and what happens in line 960:
public 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.");
// Purchase premium monthly
Purchase purchase = inventory.getPurchase(Constants.PREMIUM);
if(purchase.getPurchaseState() == Purchase.PURCHASED_SUCCESSFULLY && verifyDeveloperPayload(purchase)){
isPremium = true;
}else{
isPremium = false;
updatePremium(purchase.getPurchaseState());
}
Log.d(TAG, "User " + (isPremium ? "HAS" : "DOES NOT HAVE") + " premium surebets for 32 days.");
Log.d(TAG, "Initial inventory query finished; enabling main UI.");
}
};