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.
Related
I am implementing IAP in my app. One is for removing ad and other is for adding more puzzle. I was testing that on my device but came across an issue. After buying an item, I am getting response code "Item already owned" but it is not showing in purchase list.
I am setting up my billing client like this,
private void setUpBillingClient(){
mBillingClient = BillingClient.newBuilder(this).setListener(this).build();
mBillingClient.startConnection(new BillingClientStateListener() {
#Override
public void onBillingSetupFinished(#BillingClient.BillingResponse int billingResponseCode) {
if (billingResponseCode == BillingClient.BillingResponse.OK) {
List skuList = new ArrayList<>();
skuList.add(ITEM_SKU_MORE_PUZZLE);
skuList.add(ITEM_SKU_REMOVE_AD);
SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
params.setSkusList(skuList).setType(BillingClient.SkuType.INAPP);
mBillingClient.querySkuDetailsAsync(params.build(),
new SkuDetailsResponseListener() {
#Override
public void onSkuDetailsResponse(int responseCode, List skuDetailsList) {
// Process the result.
if (responseCode == BillingClient.BillingResponse.OK
&& skuDetailsList != null) {
for (Object skuDetailsObject : skuDetailsList) {
SkuDetails skuDetails = (SkuDetails) skuDetailsObject;
String sku = skuDetails.getSku();
String price = skuDetails.getPrice();
if (ITEM_SKU_MORE_PUZZLE.equals(sku)) {
btnMorePuzzle.setText(price);
}
else if(ITEM_SKU_REMOVE_AD.equals(sku)) {
btnRemoveAd.setText(price);
}
}
}
}
});
}
}
#Override
public void onBillingServiceDisconnected() {
//Toast.makeText(getApplicationContext(), getResources().getString(R.string.billing_connection_failure), Toast.LENGTH_SHORT);
}
});
queryPurchases();
queryPrefPurchases();
}
First question, why is Billing response is OK here when I have already purchased the item. I don't want to set text of button as price, which is getting set from this response after product is bought.
This is my Onpurchase implementation,
#Override
public void onPurchasesUpdated(int responseCode, #Nullable List<Purchase> purchases) {
if (responseCode == BillingClient.BillingResponse.OK && purchases != null) {
for (Purchase purchase : purchases) {
if (purchase.getSku().equals(ITEM_SKU_REMOVE_AD)) {
mSharedPreferences.edit().putBoolean("ad_free", true).commit();
btnRemoveAd.setText("Done");
btnRemoveAd.setEnabled(false);
}
else if(purchase.getSku().equals(ITEM_SKU_MORE_PUZZLE)){
mSharedPreferences.edit().putBoolean("more_puzzle", true).commit();
btnMorePuzzle.setText("Done");
btnMorePuzzle.setEnabled(false);
}
}
} else if (responseCode == BillingClient.BillingResponse.ITEM_ALREADY_OWNED ) {
// I am getting response "Item already owned" here for item bought but purchase list here is empty
// so i can't do anything for purchased item
}
}
Second question, here I am getting response that my item is already bought but still list is empty.
How to implement it properly?
If someone already bought a product then button should be disabled.
Another doubt is while testing do I have to wait for 1-2 hrs to get that item refunded from playstore to test again or is there any other method.
I am following this code for in-app implementation.
https://github.com/patpatchpatrick/Streakr/
There is only one thing to point out here, that you are querying only the SKU type INAPP items only not the SUBS. I think the type of product you are providing comes under subscriptions not under in-app products which are used to be consumed. That is why your query is empty.
I found a mistake in your onPurchasesUpdated, conditions are set wrongly.
It should go something like this,
if(BillingResponse.OK && purchases != null) {
// update records
} else if(BillingResponse.ITEM_NOT_OWNED){ //this condition was missing
// update records if required or ask to buy
} else if(BillingResponse.ITEM_ALREADY_OWNED ){ // update records}
and i also suggest to update db for this.
I have a really strange problem with Google In-app billing API.
I want to get the details of my available subscriptions to show their localised price on the UI. I'm using the IabHelper to query the details of the SKUs I specify, but the result contains no products, but after I purchase the subscription it's working, even if I cancel the subscription then and reinstall the app.
Here is my code:
List<String> additionalSkus = new ArrayList<>();
for (Subscription subscription : subscriptions) {
additionalSkus.add(subscription.getSku());
}
iabHelper.queryInventoryAsync(true, additionalSkus, new IabHelper.QueryInventoryFinishedListener() {
#Override
public void onQueryInventoryFinished(IabResult result, Inventory inv) {
if (iabHelper == null || result.isFailure()) {
Log.i("subs", result.getMessage());
}
for (Subscription subscription : subscriptions) {
SkuDetails skuDetails = inv.getSkuDetails(subscription.getSku());
if (skuDetails != null) {
subscription.setLocalizedPrice(skuDetails.getPrice());
Log.i("subs", subscription.getLocalizedPrice());
}
}
}
});
I have everything set up right about the In-app billing process. My products are active, I have a published beta APK, I can even get the details of my managed products, and purchase managed products and subscriptions, but this isn't working.
If you're using the latest version of IabHelper class, you should note that the function arguments relevant to additional skus to query are split to moreItemSkus (PRODUCT skus) and moreSubsSkus (SUBSCRIPTIONS skus).
public void queryInventoryAsync(final boolean querySkuDetails, final List<String> moreItemSkus,
final List<String> moreSubsSkus, final QueryInventoryFinishedListener listener)
In your case, your call should be
iabHelper.queryInventoryAsync(true, null, additionalSkus, new IabHelper.QueryInventoryFinishedListener() ...
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 currently trying to configure my app for in app purchase, but it just won't work. Every time I try to start the purchase flow it says "This version of the applications is not configured for billing through Google Play". I am quite sure that I walked through al necessary steps to make it work. I also tested SKU-Id "android.test.purchased", which works fine.
base64EncodedPublicKey is def. correct and exactly teh same like the code from the developer console
I did not forget <uses-permission android:name="com.android.vending.BILLING" /> in my manifest
I have applied the google account that I am using on my testing device as a tester account in the developer console
I am using the exactly same APK on my device an in the developer console. I installed it on my device via adb -d install
So any ideas what I could have done wrong?
This is my code:
(onCreate)
base64EncodedPublicKey
iabHelper = new IabHelper(this, base64EncodedPublicKey);
iabHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
#Override
public void onIabSetupFinished(IabResult result) {
if (!result.isSuccess()) {
// Oh noes, there was a problem.
Log.d(PURCHASE_TAG, "Problem setting up In-app Billing: " + result);
} else if (result.isSuccess()){
// Hooray, IAB is fully set up!
Log.d(PURCHASE_TAG, "Setup completed: " + result);
iabHelper.queryInventoryAsync(true, null, queryFinishedListener);
}
}
});
(onActivityResult)
if (iabHelper.handleActivityResult(requestCode, resultCode, data)) {
Log.d("TAG", "onActivityResult handled by IABUtil.");
return;
}
(Button click for starting burchase flow)
if(isPremium){
saveImageToGallery(imageState.image);
} else if (iabHelper != null) {
iabHelper.flagEndAsync();
purchaseItem(SKU_TEST);
(purchaseItem())
private void purchaseItem(String sku) {
iabHelper.launchPurchaseFlow(this, sku, 10001,
purchaseFinishedListener);
}
(listeners)
IabHelper.QueryInventoryFinishedListener
queryFinishedListener = new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result, Inventory inventory)
{
if (result.isFailure()) {
// handle error
return;
} else if (result.isSuccess()){
Log.d("$$$$$$$$$$$$$$$", "" + result);
}
}
};
IabHelper.OnIabPurchaseFinishedListener purchaseFinishedListener
= new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result, Purchase purchase)
{
if (result.isFailure()) {
Log.d("ERROR_TAG", "Error purchasing: " + result);
return;
}
else if (purchase.getSku().equals(SKU_TEST)) {
// give user access to premium content and update the UI
isPremium = purchase.getSku().equals(SKU_TEST);
}
}
};
I think you should check the key store you signed your apk. You must use same key store for apk that has uploaded to Google Play and test.
I hade the same problem, I fixed it by:
Go to play store developer console
Settings > Manage Testers, then add your another test email, not the one you use in publishing apps.
Again under Developer Console, Account Details (scroll down) > License Testing and add the emails of your users who will test your
app.
On your device, login in play store with one of the test emails 3 you allowed.
Launch you app and make in app purchase. There should be fixed.
more https://developer.android.com/google/play/billing/billing_testing.html
Summary:
)
I'm trying to implement in-app-billing within my app. In the Google Play Developer Console I declared a managed item. Buying this item works really fine. But now, when I refund or cancel the purchase in the google wallet merchant center, my app takes very long (more days) to recognize that the item is not longer owned.
I've already read lots of other articles about this problem and think one logical explanation is that the purchase is saved in the cache of the Google Play Store. Although I know that this question has asked often before, I ask here again:
Is it possible to clear the cache or does anyone know how to tell my app, when the purchase is not longer owned?
I'm thankful for any hint, that helps me to solve this problem :D
In addition, my code where I ask, if the item is purchased. I'm using in-app-billing v3.
public boolean hasUserBoughtItem() {
try {
Bundle ownedItems = mService.getPurchases(mUsedAPI, mContext.getPackageName(),
mPurchaseType, null);
int response = ownedItems.getInt("RESPONSE_CODE");
if(response == 0) {
ArrayList<String> ownedSkus = ownedItems.getStringArrayList("INAPP_PURCHASE_ITEM_LIST");
if(!ownedSkus.isEmpty()) {
for(String sku : ownedSkus) {
if(sku.equals(Constants.ITEM_ID_ALL_RECIPES)) {
return true;
}
}
}
return false;
}
} catch(Exception e) {
e.printStackTrace();
}
return false;
}
The IAP purchase inventory isn't cached by the Play Store at all and should be queried regularly in your activities. It should only take approximately 15-30mins for order cancellations to propagate.
Are you using the IABHelper as per the sample app to connect to Google Play?
IabHelper.QueryInventoryFinishedListener mGotInventoryListener
= new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result,
Inventory inventory) {
if (result.isFailure()) {
// handle error here
}
else {
// does the user have the premium upgrade?
mIsPremium = inventory.hasPurchase(SKU_PREMIUM);
// update UI accordingly
}
}
};