I'm adding licensing to my app, under allow(), I start the activity. Under dontAllow, I display a dialog saying, not licensed, go to market. Under applicationError, I'm not sure.
I don't want my app to be inaccessible because of an error in code but if I start my activity there, then licensing could just be bypassed if data access is not available. How is everybody handling this?
LVL caches last successful license check status and calls allow() method even if there's no internet connection. But after several tries or after certain time it starts treating license as expired one and calls dontAllow(). Validity period and retries count comes from Android Market, so these values aren't hardcoded in LVL. You can get additional informatoin here: http://developer.android.com/guide/publishing/licensing.html#extras
Related
We are developing an app that should get data from the Moves app API.
When authorising our app on the mobile phone, we manage to do so successfully in the case in which we submit the PIN provided by the desktop link into the moves app:
https://api.moves-app.com/oauth/v1/authorize?response_type=code&client_id=<client_id>&scope=<scope>&state=<state>
In the case where I use the Mobile website / app link instead, this won't work:
moves://app/authorize?client_id=<client_id>&scope=activity%20location&state=<state>&redirect_uri=<redirect_uri>
Note: <redirect_uri>==encodeURIComponent("mymovesconnector:8081/callback").
The response we get from the Moves API is 400: {"error":"invalid_grant"}.
From the docs, it says that invalid_grant can happen if either:
the code in the request is not valid or
the code has expired (it’s valid for 5 minutes currently) or
the code has been revoked, because it was already used in an access token request (both successful and unsuccessful requests will revoke the code) or
you are missing the redirect_uri parameter when it’s required.
My checklist:
I guess this could have happened, but how/why should this code, provided over moves://, be different than the one provided through the https:// request? What control do I have over this?
not my case, since I'm using the code right away within less then a few seconds
if this might have happened, where could it happen other than through the redirect url?
I'm providing it always. It's always the same - also in the app settings on moves.
Can't figure out what I'm missing. Went through the docs many times, step by step. No success.
While writing down my question, I managed to figure out a solution to this problem.
The docs don't point out that response_type=code might also be used for the moves:// link. I tried to add this parameter, and it worked!
Note: this has been tested on a virtual android device only (Genymotion).
Am trying out in-app billing in my App for the first time. Am using the labHelper code (https://gist.github.com/yigit/4543005) from the TriviaDrive example in the samples folder in the Play Billing Services extras. When I tried with the test code that Google gives i.e. product code as 'android.test.purchased' (give here under 'Testing with static responses'), it worked fine. But now, I switched to using the test Google account (i.e. license testing, given under 'Setting up test accounts' here). I purchased the item successfully. But when I try to use a purchased item (it's a monthly subscription product), I get an error dialog saying 'You already own this item'. At the same time, in the logs, I see the message
05-02 17:10:36.599: D/Finsky(6396): 1 PurchaseFragment.handleError: Error: PurchaseError{type=3 subtype=3}
05-02 17:10:36.599: D/Finsky(6396): 1 PurchaseFragment.fail: Purchase failed: PurchaseError{type=3 subtype=3}
(response code 3 meaning 'billing unavailable')
But, if I dismiss the error dialog, I get response 7 (i.e. 'you already own this item').
Not sure why I keep getting 3 for an item that's already been purchased? Please help.
What I tried
I tried calling startSetup() and the listener OnIabSetupFinishedListener() in the Activity's onCreate(). Am getting response 0.
Then I tried labHelper class' 'queryInventoryAsync(mReceivedInventoryListener)' method and it also returns 3.
I tried consumption related methods from labHelper class
launchSubscriptionPurchaseFlow(this,
InAppBillingExportProductId,
10001, mPurchaseFinishedListener, "”);
mPurchaseFinishedListener returns the response 0 (i.e. success), but it immediately returns the error -1010 (IABHELPER_INVALID_CONSUMPTION)
Please help. Been struggling with this one issue for more than 3 days now!
IabHelper has a method called enableDebugLogging(...) that you can use (call it with true) to turn on pretty detailed logging for all IabHelper actions. If you can post a copy of the full log, it might be easier to figure out exactly what's going on.
But let me post a couple thoughts anyways based on what you wrote:
Just like normal managed products, subscription items can not be purchased again if they are already owned.
The way to check ownership is via queryInventoryAsync(...) (if inventory.getPurchase(sku) is not null, you own the item). Unfortunately this is not always 100% accurate and I haven't found a fix yet.
To "use" a managed product or subscription, simply have your application provide whatever service you sold if the above check tells you that the item is owned.
The big difference between managed products and subscriptions is the way that the user loses them again (i.e. can purchase the same sku for a second or third time):
Subscriptions expire automatically at the end of a pre-determined period if the user doesn't renew them,
while managed products need to actively be "consumed" by your app to make them available again.
So, if you try to purchase a managed product again before "consuming" it, you will get the "you already own this item"-error that you mentioned. Same goes for trying to purchase a subscription again that you already purchased and that hasn't expired yet.
The "IABHELPER_INVALID_CONSUMPTION" error is likely caused by the fact that subscriptions cannot be consumed, only managed products can. Subscriptions only expire (or you can refund them from the developer console).
I'm not sure where the "PurchaseError{type=3 subtype=3}"-error is coming from. That one might be easier to track down from the actual IabHelper debug output. If you can update your question with a full log, send me a comment and I can take a look at it and probably help you make sense of it if needed.
Let me know if you have any further questions. I hope this helps.
I've been setting up Android in app billing v3, using the IABHelper class, and following the example code provided by Google. I have it mostly working all the way through purchase (with signed apk and real credit card charge).
However, in the course of testing today I started to get a new error in my QueryInventoryFinishedListener from the queryInventoryAsync() method:
IABResult message: "Error refreshing inventory (querying prices of items)"
IABResult response: 5:Developer Error
Weird thing #1 is that this occurs after the onIabSetupFinished() callback returns (with the customary "Hooray" message). Weird thing #2 is that I can subsequently & successfully process an in app purchase (using the launchPurchaseFlow() method).
I found a patch here that addresses the same symptoms I'm experiencing, but it didn't work for me.
I've tried using different devices, using different gmail accounts, and building a new product from scratch. I even getting the error on earlier versions of my app that ran correctly (what?!).
My question is: Why can't I query the product inventory, even after IABHelper has confirmed the set up was successful? What could be causing this error, and how can I fix it?
Thank you for any insight.
UPDATE
I was able to get the inventory query transactions to work again by ditching the account I was testing with, and switching to a new account. No code change.
My tentative conclusion is that something got corrupted in the user account I was using (?). During testing, I had hit it pretty hard with a lot of purchases of different in-app products -- but I still need to find out what happened, and make sure this doesn't happen to any of my users.
Please let me know if you have any experience with this. Thanks!
We had the same problem in one of our apps under test mode. Later, we figured out that, we had to clear the Cache of the Google Play Store app.
So you can try this -
Go your device's Settings menu.
Go to Applications (may also be labeled Application Manager).
Tab over to All Applications.
Search for and open the Google Play Store app. Tap on it, application settings will show
Tap on Clear Data and Clear Cache.
Now go back to your app and try to load inventory. It solved our problem.
I found by trial and error that if you query more than 20 items at once, it will fail with this error.
I submitted a patch for IabHelper.java that splits the list of SKUs into packets of 20 items each and does the query.
You can grab it there: https://code.google.com/p/marketbilling/issues/detail?id=123
I have the same issue with this log :
"InAppBillingManager.getSkuDetails: Input Error: skusBundle array associated with key ITEM_ID_LIST cannot contain more than 20 items."
this note had mentioned in IInAppBillingService.aidl file, see the documentation of the method getSkuDetails(..) .
So you should make the same process for each 20 items every time
I face same error but my issue is Date Time change
Go to setting and check your date time is accurate
In app Billing v3 returns Response Code (BILLING_RESPONSE_RESULT_USER_CANCELED:1) -1005 in more than one scenario.
Launching the purchase flow opens the Google Play-like UI. Whenever an error happens in this UI, say a network disconnection / Timeout / Item Unavailable / Item Already Purchased, a dialog pops up to indicate the error. When OK is clicked , Response code : User Cancelled(1) is invariably returned in the OnActivityResult Intent extras. According to the reference on Android developer's site this is supposed to happen only when the user cancels the activity/Dialog, which i clearly don't do.
I would like to separate out the different errors and handle them individually in my app. Having a single error code returned doesn't help. Any one faced it yet? Is there a solution?
For the record, this seems to be an issue with the Google Play client. Google has started rolling out updates to the GP client as versions 4.0+ and the bug appears to be fixed.
The TrivialDrive demo tests the onActivityResult() callback for the value Activity.RESULT_CANCELED,
and assumes that if that is the value returned then the user has canceled.
That is an incorrect assumption, because if the responseCode in that activity result is BILLING_RESPONSE_RESULT_ITEM_ALREADY_OWNED (=7) then Activity.RESULT_CANCELED means that
the in-app billing service would not allow the purchase because the item is already owned.
This response code is working for me.
However, once that possibility is eliminated, I've found that it's safe to assume that the user has canceled. I haven't checked very recently however to see if Google has made the BILLING_RESPONSE_RESULT_USER_CANCELED response code directly testable and reliable.
Using the beta 3.0 SDK on Android, I'm opening a Facebook Session with a simple session.openForRead() call with basic permissions (email).
As soon as I want to publish a message on a user's wall I'm re-authorizing the session with session.reauthorizeForPublish() including the new publish permissions (publish_actions) in the ReauthorizeRequest object.
As soon as the last request succeeded, a call to session.getPermissions() only returns the last requested permissions (publish_actions), but loses all of the previous read permissions (email).
The documentation of ReauthorizeRequest's parameter permission clearly states "additional permissions to request", so I'm currently not quite sure why the session loses all the other permissions after a request?
Many thanks,
Alex
Alex, sorry you are running into this. This is a bug in the SDK that we are working on fixing prior to the final release. In the meantime, there are a couple of workaround approaches you could try.
While the Session object's notion of its permissions is out of sync with the Facebook service, the access token associated with the Session still has those permissions and can still be used to make Graph API calls requiring any of the permissions it has been granted (unless the user has subsequently revoked any of them, of course). So if your application logic allows you to disregard the results of the Session.getPermissions() call (for instance, if your UI flow implies the user must have already granted a certain permission prior to reaching a certain step in the flow, so you can assume it is present), you can go ahead and make Graph API calls that require those permissions regardless of what getPermissions says -- real truth about what permissions are associated with the token lives in the service, not in the Session object.
If your app logic is such that you need to check whether a certain permission has been granted, unfortunately right now you may need to keep track of the permissions separately,
perhaps by declaring an ArrayList<String> somewhere that you append the new permissions to, perhaps in your Session.StatusCallback whenever the session is opened, and clear it whenever the session is closed. (You could also make a call to "me/permissions" each time the state transitions to OPENED_TOKEN_UPDATED and store the results.) This should be considered only a temporary workaround until the real fix is available. Hope this helps.