I recently came across this app Purchase Apps, which is somehow able to retrieve apps I've paid for in google play after I signed in using my google account.
I'm trying to find out how it is being done as I want to build a similar app, but for the free apps which were downloaded.
However, I can't find which OAuth API Scope was used for retrieving that information, even after going through the entire list of APIs.
EDIT:
I'm putting a new bounty on this question, as suggested by a similar question I've asked about here, and because here and there I don't see a real answer about how to do it, and what can be done with it.
I'd like to refine the questions into multiple pieces:
What is the API that can be used to get information of purchased apps? Where can I read about it? Please show a full, working example of how to do it.
Can it do more ? Maybe perform search? Maybe show free apps that were installed? Maybe the time they were installed and uninstalled? And the categories of those apps?
Are there any special requirements for using this API ?
EDIT: I'm putting a max bounty on this, because no matter how much I've read and tried, I still failed to make a POC that can query the apps from the Play Store that the user has ever downloaded (name, package name, date installed and/or removed, icon URL, price...), including both paid and free apps.
If anyone finds a working sample, show how it's done, and also show how you've found about it (documentation or anything that has led you to the solution). I can't find it anywhere, and the current solutions here are too vague for me to start from.
Issue is resolved. The exploit has been closed.
We will be closing this bug due to being logged in a Preview version of Android. If the issue is still relevant and reproducible in the latest public release (Android Q), please capture a bugreport and log the bug in https://source.android.com/setup/contribute/report-bugs. If a reply is not received within the next 14 days, this issue will be closed. Thank you for your understanding.
Latest update:
This is a bug and Google will address it in the next update.
We've deferred this issue for consideration in a future release. Thank
you for your time to make Android better
This answer has turned into a conglomeration of ideas and been edited to include information from discussion in the comments.
The androidmarket api, would be a customised api written by the developer. It's not available to the public.
To address your concerns in the comments. The developer would have utilised the current apis available through Android Developer and Google to create a project that manages all of these.
As for accessing Full Account Access, I'm not sure exactly how these developers have achieved this.
I'd recommend using the AccountManager, which is part of android.accounts, has access to credentials and a method getUserData. The account manager has access to passwords and is capable of creating and deleting accounts. This, possibly used with Content Provider
See Udinic/SyncAdapter Authentication.
To reply to your comment:
This blog should help you to get started. Write your own Android Authenticator.
How these apps actually work, I cannot tell you. They may also have different implementations (unless they're a collaborative effort behind the scenes, they most certainly will be different).
One guess. Firstly use GoogleSignInAccount with com.google.android.gms.auth.api.signin.
There a definition for scope, to determine the extent of the permissions the app is granted.
Using requestScopes(), the
public static final String PROFILE
.../ It lets your web app access over-the-air Android app installs.
For example:
GoogleSignInOptions gso =
new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestEmail().
.requestScopes(new Scope("https://www.googleapis.com/auth/contacts.readonly"))
.build();
If full access can be gained a list of all apps used by the account holder can be found and compared to what's on the device.
Package Manager will retrieve a list of all apps currently installed on the device.
PackageInfo provides the details about the app.
INSTALL_REASON_USER will also filter out apps that have been actively installed by the user.
You might want to look at com.google.firebase.appindexing and Log User Actions. Different actions can be tracked.
The users account history is found at https://myactivity.google.com/myactivity.
A helpful link is the OAuth 2.0 Playground.
This github repo node-google-play, using node, is current and will call Google Play APIs. As did the archive that was used as an "unofficial" api, android-market-api, to query the market place.
App 1
The app claims to use the following permissions:
Version 2.1.8 can access:
$ In-app purchases
Other
receive data from Internet
view network connections
full network access
use accounts on the device
prevent device from sleeping
read Google service configuration
Noteworthy, the app doesn't set any permissions when there was a basic, install. I was unable to use any of the features, as I have no paid apps. So for the initial search - there were no permissions needed, which would indicate the app didn't have access to my account.
I checked the permissions - there were none set. So the only thing required was to accept the pop up, as displayed in your question.
App 2
The other app you refer to that does the same thing is more upfront about what is being accessed.
My Paid Apps
SECURITY/PRIVACY NOTICE
The first time you run this app, it will ask for full permission to your Google account. This is unfortunately
the only way to access the required information. No personal
information is stored, no information about your apps is shared with
the developer of this app, nor shared with any third parties.
Everything is kept on your phone only.
I've gone into detail over these apps in this blog post, which was for a university capstone project (no monetary gain). I'm inclined to think this is an exploit in the API and not status by design by Google, as there are no API calls to fetch purchases of apps other than the developer's own app. I hypothesize it's a zero day exploit, in which case there's no legitimate way to access this information.
In case of one of these applications (My Paid Apps), after checking the network traffic it is pretty obvious that it does use the Store's Account page to retrieve the list of paid applications.
Now, the mechanism it uses is the same mechanism that Google Chrome currently, and Pokemon GO supposedly at a point in time used.
In a nutshell, steps to do so are as follow:
Login:
What the mentioned program do for the first step is to log the user in and get access to the user's access token. To do so, it uses the android.accounts.AccountManager.getAuthToken() method. (See more: AccountManager)
However, as for the token scope, oauth2:https://www.google.com/accounts/OAuthLogin is requested.
It might be important to note that based on the OAth2 documentation from Google, this scope is not valid; however, it seems like a valid scope for Google OAuth v1.
Converting the newly retrieved access token to a ubertoken:
Now, what actually ubertoken supposed to do, is unknown and there is no official documentation about it. However, it was seen in the wild to be used by chrome browser to login users.
This is done by requesting the https://accounts.google.com/OAuthLogin?source=ChromiumBrowser&issueuberauth=1 page.
Converting ubertoken to website session:
Later on, using the newly created ubertoken it is possible to get a website session using the https://accounts.google.com/MergeSession API endpoint. After this step, the application is essentially capable of loading all personal pages that you can open using your browser while logged in; except some special pages including Payment settings.
Retrieving the list of paid applications:
Requesting and parsing the https://play.google.com/store/account page.
Following is the application's traffic as captured by 'Packet Capture':
As it is clearly visible in the picture, the end result is identical to what I get when I normally open the store's account page on my PC with Chrome Desktop:
Side note:
It seems none of these endpoints are documented as they are primarily used by Google's own programs and should be considered internal. Therefore I strongly recommend not using them in any program or code that you expect to run for a long time or in a production environment.
Also, there is bad news here for you too, it seems that the Google Play's account page only lists paid applications or special free apps (more especially OEM apps). I will try to find some time and dig deeper into the other application.
Interesting articles:
Pokemon tokens
Exploiting Google Chrome's OAuth2 Tokens
If you have root access, You can access /data/data/com.android.vending/databases/library.db
OnePlus3T:/data/data/com.android.vending/databases
-rw-rw---- 1 u0_a2 u0_a2 229376 2018-12-26 18:01 library.db
This database has all information, which app you have downloaded, which apps you have purchased, and even in which app you have done IAP.
Check ownership table, It has all information.
ownership (account STRING, library_id STRING, backend INTEGER, doc_id STRING, doc_type INTEGER, offer_type INTEGER, document_hash INTEGER, subs_valid_until_time INTEGER, app_certificate_hash STRING, app_refund_pre_delivery_endtime_ms INTEGER, app_refund_post_delivery_window_ms INTEGER, subs_auto_renewing INTEGER, subs_initiation_time INTEGER, subs_trial_until_time INTEGER, inapp_purchase_data STRING, inapp_signature STRING, preordered INTEGER, owned_via_license INTEGER, shared_by_me INTEGER, sharer_gaia_id TEXT, shareability INTEGER, purchase_time INTEGER, PRIMARY KEY (account, library_id, backend, doc_id, doc_type, offer_type))
Dealing with unofficial Google APIs is incredibly complicated territory. It's going to be possible to get this to work, but that's all I'll say. Proceed at your own risk.
The first thing you're going to need to do is get a Google Play auth token. This can be done several ways, but here's how they do it in Purchased Apps:
public static String getAuthToken(Activity activity, String userEmail) {
AccountManager accountManager = AccountManager.get(activity);
Account userAccount = new Account(userEmail, "com.google");
Bundle options = new Bundle();
options.putBoolean("suppressProgressScreen", true);
String token;
try {
Bundle result = accountManager
.getAuthToken(userAccount, "androidmarket", options, activity, null, null)
.getResult();
token = result.getString("authtoken");
} catch (OperationCanceledException e) {
Log.d(TAG, "Login canceled by user");
return null;
} catch (IOException | AuthenticatorException e) {
Log.e(TAG, "Login failed", e);
return null;
}
return token;
}
A few things to note here:
The above code must be run asynchronously. I recommend RxJava, but an AsyncTask will work.
You must supply a email for the account you want to use. I'll leave the details up to you but this is fairly easy using AccountManager.
After you have an auth token, you can now access any Google Play Store endpoint. The main one used by Purchased Apps is https://android.clients.google.com/fdfe/purchaseHistory. Another one you might be interested in is https://android.clients.google.com/fdfe/details?doc=(package name) (from APKfetch code). Here's a page with some more and some analysis. If you make a request to these APIs, you'll need to supply several headers:
Authorization - "GoogleLogin auth=(your auth token)"
User-Agent - "Android-Finsky/6.4.12.C-all%20%5B0%5D%202744941 (api=3,versionCode=80641200,sdk=" + VERSION.SDK_INT + ",isWideScreen=0)";
X-DFE-Device-Id - your device's Google Services Framework ID, obtained from AdvertisingIdClient.
X-DFE-Client-Id - "am-android-google"
Accept-Language - The device's language code, eg "en".
Now, you need to parse the response. Here's where things get tricky. These APIs returns a message encoded as a Protobuf, so it's essentially just binary data unless you have a schema (which of course, only Google has). One way to go about this in theory is to decompile the Google Play Store app and reuse their generated protobuf models with a tool like JADX.
Unfortunately, I've tried this and it doesn't really work. Protobuf model classes are just too complex for a standard decompiler. What you can use is a tool called PBTK. You'll ideally want to run this on the Google Play Store 6.1.12 APK, since that's the last version before they started using ProGuard. Do note that this program has two errors in its script that need to be fixed before running it: changing 'extracto' to 'extractor' in gui.py and removing the assertion statement on line 500 of jar_extract.py.
Now, that should output all of the response classes as .proto files. Create a folder under src/main called proto and drag the entire generated 'com' directory to it. You can delete everything that's not under com/google/android/finsky/protos. Follow instructions online to setup Gradle with the Protobuf Lite plugin.
When you want to parse a response, you can use the ResponseWrapper class, since they all appear to be contained under that.
That's about as far as I can take you. There's a good chance I got some part of this wrong; JADX is your best friend here, because the best way to figure out what an app is doing is by looking at its code. Hope this helps and happy developing!
you can get the package name of all installed apps on device and then get the information of every installed package that you find in the device from google play without any need to get to user account. there is some third party or unofficial apis to get google play apps details as json by getting the app package name. for example: https://42matters.com/
then use the received information for every package to find free ones.
i have two resources for you to consider, but first, in a word, no. there is no api from GOOGLE to let you do what you want, as these metrics arent stored in the phone, they are on the google play store servors, and google has no OFFICIAL api for the play store. you can however glean some info from these two sites:
https://www.quora.com/Is-there-an-API-for-the-Google-Play-Storeenter link description here
https://android.stackexchange.com/questions/162146/how-to-see-all-the-apps-i-have-downloaded-from-google-play-store
and this is enough to see how to accomplish this.
first, a list of what apps have been downloaded by an account is only referencable by the account. and this can be done through the play store. since your app will be installed on that users phone, this dosnt matter... you're in.
second, you will need a 3rd party API built for the GOOGLE PLAY STORE, there are some out there, check the first link.
using the api of your choice, you will send a get request, to the play store, and in return you should receive in most cases a json object to deserialize.
deserialize the object, and you will have your list. which list you get will depend on the endpoint you use, but that should be explained by/in the API itself.
good luck!
Suppose I want to write some article on a web-site and publish a link to the discussed Android application (which is available on Google Play).
Is there a standard way to create "badge" for this application, which shows app's name, icon, descriptions, rating, download/install count? Something like this:
or this:
or this:
I found Google Play Badge Generator, but this "Badge Generator" does not do anything special, it is just a dummy static image. Other than that I could not find anything.
I don't think there's an official way to do this (as you have already mentioned the Badge Generator). Anyway you can use one of the many Play Store API wrapper on github to extract the desidered information and build your own fancy badge.
E.g.:
PHP+Curl: https://github.com/thetutlage/Google-Play-Store-API#itemInfo
JS: https://github.com/basiclines/GooglePlay-JSAPI
This one generates simple and clear output like this: http://googleplay-jsapi.herokuapp.com/app/com.meetsapp
many more in various languages..
There´s a fully embedable html widget http://playboard.me/android/widgets/apps you can use out of the box.
You can customize look and feel as they´ve got class per item so you can hide or remove things with css or javascript. Even the "widget by..." has a class pb-wd-footeryou can set to be hidden and leave the widget clean.
You can try this.
https://github.com/facundoolano/google-play-scraper
{
appId: "com.dxco.pandavszombies",
title: "Panda vs Zombie: Elvis rage",
url: "https://play.google.com/store/apps/details?id=com.dxco.pandavszombies&hl=en",
icon: "https://lh6.ggpht.com/5mI27oolnooL__S3ns9qAf_6TsFNExMtUAwTKz6prWCxEmVkmZZZwe3lI-ZLbMawEJh3=w300",
minInstalls: 10000,
maxInstalls: 50000,
score: 4.9,
reviews: 2312,
description: "Everyone in town has gone zombie.",
descriptionHTML: "Everyone in town has gone <b>zombie</b>.",
developer: "DxCo Games",
genre: "Action",
price: "0",
free: true
}
Fetch data using ajax jsonp and create html UI to include in your page. Hope you should be good to go.
I wanted almost the same thing for my project's GitHub page. I wanted the different versions my app has on F-Droid, GitHub release and on Playstore.
I found a cool badge generator http://shields.io/ that can also get data from an endpoint.
I forked google-play-scraper and added the shields.io endpoint requirements. See my
heroku branch for the changes needed.
It's free and a mouse click away to deploy to https://heroku.com from GitHub. After the deploy I have my badge.
Here is how it looks (static image)
You can also check out the google-play-api repo, it's also using google-play-scraper and the RESTful API is already implemented. You would only need to set up how you want to show the information.
some might remember me from previous questions. I'm building an app for Android and it's going well. Most of the functions I wanted work great. I learned the basics by myself (and with the help of a few generous people here on StackOverflow!) but I still consider myself a beginner (today's question will show you how much of a beginner I am!).
My app is a dynamic map that shows the history of a country at a specific point in time. On Google Play, the user can download for free the base app (mostly empty), then he can buy packs (France, USA, UK, etc.) with in-app billing. That's where I am stuck.
I've bought the Milkman AndroidIAB ANE and read carefully the documentation (this one). I've managed to add the ANE to my library and update my application manifest. I've modified the example file to add my public key and the IDs for the purchasable packs. (I don't post the code here because I don't know if I'm allowed since the ANE is licensed.)
My app works that way:
First screen : Logo with a link to the website and a "Enter" button.
When clicked, the user arrive on a screen with a few buttons (one for each pack/country).
If the user click on a pack he owns, he is send to the chosen country's map.
If he doesn't own it, he is asked if he'd like to buy it and send to buy it.
Problems: (warning: some of those are worthy of noob of the month status, but I'm here to learn right?)
The code adapted from the Milkman's example is in an outside .as file used as DocumentClass.How do I link my screen 2 buttons with the functions from the .as file?
I tried this, but it doesn't work:
franceBtn.addEventListener(MouseEvent.CLICK, checkAndPurchase);
function checkAndPurchase (e:MouseEvent):void{
purchasePack1();
//function from the example, it checks if the pack is owned
// and send the user to the store if not
};
EDIT 1: It is probably really easy to do, but I'm just not experienced enough to understand what I need to do I guess.
I have a series of buttons in my app (say "franceBtn", "usaBtn" and "ukBtn"). Those buttons, when clicked, need to check if the pack ("francePack", "usaPack" and "ukPack") is owned by the user and if it is not, start the in-app purchase. I have tried to add an EventListener to the buttons, but nothing happens. Not on screen, not in the log.
2.Let's say the problem 1 is fixed. My app is meant to be used offline (except for buying additional packs). The way I understood what I read is that the "inventory" of in-app purchased packs is obtained via Google Play, which means (if I'm not mistaken) that the user needs to be online. Is there a way to create a file of some kind inside the device that stores this "inventory" so it can be accessed offline?
EDIT 2: I want the user to be able to use the app from everywhere, without the need to be online (except for purchasing packs obviously). But I guess that the app checks with Google Play in order to know which pack is already owned. So I'm looking for a way to store the "inventory" of owned packs directly inside the device/app (so it can be accessed offline and updated everytime internet starts.
I hope it is more clear, and thank you for pointing out it was not.
I've read the doc quite a few times, and I'm really stuck, so please, I'd really appreciate any help. ;)
Thanks in advance,
Jeryl
EDIT 3: Here is the portion of my code relating to IAB (made by following this tutorial :here but I didn't really understand it. I'm willing to learn but this is an intermediate level tuto, and I've found nothing on Internet that explains what to do for real beginners. If you have links I couldn't find, I'm all hears :D )
import com.milkmangames.nativeextensions.android.*;
import com.milkmangames.nativeextensions.android.events.*;
import flash.events.MouseEvent;
if (AndroidIAB.isSupported()) {
AndroidIAB.create();
}
// listeners for billing service startup
AndroidIAB.androidIAB.addEventListener(AndroidBillingEvent.SERVICE_READY, onReady);
AndroidIAB.androidIAB.addEventListener(AndroidBillingEvent.SERVICE_NOT_SUPPORTED, onUnsupported);
// start the service
AndroidIAB.androidIAB.startBillingService("my_key");
function onReady(e: AndroidBillingEvent): void {
trace("service now ready- you can now make purchases.");
}
function onUnsupported(e: AndroidBillingEvent): void {
trace("sorry, in app billing won't work on this phone!");
}
// listen for inventory events
AndroidIAB.androidIAB.addEventListener(AndroidBillingEvent.INVENTORY_LOADED, onInventoryLoaded);
AndroidIAB.androidIAB.addEventListener(AndroidBillingErrorEvent.LOAD_INVENTORY_FAILED, onInventoryFailed);
function onInventoryLoaded(e: AndroidBillingEvent): void {
for each(var purchase: AndroidPurchase in e.purchases) {
trace("You own the item:" + purchase.itemId);
}
}
function onInventoryFailed(e: AndroidBillingErrorEvent): void {
trace("Something went wrong loading inventory: " + e.text);
}
// load the player's current inventory
AndroidIAB.androidIAB.loadPlayerInventory();
// listen for purchase events
AndroidIAB.androidIAB.addEventListener(AndroidBillingEvent.PURCHASE_SUCCEEDED, onPurchaseSuccess);
AndroidIAB.androidIAB.addEventListener(AndroidBillingErrorEvent.PURCHASE_FAILED, onPurchaseFailed);
function onPurchaseSuccess(e: AndroidBillingEvent): void {
var purchase: AndroidPurchase = e.purchases[0];
trace("you purchased the item " + purchase.itemId);
AndroidIAB.androidIAB.loadPlayerInventory();
}
function onPurchaseFailed(e: AndroidBillingErrorEvent): void {
trace("Something went wrong with the purchase of " + e.itemId + ": " + e.text);
}
franceBtn.addEventListener(MouseEvent.CLICK, onPackOneButtonClicked);
function onPackOneButtonClicked(e: MouseEvent) {
if (purchase.itemId == "packone") {
franceMap.visible = true;
} else {
AndroidIAB.androidIAB.purchaseItem("packone");
}
}
Thanks for your patience! Jeryl
Before proceeding, I am assuming that you have properly set up the products in IAP section of Google play.
Now for your first question, I am not sure about the problem as clicking on a button and something to be done on that event should have worked out of the box. Your code should be something along the lines:
( Assuming you have two packs and their itemIds are com.yourcomapny.packOne and com.yourcompany.packTwo)
franceBtn.addEventListener( MouseEvent.CLICK, onPackOneButtonClicked );
function onPackOneButtonClicked( event:MouseEvent )
{
if( !isPackOnePurchased() )
{
purchasePackOne();
}
}
// Similarly for pack two
This is pretty much the same code , you are trying to write. Perhaps we can help you more with your problem, if you can post some portion of the code which is not working.
For your second part, if you want to store the "inventory" information offline, you may proceed in the following way:
1) Lets say you create an empty file.
2) Lets say if a person buys com.yourcompany.packTwo and you received purchase Successful event, you just add com.yourcompany.packTwo to the file and thus marking it as purchased.
3) Whenever you call isPackOnePurchased or isPackTwoPurchased, it checks whether corresponding itemId is present in the file and decide whether this package needs to be bought or it is already bought.
This will get you started. Another thing for enhanced user experience and security is that whenever user clicks on a button to buy the pack ( and he/she is connected to the internet ), always check whether that pack is in the inventory or not and show the feedback according to that. Always sync your local state with inventory, if there is any inconsistency. The inconsistency may arise if user deletes their application data and then tries to open the pack. This check will ensure that your application is synced with server state.
If you want next level of security you can either encrypt the information( item Ids in this case ) and store it a secure database.
Encryption can be done using numerous encryption methods. One such method is Rijndael encryption. You can use this tool this online tool for generating encrypted strings for your itemIds. This will give you a feel of what I am trying to say.
For how to store information in local database in an AIR based applications, see this link from Adobe
I'm using phonegap-build / FacebookConnect
Everything works correctly but when I put some post using Android app on the Facebook wall, most of the text remains not shown. There is not even "see more" link at the end of the part of the post. At the half of the second sentence of "description" everything breaks.
The JS function that I'm using for this Android app:
function facebookWallPost() {
console.log('Debug 1');
var params = {
method: 'feed',
name: 'Facebook Dialogs',
link: 'https://developers.facebook.com/docs/reference/dialogs/',
picture: 'http://fbrell.com/f8.jpg',
caption: 'Reference Documentation',
description: 'The Internet is a global system of interconnected computer networks that use the standard Internet protocol suite (TCP/IP) to serve billions of users worldwide. It is a network of networks that consists of millions of private, public, academic, business, and government networks, of local to global scope, that are linked by a broad array of electronic, wireless and optical networking technologies. The Internet carries an extensive range of information resources and services, such as the inter-linked hypertext documents of the World Wide Web (WWW) and the infrastructure to support email.'
};
console.log(params);
FB.ui(params, function(obj) { console.log(obj);});
}
The whole script and an example can be seen here ...
Do I need to change data structure and post params as JSON?
Thanks in advance. I appreciate any help...
I have currently built an application using phone gap targeting the android and blackberry platforms.
I use a combination of Jquery mobile and Phonegap for my application, since both are open source frameworks and improvements as well as bug fixes keep happening I wanted to know what would be a good solution for alerting my users to update their application when I upgrade the above frameworks in my application.
One solution I had in mind is maintain a version numbering on my server for the apps, when the app is loaded on the users device we can make an ajax call to check for version update and then alert the user to upgrade their application.
Android market also has an auto update feature how does that work! How do I go about this what would be a good approach.
If you are planning to build "native", in this case localy installed apps. You don't have to worry about informing the user. As soon as you uplad the new versions to the Android market or App World the App market systems will let the users know automatically.
I think (in most cases) it is not necessary to let the user know about updates within the app. Some apps do that but I see it less often since it really does not make much sense.
But in case you want to build such a feature, I would store a version number somwhere in the code and make a server request eg. when the app starts which then compares the latest version number of your app stored on your server.
Eg.
Client:
$.ajax({
url: webservice_host + '&callback=?',
dataType: 'jsonp',
success: function (data) {
//data -> remote version
var local_version;
if (local_version < data ){
alert("There is a newer version available");
}
}
});
Server (php in this case):
<?php
print mysql_real_escape_string($_GET['callback']). '( 1.1 )';
?>
I didn't test the code for typos etc. But this should do the trick.