I have implemented in-app purchased in my application and my product type is Managed and i am using API version 3.
When i make purchase from my credit card it is successfully done.
But the problem is if i uninstall my application and want to purchase this with same account it will charge me again?
According to Google rules of managed product type we only purchase the product once? But why is this happening ?
any one help me please?
here is my PurchaseActivity.java class
public abstract class PurchaseActivity extends BlundellActivity implements OnIabSetupFinishedListener, OnIabPurchaseFinishedListener {
private IabHelper billingHelper;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_purchase);
setResult(RESULT_CANCELED);
billingHelper = new IabHelper(this, AppProperties.BASE_64_KEY);
billingHelper.startSetup(this);
}
#Override
public void onIabSetupFinished(IabResult result) {
if (result.isSuccess()) {
Log.d("In-app Billing set up" + result);
dealWithIabSetupSuccess();
} else {
Log.d("Problem setting up In-app Billing: " + result);
dealWithIabSetupFailure();
}
}
protected abstract void dealWithIabSetupSuccess();
protected abstract void dealWithIabSetupFailure();
protected void purchaseItem(String sku) {
billingHelper.launchPurchaseFlow(this, sku, 123, this);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
billingHelper.handleActivityResult(requestCode, resultCode, data);
}
*/
#Override
public void onIabPurchaseFinished(IabResult result, Purchase info) {
if (result.isFailure()) {
dealWithPurchaseFailed(result);
} else if (pmg.SKU.equals(info.getSku())) {
dealWithPurchaseSuccess(result, info);
}
finish();
}
protected void dealWithPurchaseFailed(IabResult result) {
Log.d("Error purchasing: " + result);
}
protected void dealWithPurchaseSuccess(IabResult result, Purchase info) {
Log.d("Item purchased: " + result);
// DEBUG XXX
// We consume the item straight away so we can test multiple purchases
billingHelper.consumeAsync(info, null);
// END DEBUG
}
#Override
protected void onDestroy() {
disposeBillingHelper();
super.onDestroy();
}
private void disposeBillingHelper() {
if (billingHelper != null) {
billingHelper.dispose();
}
billingHelper = null;
}
}
This is working as intended - in your code you are consuming the in-app purchase immediately, which means you can then purchase it again:
protected void dealWithPurchaseSuccess(IabResult result, Purchase info) {
Log.d("Item purchased: " + result);
// DEBUG XXX
// We consume the item straight away so we can test multiple purchases
billingHelper.consumeAsync(info, null);
// END DEBUG
}
There's nothing that says you can't purchase a managed product more than once. What you can't do is purchase a managed product before a previous purchase of the same managed item has been consumed. So this is working exactly as intended, and if you remove that call to consumeAsync, you'll see that you can't purchase it again.
Sample use case:
Imagine some game where you can purchase extra lives. First, the user would purchase the extra lives (a managed in app product), your game (client or server) would then add those lives to the user's profile, for example, and assuming that was successful, you'd tell Google Play that the purchase has been consumed.
This is important in order to handle error cases - for example say the user's device dies in between the initial purchase and the addition of lives to the user's profile. Your app can then, the next time it's launched, try again to add those lives, and consume the purchase on success. And, obviously you wouldn't want the user trying to purchase even more lives before you successfully grant them - which is why you can't purchase a managed product twice before it's been consumed.
Related
I have kept a donate tab and want to let the users buy the items over and over again. I have implemented a code but it lets the user buy the specific item only once. I have used managed products in play console for products.
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(billingClient.isReady()){
SkuDetailsParams params=SkuDetailsParams.newBuilder()
.setSkusList(Arrays.asList("purchase_aaa","purchase_bbb","purchase_ccc","purchase_ddd"))
.setType(BillingClient.SkuType.INAPP).build();
billingClient.querySkuDetailsAsync(params, new SkuDetailsResponseListener() {
#Override
public void onSkuDetailsResponse(int responseCode, List<SkuDetails> skuDetailsList) {
if(responseCode==BillingClient.BillingResponse.OK)
{
loadProductToRecyclerView(skuDetailsList);
}
else{
Toast.makeText(Donate.this, "Cannot query product", Toast.LENGTH_SHORT).show();
}
}
});
}
else
{
Toast.makeText(Donate.this, "Not ready", Toast.LENGTH_SHORT).show();
}
}
});
#Override
public void onPurchasesUpdated(int responseCode, #Nullable List<Purchase> purchases) {
if(purchases!=null){
Toast.makeText(this, "Purchased"+purchases.size(), Toast.LENGTH_SHORT).show();
}
}
That's by design and cannot be changed, in-app managed products can only be purchased once.
If you want the user who has paid more to have more features enabled, you will have to create as many in-app managed products as levels exist.
If it is a game in which, for example, the user is consuming items then when he no longer has any, you consume the in-app product so he can buy it again.
Or you can also consume the product immediately after the purchase and keep track of how many he has purchased through your own means, an own server or perhaps through firebase, but this already means that you will have to implement a user authentication system for your app.
Consume a purchase:
ConsumeResponseListener consumeListener = new ConsumeResponseListener() {
#Override
public void onConsumeResponse(BillingResult billingResult, String purchaseToken) {
}
};
String token = purchase.getPurchaseToken();
ConsumeParams consumeParams = ConsumeParams.newBuilder().setPurchaseToken(token).build();
billingClient.consumeAsync(consumeParams, consumeListener);
I'm making an Android application that will include a subscription using in-app billing from Google (https://developer.android.com/google/play/billing/index.html).
The aim is to let user to have 1 subscription per device.
I know that Google limit subscription for 1 google mail account but not for 1 device, that's why I made my own restriction using a server with a database.
So I made a pool of 10 subscriptions in the developer console product list and I want that when a device subscribe for the 1st subscription, a second device (using the same google account) will subscribe for the next subscription...
But when I want the second device chose automatically the next subscription not even bought on the account, it is saying to me "Product already own". The problem is that the current inventory is not refreshed.
I'm using IabHelper and here is the part of code where I'm trying to buy the next subscription available.
public void initIab() throws IabHelper.IabAsyncInProgressException {
iabHelper = new IabHelper(this, AppConfig.APPLICATION_KEY);
iabHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) throws IabHelper.IabAsyncInProgressException {
if (result.isSuccess()) {
iabHelper.queryInventoryAsync(iabInventoryListener());
billingServiceReady = true;
}
}
});
}
private IabHelper.QueryInventoryFinishedListener iabInventoryListener() {
return new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
if (iabHelper == null) {
return;
}
if (!result.isSuccess()) {
return;
}
String[] PREMIUM_KEYS = {"premium1","premium2","premium3","premium4","premium5","premium6","premium7","premium8","premium9","premium10"};
Purchase premiumPurchase = null;
String premiumKey;
boolean payload = false;
for(String key : PREMIUM_KEYS) {
if (inventory.hasPurchase(key)) {
premiumPurchase = inventory.getPurchase(key);
premiumKey = key;
payload = verifyDeveloperPayload(hasPurchase,premiumKey);
if(payload)
break;
}
}
session.setPremium(achatPremium != null && payload);
}
};
}
Please could you help me to find a solution ?
Sorry for my bad english.
Thank you.
I have an Android app that launches the purchase flow from a dialog within a Fragment.
The purchase flow is launched like:
config.getIabHelper().launchPurchaseFlow(
faActivity,
sku,
Constantes.SOLICITUD_COMPRA,
faActivity.mPurchaseFinishedListener,
purchaseIdentifier);
Where:
config.getIabHelper() returns an instance of the IabHelper class as implemented in the google documentation (the same used in the trivialgame example)
faActivity is the parent activity of the fragment
Constantes.SOLICITUD_COMPRA is a positive integer
mPurchaseFinishedListener is the listener, which is implemented in the parent activity.
So the current flow should work like:
Make a purchase.
Acknowledge that the purchase was bought.
Update the UI so that it enables the user to use the purchased item rather than to buy it.
However it works like:
Make a purchase.
The application freezes and stops.
When the application is relaunched, the inventory is queried and the UI gets updated.
It seems like mPurchasedListener is never called.
To make it more weird, everything seems to work fine with test responses. I am testing the app in alpha with real responses and that is where the trouble appears.
Any ideas?
i've had this issue before my self and there are multiple things that could be causing this issue.
Firstly, please make sure that you've done the following for aplha stage testing and that you've added all the required permissions and aidl files.
1) Are you using a version of the app you've put on your device through android studio? If so, this could be your issue. Usually you'll receive a message notifying you that the version of the app you're using isn't compatible with in-app purchases. Alpha stage apps are actually downloaded from the google play store. You should have set up a google group that has access to a specific link that will allow you to open the google play store and download your alpha stage app.
2) Are you connected to the internet? Obviously launching a purchase flow with a real SKU will require an internet connection to the google play servers.
3) Did you set up this in app product up as a managed product? For simplicity, i highly recommend doing so.
if you've correctly set up everything as I mentioned above and are still having a problem, then likely it's an issue with how you setup and attempt to use your purchase flow.
Here's the steps i took to launching a purchase flow from within a fragment
Instead of setting up the IAB Helper in the fragment, set it up in the faActivity Class. We will then call the purchase flow method within the faActivity class from within the fragment in which you're viewing via Dialog Box.
This is how i set up in app purchases :
faActivity.java
IabHelper mHelper;
In the oncreate method :
String base64EncodedPublicKey = "your in app purchase key";
mHelper = new IabHelper(this, base64EncodedPublicKey);
mHelper.startSetup(new
IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result)
{}});
Add these methods as well :
#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()) {
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) {
/*The purchase was successfully consumed, now update the
//ui/award the user with their purchase (I suggest storing the fact that
they're premium or whatever within the shared prefrences of your app)*/
}};
String ITEM_SKU = "";
//Launching the purchase Flow
public void makeThePurchase() {
//Assign the SKU Name of your managed product
ITEM_SKU="premiumMembership";
mHelper.launchPurchaseFlow(this, ITEM_SKU, 10000,
mPurchaseFinishedListener, "mypurchasetoken");
}
Now in your fragment, use this code to show a dialogBox asking the user if they wish to purchase the product and launch the purchase flow if they wish to do so
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
LayoutInflater inflater = getActivity().getLayoutInflater();
builder.setView(inflater.inflate(R.layout.dialog_signin, null))
.setPositiveButton("Purchase", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int id) {
((faActivity)getActivity()).makeThePurchase();
}
})
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.dismiss();
}
});
builder.setTitle("Purchase");
builder.setMessage("Purchase premium membership?");
builder.create().show();
And that is how i managed to launch a purchase flow from within a fragment. I'm sure there is a better way to do so, but when setting up the purchase flow from within the fragment, i always recieved issues as well when trying to launch a purchase.
Hopefully this solved your issue!
Good Luck!!! :)
NOTE: The code i've listed above is setup to consume the purchase (meaning that the fact that they own it won't show up when querying the inventory) Like i said above although, if you're not concerned about the users having to repurchase their items/premium purchases in the occurance of them getting a new device or uninstalling your app, then just keep track of what they've purchased and consumed within the shared preferences by calling this code below from within the onConsumePurchasedFinished listener :
SharedPreferences pref = getActivity().getApplicationContext().getSharedPreferences("MyPref", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = pref.edit();
editor.putBoolean("hasBoughtPremium",true);
editor.apply();
Then when reloading the app, check to see if they own the premium item :
SharedPreferences pref = getActivity().getApplicationContext().getSharedPreferences("MyPref", Context.MODE_PRIVATE);
Boolean isPremium = pref.getBoolean("hasBoughtPremium",false);
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");
}
}
}
});
}
});
}
I am trying to set up in-app-billing and test using a static product ID in Google Play.
Am following developer tutorial here.
When the launhPurcahseFlow method is called on the labHelper object I get the exception:
java.lang.IllegalStateException: IAB helper is not set up. Can't
perform operation: launchPurchaseFlow at
com.android.vending.billing.IabHelper.checkSetupDone(IabHelper.java:782)
Have been searching for hours and can't find a solution that works.
Any input appreciated.
My code is:
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
// compute your public key and store it in base64EncodedPublicKey
mHelper = new IabHelper(this, base64EncodedPublicKey);
// enable debug logging (for a production application, you should set this to false).
mHelper.enableDebugLogging(true);
//perform service binding to Google Bill ser and return ny errors with IabResult object and listener
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
Log.d(TAG, "Setup finished.");
if (!result.isSuccess()) {
// Oh noes, there was a problem.
alert("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);
}
});
//ILLEGALSTAEEXCEPTION THROWN HERE
mHelper.launchPurchaseFlow(this, testProduct, RC_REQUEST,
new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result, Purchase purchase)
{
if (result.isFailure()) {
Log.d(TAG, "Error purchasing: " + result);
return;
}
else if (purchase.getSku().equals(testProduct)) {
// give user access to premium content and update the UI
//set the purchaesd booean to true
//when purcajsed add this code
editor.putBoolean("purchased", true);
editor.commit();
Toast.makeText(getApplicationContext(), "ADD FREE VERSION PURCAHSED!!!" +
" Details OrderID: "+purchase.getOrderId() +" Payload ID: "+purchase.mDeveloperPayload, Toast.LENGTH_LONG).show();
Log.d("CONNECT TO GOOGLE BILL", "ITEM PURCAHSED! : "+purchased);
}
}
}, "diveAppPurchase");
}//onCreate
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent 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.");
}
}
This be useful to those like me, who are dealing with in app purchasing first time.
The set up call is asynchronous, therefore this must complete before the launchPurchaseFlow is called. So i disabled a 'Purchase' button to make the launchPurchaseFlow call, which is enabled once the set up call is done. Works fine:
Set up call
//perform service binding to Google Bill ser and return ny errors with IabResult object and listener
mHelper.startSetup(...)
if successfully then enable button and call mHelper.launchPurchaseFlow(...) method
Also, when using the test product id provided by google you may note a signature exception is thrown during the launchPurchaseFlow with a subsequent result failure although the transaction is successful , this is apparently a known bug that google are aware of