Authorize YouTube Data API for Android via OAuth2 - android

I am trying to authorize with OAuth2 to use YouTube data API. I want to write a simple client for Android to have a possibility, for example, get lists of user subscriptions, playlist etc.
The problem is that each method I try (e.g. HTTP requests like https://www.googleapis.com/youtube/v3/subscriptions or YouTube library com.google.apis:google-api-services-youtube:v3-rev120-1.19.0), I get the same error:
401 Invalid Credentials
I do these steps:
Register app in Google's console. Enable YouTube Data API. Create OAuth credentials with my app package name and keystore.
Create Android API Key.
And here I have a lot of questions.
To have a possibility to get user subscriptions etc. do I have to register the app with OAuth2 credentials AND (?) Register API Key?
How can I use client_id and other data that is in client_secrets.json? How can I use this client_secrets.json at all? Do I need download it, and how to use it?
Because when I saw sample code, for example this:
GoogleAccountCredential credential = GoogleAccountCredential.usingOAuth2(context.getApplicationContext(), Arrays.asList(SCOPES));
credential.setSelectedAccountName(accountName);
YouTube youtube = new YouTube.Builder(new NetHttpTransport(),
new JacksonFactory(), credential).setApplicationName(context.getString(R.string.app_name)).build();
Or used AccountManager and get token
accountManager.getAuthToken(userAccount, "oauth2:" + mScope, null, (Activity) mContext,
new OnTokenAcquired(), null);
where mScope="https://gdata.youtube.com"
and then use it this way
https://www.googleapis.com/youtube/v3/subscriptions?part=snippet&maxResults=10&mine=true&key={here is my key}&access_token= ya29.bgJa5J8LHHu0Mu3P87mbPY9gfhSFzWUHh8gCw022FC6yfMO6GfVHveYr_BG1-HnWF-jpd-IDcGGVrF3gEf4UhSHPSrV76RAwqd4V17ixn72s1Xl6a0wQ4FVDz3cEZjUEN80o
I get the same authError error every time (in short):
"code": 401,
"message": "Invalid Credentials"
What I am doing wrong? How to use client_id in variant 1 and 2. Is it necessary? Because I think it is the reason why it does not work.

Related

How to get Purchases.subscriptions: Details in Android

I implemented in-app billing in my mobile app. It uses auto renewing subscription. I want to check the subscription expiry date through the app. I found this link to check the subscription details : Purchases.subscriptions:get
The documentation shows that some authorization needed. I have tried but I am not able to get the result. I got client secret.json file but it does not contain client secret Id. So please help me to get the subscription expiry date.
My answer is late, but maybe it helps somebody else. See my answer here: Server side authorization with Google Play Developer API?
And these are the steps when you would do it manually:
1.) Create credentials in your OAuth2 configuration in Google API with the following link: https://developers.google.com/mobile/add?platform=android&cntapi=signin&cnturl=https:%2F%2Fdevelopers.google.com%2Fidentity%2Fsign-in%2Fandroid%2Fsign-in%3Fconfigured%3Dtrue&cntlbl=Continue%20Adding%20Sign-In
2.) Go to your Developer API console. If you have done the first step correct, the result should look like something like that:
3.) Go to Google Play Developer Console -> All Apps -> Settings -> API Access and link the project you defined in Developer API Console (step 2, black bar top left). (If you can't link your project in Google Play, because you didn't find some, you used different google accounts for Google Developer API and Google Play Developer Console.)
4.) Invoke the following link with your callback url:
https://accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/androidpublisher&response_type=code&access_type=offline&redirect_uri=http://www.yourdomain.com/oauth2callback&client_id=[WEBAPPLICATION_ID_FROM_ABOVE.apps.googleusercontent.com]
My /oauth2callback script is a simple php script. (This is not production safe. It's just for illustration):
oauth2callback.php
<?php
print_r($_REQUEST);
?>
5.) After you invoked the url from above, you will be asked for your google account and allowance to access the api. After you confirmed, you will be redirected to your callback url and get a result which looks like this:
4/vPv_eYX-az6YucwdpBzATpJEu8129gN9aYsUIMI3qgQ
6.)¹ Get an oauth2 access token and refresh token by invoking a POST request to https://accounts.google.com/o/oauth2/token with the following headers:
grant_type=authorization_code
code=4/vPv_eYX-az6YucwdpBzATpJEu8129gN9aYsUIMI3qgQ
client_id=[YOUR_WEBAPPLICATIONID.apps.googleusercontent.com]
client_secret=[YOUR CLIENT SECRET OF THIS WEBAPPLICATION ID]
redirect_uri=http://www.yourdomain.com/oauth2callback
(The client secret can be found when you click on the Web Client ID of the OAuth2 client IDs in the Developer Console from step 2)
The result will look like this:
{
"access_token": "ya29.GdsCBbnM584k3SUzoxDgIdaY26pEM_p_AdhkIkFq3tsai8U7x8DuFKq3WEF7KquxkdLO89KHpuUFdJOgkhpGbGyDfxkD32bK1ncnsu2IkA0e_5ZayOEr86u4A1IN",
"expires_in": 3600,
"refresh_token": "1/U5HF1m0nHQwZaF2-X35f_xyFaSOofdw3SEubnkkYUQ0",
"token_type": "Bearer"
}
7.) The access token is needed to invoke the Google Developer API requests, for example: https://www.googleapis.com/androidpublisher/v2/applications/[packageName]/purchases/subscriptions/subscriptionId/tokens/[purchaseToken]?access_token=ya29.GdsCBbnM584k3SUzoxDgIdaY26pEM_p_AdhkIkFq3tsai8U7x8DuFKq3WEF7KquxkdLO89KHpuUFdJOgkhpGbGyDfxkD32bK1ncnsu2IkA0e_5ZayOEr86u4A1IN
8.) The access token expires very quickly. With the refresh you can fetch new access tokens by using the following url and headers:
POST https://accounts.google.com/o/oauth2/token
grant_type=refresh_token
client_secret=[YOUR CLIENT SECRET]
refresh_token=1/U5HF1m0nHQwZaF2-X35f_xyFaSOofdw3SEubnkkYUQ0
client_id=[YOUR_WEBAPPLICATION_ID.apps.googleusercontent.com]
Result:
{
"access_token": "ya29.GlsCBZuN7hYJILi5VaVggIsCIb1_5feGvcjvQFmJRnPYXsnhsi_w3Md87tQwGd_WXmifo4s5739c3IU5INPmby8q64k0LdDFkO2JpNRG13K9sizvU1Sc-3cWzbf8",
"expires_in": 3600,
"token_type": "Bearer"
}
HINT (1): If you don't get a refresh token from the request of step 6, attach the following query params to your request: prompt=consent
I also struggled a bit with this, but at least I found what needs to be done.
Google provides an android library google-api-services-androidpublishe that can be used to access the Purchase.Subscription.Get API.
implementation 'com.google.apis:google-api-services-androidpublisher:v3-rev74-1.25.0'
Here is a Kotlin code example:
import com.google.api.client.http.apache.ApacheHttpTransport
import com.google.api.client.json.jackson2.JacksonFactory
import com.google.api.services.androidpublisher.AndroidPublisher
...
fun retrieveSubscriptionInfo(sku: String) {
val packageName = ""
val sku = ""
val purchasedSubscriptionToken = ""
val apacheHttpTransport = ApacheHttpTransport()
val jacksonJsonFactory = JacksonFactory.getDefaultInstance()
val AndroidPublisher.Builder(apbacheHttpTransport, jacksonJsonFactory, null).build()
val request = publisher.Purchases().subscriptions().get(packageName, sku, purchasedSubscriptionToken);
return request.execute()
}
Note that before the above code is executed you must first Authorize the AndroidPublisher. You would have to request the users permission in order to be able to query the Purchase.Subscription.Get
Hope that this is helpful.
Google's documentation here is horrible, no clear examples of implementation. I finally found the answer after a LOT of Googling thanks to #Sabeeh's answer here and lots of other snippets spread out online.
1. First step is to add the dependencies:
implementation "com.google.apis:google-api-services-androidpublisher:v3-rev142-1.25.0" // Update based on latest release
implementation "com.google.auth:google-auth-library-oauth2-http:1.12.1" // Update based on latest release
2. Follow these steps to link the Google Play Console with Google Play Developer API (choose the "Use a service account", not "Use OAuth clients" and follow until "Additional information").
3. Download the services JSON file from your Google Cloud service account (click on the account that you set up in the previous step). You can find/create this file under the "Manage Keys" action or the "Keys" tab. Add the exported JSON file in your assets folder in Android
4. Then you can call the Google Play Developer API to query subscriptions like this (important to call from a Thread, didn't work from the UI thread, not sure why):
new Thread(() -> {
InputStream inputStream = context.getAssets().open("service_account_google_play.json"); // JSON file from step 3
GoogleCredentials credentials = GoogleCredentials.fromStream(inputStream)
.createScoped(AndroidPublisherScopes.ANDROIDPUBLISHER);
AndroidPublisher androidPublisher = new AndroidPublisher(
new NetHttpTransport(),
JacksonFactory.getDefaultInstance(),
new HttpCredentialsAdapter(credentials)
);
SubscriptionPurchase purchase = androidPublisher.purchases().subscriptions().get(
context.getPackageName(), subscriptionId, purchaseToken
).execute();
// Do with the purchase object what you want here
}).start();
At the risk of being overly descriptive, the subscriptionId is the ID of your subscription in the Play Console (e.g. subscription_monthly or whatever you called it), and the purchaseToken is the token you get from the Purchase token after querying the BillingClient (querying subscriptions is explained in detail here).
Let me know if anything is unclear or doesn't work yet. This took me 6 hours to figure out and I'm happy to save others that pain.

How do I restrict Google App Engine Endpoints API access to only my Android applications?

I am an Android developer building my first Google App Engine (java) back-end for my apps. I don't want anybody else to access this API other than my app. (I plan to use App engine for verifying InApp purchases in my Android app). My data is not relevant to users so,
I don't want users to be able to access my API even if they are logged in with their Google accounts (on web or Android devices).
I followed the steps mentioned in - "Specifying authorized clients in the API backend"
(https://developers.google.com/appengine/docs/java/endpoints/auth)
like generating client IDs and add them in #Api (clientIds and audiences)
except "Add a User parameter" - since I don't need user authentication.
Then I deployed App engine and I am still able to access the API through API explorer (https://your_app_id.appspot.com/_ah/api/explorer)
(I haven't added API_EXPLORER client ID)
I tested with the APK that was built with the endpoint libs before adding client IDs and can still access the API.
Is adding a "User parameter" to all endpoint APIs a must? to achieve my purpose (restrict API to only my Android apps).
Can I pass null as userAccount name from Android client and ignore user parameter value on server (since it will be null)? Will this ensure that the API is accessible only from my android apps (since the client ID is generated for my package name and SHA1 of the APK?)
Should I use something like a service account for this purpose?
The documentation says for Android, both Android and Web client IDs must be added and audience must be the same as web client ID. Does this open access to any other web client? can I skip mentioning web client ID and still achieve my purpose?
Appreciate your time and help.
...... updating with my further investigation ...
I did the following:
Added User parameter to APIs on backend - but did not check for null value. API can still be accessed without passing any credentials (from Android debug APK and API explorer)
Then, I tried
mCredential = GoogleAccountCredential.usingAudience(this, "server:client_id:" + WEB_CLIENT_ID);
mCredential.setSelectedAccountName(null);
and passed this credential to API builder (as suggested in some other posts)
Caused FATAL EXCEPTION. So, we can't pass null account name.
I could call the API using API explorer without OAuth. But when I enabled OAuth, it gave error saying this client ID is not allowed! ( I haven't yet added com.google.api.server.spi.Constant.API_EXPLORER_CLIENT_ID in client_ids{})
Then I added code to throw OAuthRequestException on the backend if the user is null. This resulted in API explorer getting errors without OAuth. It works with OAuth enabled after adding API_EXPLORER_CLIENT_ID to client_ids)
Added code to pass valid user account name(email) from my Android app. Then, I am able to access API only with my release APK. Even the debug APK gets exceptions! - which is what I expected..So, I assume no other Android apps will be able to access this API.
So, not checking for null user on back-end API is a bad idea (as suggested in other posts). It is as good as not mentioning any client_ids and not having User param.
Only question I have at this moment is: If some one can figure out the WEB_CLIENT_ID from the APK, will they be able to use it to build a web client to access my API (I haven't mentioned client secret anywhere in the code. So I am thinking this is not possible).
I did search Google groups and Stackoverflow, but still it is not clear.
(Authenticate my “app” to Google cloud endpoints not a “user”)
Authenticate my "app" to Google Cloud Endpoints not a "user"
(How do I protect my API that was built using Google Cloud Endpoints?)
How do I protect my API that was built using Google Cloud Endpoints?
(Restrict access to google cloud endpoints to Android app)
Restrict access to google cloud endpoints to Android app
I had a similar issue, not between Android and App Engine, but between a separate server and App Engine. The way I handled it was to add a signature hash field as a parameter to each API call. If the request had an improper signature, it would be denied.
For example, suppose your API end-point is example.com/api/do_thing?param1=foo. I would hash the entire url, along with a secret key, and then append the result of the hash to the request: example.com/api/do_thing?param1=foo&hash=[some long hex value].
Then, on the server side, I would first remove the hash from the url request, then run the hash on everything that was remaining. Finally, you check whether the calculated hash matches the one that was sent with the request and if they don't, you can deny the request.
It is very important however that your secret key remain secret. You have to be careful with this on Android because someone could attempt to decompile your APK.
Facing the same problem than you ! Authenticate Android End point without Google User Account is just impossible !
So here is my way to resolv this problem, without any user interaction (Maybe not the right but that works, and you've got strong authentication (SHA1 + Google Account)):
HERE IS MY ANDROID CODE
Get and Build Valid Credential
//Get all accounts from my Android Phone
String validGoogleAccount = null;
Pattern emailPattern = Patterns.EMAIL_ADDRESS; // API level 8+
Account[] accounts = AccountManager.get(context).getAccounts();
for (Account account : accounts) {
if (emailPattern.matcher(account.name).matches()) {
//Just store mail if countain gmail.com
if (account.name.toString().contains("gmail.com")&&account.type.toString().contains("com.google")){
validGoogleAccount=account.name.toString();
}
}
}
//Build Credential with valid google account
GoogleAccountCredential credential = GoogleAccountCredential.usingAudience(this,"server:client_id:301991144702-5qkqclsogd0b4fnkhrja7hppshrvp4kh.apps.googleusercontent.com");
credential.setSelectedAccountName(validGoogleAccount);
Use this credential for secure calls
Campagneendpoint.Builder endpointBuilder = new Campagneendpoint.Builder(AndroidHttp.newCompatibleTransport(), new JacksonFactory(), credential);
HERE IS MY API BACKEND CODE:
API Annotation
#Api(
scopes=CONSTANTES.EMAIL_SCOPE,
clientIds = {CONSTANTES.ANDROID_CLIENT_ID,
CONSTANTES.WEB_CLIENT_ID,
com.google.api.server.spi.Constant.API_EXPLORER_CLIENT_ID},
audiences = {CONSTANTES.ANDROID_AUDIENCE},
name = "campagneendpoint",
version = "v1"
)
Method code:
public Collection<Campagne> getCampagnes(#Named("NumPortable")String NumPortable, User user) throws UnauthorizedException {
if (user == null) throw new UnauthorizedException("User is Not Valid");
return CampagneCRUD.getInstance().findCampagne(NumPortable);
}
For the moment, it only works on Android (I don't know how we gonna do on IOS..)..
Hope It will help you !
Google provides ways to do this for Android, web and iOS
The steps involves:
Specifying a client Id for apps you want to allow to make requests to your API
Adding a User parameter to all exposed methods to be protected by authorization.
Generating the client library again for any Android clients
Redeploying your backend API.
Updating the regenerated jar file to your Android project for your Android client.
These steps are laid out in clear detail on Google's Using Auth with Endpoints and also on this blog
Facing the same problem, here are the result of my research :
Added Android cliend id with SHA1 fingerprint in Google console
Use of it in the API annotation
BUT :
If i dont add user parameter to methods : the check about android app client id does not work
If I add the USER parameter but do not ask the user to choose its google account to create the credential ... also it does not work ...
Conclusion : It seems to be mandatory to connect a user account for the check about the app client id to be executed ... I really do not understand why because no link exist between the 2 processes
Access this site
Choose your project, go to credentials section
Create a new api key
Create a new android key
Click on "Edit allowed android applications" and enter your SHA1 key; your android package name
Let me know if this solves the issues.

Am I getting the steps right for verifying a user's Android in-app subscription?

I am making an app that does not require a user account/login, and allows the user to purchase a subscription. I want to use the Google Play Developer API to verify whether or not a user has a purchased/active subscription. From all of the documentation, I've gathered the following steps.
Are they correct, and could you answer the two questions in them?
Create a Service Account in the Google APIs Console.
Save the private key that is given to me (where? surely not in my code/on the device as this sample code suggests)
Use Google APIs Client Library for Java to create and sign a JWT with the private key (how? the docs give me this, but that is not Java code... What do I do with it?)
Construct an access token request, and get access to the API
Application can now send a GET request to the API to find out whether or not the
user has a subscription
When the access token expires, go back to step 3.
Also, I have a web service, though I know nothing about web services or web service programming... I only know enough to be aware that it is probably necessary to use here.
EDIT: These steps were not correct. See my answer below for the correct steps. However, note that this only applies to using a service account (because I did not want to require a user to have to explicitly allow API access)
As it turns out, my steps were not correct. It took me weeks to figure this out and it doesn't seem to be documented anywhere else. You're welcome:
Create a Web Application account in the Google APIs Console. Put any website as a "redirect URI"; it doesn't matter since you will not really be using it. You will get a client id and client secret when you create the account.
In a browser on your computer go to https://accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/androidpublisher&response_type=code&access_type=offline&redirect_uri=[YOUR REDIRECT URI]&client_id=[YOUR CLIENT ID] and allow access when prompted.
Look in the address bar. At the end of the URI you entered originally will be your refresh token. It looks like 1/.... You will need this "code" in the next step. The refresh token never expires.
Convert this "code" to a "refresh token" by going to https://accounts.google.com/o/oauth2/token?client_id=[YOUR CLIENT ID]&client_secret=[YOUR CLIENT SECRET]&code=[CODE FROM PREVIOUS STEP]&grant_type=authorization_code&redirect_uri=[YOUR REDIRECT URI]. You can save the resulting value right in your program; it never expires unless explicitly revoked. (this step inserted by #BrianWhite -- see comments)
Make sure you are using POST.(inserted by Gintas)
In your code, send an HttpPost request to https://accounts.google.com/o/oauth2/token with the BasicNameValuePairs "grant_type","refresh_token", "client_id",[YOUR CLIENT ID], "client_secret",[YOUR CLIENT SECRET], "refresh_token",[YOUR REFRESH TOKEN]. For an example look here. You will need to do this in a separate thread, probably using AsyncTask. This will return a JSONObject.
Get the access token from the returned JSONObject. For an example look here. You will need to get the string "access_token". The access token expires in 1 hour.
In your code, send an HttpGet request to https://www.googleapis.com/androidpublisher/v1/applications/[YOUR APP'S PACKAGE NAME]/subscriptions/[THE ID OF YOUR PUBLISHED SUBSCRIPTION FROM YOUR ANDROID DEVELOPER CONSOLE]/purchases/[THE PURCHASE TOKEN THE USER RECEIVES UPON PURCHASING THE SUBSCRIPTION]?accesstoken="[THE ACCESS TOKEN FROM STEP 4]". For an example look here.
.NET Users: I hope this answer saves someone a ton of grief.
As #Christophe Fondacci noted on 2015, the accepted solution worked great a few years ago.
Now it's 2017 2020 and the process is far easier and faster.
My use case is to validate in-app subscriptions, where my mobile app sends subscription purchase information to my RESTful server, which in turn contacts Google to validate a subscription purchase.
The strategy is to create a Service Account that will operate on your behalf.
Sign into your Google Play Dev Console and click the app you're setting up.
Visit Settings->API access
Under Service Accounts, hit the Create Service Account button.
As of Jan 2017 a dialog with directions on setting up a service account appears. The dialog takes you to the Google API Console; from there,
A) Click Create Service Account
B) Create the service account name that makes sense. Since we're interested in accessing Android Publisher Services, I chose "publisher".
C) For Role, just choose something - you can change this later.
D) Choose "Furnish New private key" and choose P12 for .Net implementations. Don't lose this file!
Now you're done with #4, you'll see your new Service Account listed; click "Grant Access" to enable it.
Tap on the link to "View permissions". You should modify permissions based on your needs and API.
To validate in-app purchases, visit the Cog->Change Permissions and enable the GLOBAL "Visibility" and "Manage Orders" permissions.
OK at this point you have configured everything on Google's end. Now to setup your server to server stuff. I recommend creating
a .Net Console App to test out your implementation then offload it where needed.
Add the Android Publisher Client Library from Nuget[1]
PM> Install-Package Google.Apis.AndroidPublisher.v3
Add the P12 file to your project root
Change the P12 Properties so "Build Action" is "Content" and "Copy To Output Directory" to "Copy if newer".
Implement something like this to test your access and fine tune [1] .
using System.Threading.Tasks;
using System.Security.Cryptography.X509Certificates;
using Google.Apis.Services;
using Google.Apis.Auth.OAuth2;
using Google.Apis.AndroidPublisher.v3;
...
public Task<SubscriptionPurchase> GetSubscriptionPurchase(string packageName, string productId, string purchaseToken)
{
var certificate = new X509Certificate2(
"{{your p12 file name}}",
"{{ your p12 secret }}",
X509KeyStorageFlags.Exportable
);
var credentials = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer("{{ your service account email }}")
{
Scopes = new[] { AndroidPublisherService.Scope.Androidpublisher }
}.FromCertificate(certificate));
var service = new AndroidPublisherService(new BaseClientService.Initializer()
{
HttpClientInitializer = credentials,
ApplicationName = "my server app name",
});
return service.Purchases.Subscriptions.Get(packageName, productId, purchaseToken).ExecuteAsync();
}
Good luck, hope this helps someone.
Sources:
Using OAuth 2.0 for Server to Server Applications
.Net Client Library for Google.Apis.AndroidPublisher.v3[1]
1 Updated 04/11/2020 - Google.Apis.AndroidPublisher.v2 EOL'd, use Google.Apis.AndroidPublisher.v3.
If you are like me, and want to do this in PHP, here is the procedure how to do it... Thanks to Kalina's answer it took me only three days to work out how it works :).
Here goes:
go to google developers console https://console.developers.google.com/ and create a web app. Put 'developers.google.com/oauthplayground'as a "redirect URI"; You will use it in step 2. You will get a client id and client secret when you create the account. Make sure you have the Google Play Android Developer API added.
go to the Google oauth2 playground https://developers.google.com/oauthplayground/. This great tool is your best friend for the next few days.
Now go to settings : make sure Use your own OAuth credentials is set. Only then you can fill in your client ID and client secret in the form below.
In Google oauth2 playground go to step 1 Select & authorize APIs fill in the scope in the input field https://www.googleapis.com/auth/androidpublisher. I couldnt find the Google Play Android Developer API in the list, maybe they will add some time later. Hit AUTORIZE APIS. Do the authorisation thing that follows.
In Google oauth2 playground go to step 2 Exchange authorization code for tokens. If all went well you will see a authorization code starting with /4. If something didnt go well check the error message on the right. Now you hit 'refresh access token'. Copy the Refresh token... it will start with /1...
Now you can always get an access token! here is how:
$url ="https://accounts.google.com/o/oauth2/token";
$fields = array(
"client_id"=>"{your client id}",
"client_secret"=>"{your client secret}",
"refresh_token"=>"{your refresh token 1/.....}",
"grant_type"=>"refresh_token"
);
$ch = curl_init($url);
//set the url, number of POST vars, POST data
curl_setopt($ch, CURLOPT_POST,count($fields));
curl_setopt($ch, CURLOPT_POSTFIELDS, $fields);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
//execute post
$lResponse_json = curl_exec($ch);
//close connection
curl_close($ch);
Now you have an ACCESS TOKEN hooray... the JSON will look like this:
"access_token" : "{the access token}", "token_type" : "Bearer", "expires_in" : 3600
Finally you're ready to ask google something! Here is how to do it:
$lAccessToken = "{The access token you got in}" ;
$lPackageNameStr = "{your apps package name com.something.something}";
$lURLStr = "https://www.googleapis.com/androidpublisher/v1.1/applications/$lPackageNameStr/subscriptions/$pProductIdStr/purchases/$pReceiptStr";
$curl = curl_init($lURLStr);
curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$curlheader[0] = "Authorization: Bearer " . $lAccessToken;
curl_setopt($curl, CURLOPT_HTTPHEADER, $curlheader);
$json_response = curl_exec($curl);
curl_close($curl);
$responseObj = json_decode($json_response,true);
The JSON returned will contain two timestamps, the initiationTimestampMsec and validUntilTimestampMsec the time the subscription is valid. Both are the nr of millisecs to add to the date 1/1/1970!
I don't know in 2012, but in 2015 you should not do any of these steps manually. I had a very hard time to find the documentation so I am posting here in case it helps anyone.
You should only query in-app purchases from your server for security reasons as otherwise you can trust none of the 2 ends of the purchase process.
Now on the server side (I think you could still use the same code from your app if you absolutely need to), include the google-api-services-androidpublisher client library to your project (see https://developers.google.com/api-client-library/java/apis/androidpublisher/v1)
As you mentioned, you need a service account with a P12 file (the client library only accept P12 file).
Then the following code will authenticate and get purchase information nicely:
HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
JsonFactory jsonFactory = new JacksonFactory();
List<String> scopes = new ArrayList<String>();
scopes.add(AndroidPublisherScopes.ANDROIDPUBLISHER);
Credential credential = new GoogleCredential.Builder().setTransport(httpTransport).setJsonFactory(jsonFactory)
.setServiceAccountId(googleServiceAccountId)
.setServiceAccountPrivateKeyFromP12File(new File(googleServicePrivateKeyPath))
.setServiceAccountScopes(scopes).build();
AndroidPublisher publisher = new AndroidPublisher.Builder(httpTransport, jsonFactory, credential).build();
AndroidPublisher.Purchases purchases = publisher.purchases();
final Get request = purchases.get(packageName, productId, token);
final SubscriptionPurchase purchase = request.execute();
// Do whatever you want with the purchase bean
Information on Java client authentication can be found here:
https://developers.google.com/identity/protocols/OAuth2ServiceAccount
I may misunderstand your question, but I don't see a reason for you to be using the links you're referencing to get In-App Billing for an Android app working. This page is much more helpful:
http://developer.android.com/guide/google/play/billing/index.html
You can try out the demo application they include (Dungeons -- http://developer.android.com/guide/google/play/billing/billing_integrate.html#billing-download). That uses products (one-time purchases) rather than subscriptions, but you should be able to modify to test for what you want.
I think the key, for you, would be the restoreTransactions method they provide in the sample to see if the Google Play account has any subscriptions for your app:
#Override
public void onRestoreTransactionsResponse(RestoreTransactions request, int responseCode) {
if (responseCode == BillingVars.OK) {
// Update the shared preferences so that we don't perform a RestoreTransactions again.
// This is also where you could save any existing subscriptions/purchases the user may have.
SharedPreferences prefs = getSharedPreferences(my_prefs_file, Context.MODE_PRIVATE);
SharedPreferences.Editor edit = prefs.edit();
edit.putBoolean(DB_INITIALIZED, true);
edit.commit();
} else {
Log.e(TAG, "RestoreTransactions error: " + responseCode);
}
}
If anyone is having issues with the accepted posts final step (#7), i found ?access_token= to work instead of ?accessToken=
Too bad stack overflow won't let me make that comment directly to the thread...
As you have a web service which your app can call, I would recommend storing your private key securely on your server. You should look to moving as much of the in-app stuff to service calls, as possible, see this link. I've implemented in-app subscription, but it was before this part of the API was out. I had to do my own registration and security verification but it looks like this API does most of that for you, using OAuth, although it looks like you are still responsible for storing the subscription request/verification.
Where it talks about signing your JWT's with an existing library, they do appear to provide you with links to a java library, a Python library and a PHP library - it depends what your web service or server component is written in (mine is C#, so I'm using RSACryptoServiceProvider) to verify signed purchases. They're using JSON objects for the actual transfer of data.

Android subscription and Google API

I'm trying to use the new Android subscription system from Google Play into my application (I already had in-app billing working fine). I have successfully done the subscription billing, but I now want to retrieve informations about this subscription by using the google apis as indicated in the android documentation (http://developer.android.com/guide/market/billing/billing_subscriptions.html).
I want my service to be able to do the API call to retrieve these informations, but I have problems with authentication (with oauth2). So far, this is what I do (in my php service) :
require_once 'google-api-php-client/src/apiClient.php'
const SERVICE_ACCOUNT_NAME = 'email from services account access';
$key = 'content of my private key retrieved from services account access';
$client = new apiClient();
$cred = new apiAssertionCredentials(SERVICE_ACCOUNT_NAME, array('https://www.googleapis.com/auth/androidpublisher'), $key);
$assertion = $cred->generateAssertion(); // This generate my encrypted JWT
I then try to retrieve the access token with this JWT object. The problem is that when I use the access token given I got the error that the developer account does not own the application, which is not true.
(I know this is not the way to do it, but I just wanted to retrieve the access_token using the JWT to understand why it is not working, if I do it as indicated in the google apis documentation it is not working too).
I need to do this API call from a server, so no end-user has to be involved (no manual consent).
I had the same issue, and ultimately discovered that as of right now service accounts can not access the Play API.
I'm not sure when Google is planning on fixing this but you can get around this by creating a web app client ID and setting up a basic login page to first generate a code using the new web app Client data and going to $client->createAuthUrl():
$client = new apiClient();
$key = file_get_contents(KEY_FILE);
$client->setClientId(CLIENT_ID);
$client->setClientSecret(CLIENT_SECRET);
$client->setRedirectUri(MY_WEBAPP_URL);
$client->setDeveloperKey($key);
$client->setScopes(array('https://www.googleapis.com/auth/androidpublisher'));
$authUrl = $client->createAuthUrl();
print "<a class='login' href='$authUrl'>Connect Me!</a>";
This should take you to a Google login page where you should log in with the developer account. When you authorize the app, it will take you back to your web app URL as defined when you set up the client ID with a CODE as a get parameter. You can use to generate a token (and more importantly, a refresh token) like so:
$url = 'https://accounts.google.com/o/oauth2/token';
$fields = array(
'grant_type'=>'authorization_code',
'code'=>$code,
'client_id'=>CLIENT_ID,
'client_secret'=>CLIENT_SECRET,
'redirect_uri'=>MY_WEBAPP_URL
);
// cURL call to OAuth URL with $fields sent as POST
This should return you JSON data with a refresh token. Save this token and use it to make another call whenever you need to generate an access token. You will essentially run the same code you did to get the refresh token, but with different fields:
$fields = array(
'grant_type'=>'refresh_token',
'refresh_token'=>$refresh_token,
'client_id'=>CLIENT_ID,
'client_secret'=>CLIENT_SECRET,
);
This will give you an access token you can use to get purchase data from the following URL:
https://www.googleapis.com/androidpublisher/v1/applications/[PACKAGE]/subscriptions/[SKU]/purchases/[PURCHASE_TOKEN]?access_token=[ACCESS_TOKEN]
The trick is getting the refresh token, once you have that the rest should be pretty straightforward.
I`ve got the same problem. It occurs because you authorize user in Google API who does not own the application and try to get data which belows to your app.
In this topic it is well described. http://support.google.com/googleplay/android-developer/bin/answer.py?hl=en&answer=2528691&topic=16285&ctx=topic
You should authorize by OAuth2 the owner of application, and then use Google API with obtained token.

Using authToken for Google Health Data

We have developed and published an app for Google Health. Now we want to avoid every time logging into the gmail account by asking username and password.
So as to do this i have heard that I can have following options.
1. OAuth
2. Account Manager
3.
The problem with OAuth is that it will go via Android -> Web App -> Health path so i will need to develop an web app as well which we dont wish to do it right now.
So I am trying to use Account Manager, here is my code with which I could get list of accounts and an valid authToken for the selected account.
AccountManager mgr = AccountManager.get(getApplicationContext());
System.out.println("Got account manager");
Account[] accts = mgr.getAccounts();
}
Account acct = accts[0];
AccountManagerFuture<Bundle> accountManagerFuture = mgr.getAuthToken(acct, "android", null, this, null, null);
Bundle authTokenBundle = accountManagerFuture.getResult();
System.out.println("Account name "+accts[0].name);
String authToken = authTokenBundle.get(AccountManager.KEY_AUTHTOKEN).toString();
System.out.println("Got token:"+authToken);
But now I am confused about how to use this token to access the health data.
For getting the demographic feed we used the following code,where we explicitly made user to login into our application.
String queryURL = "https://www.google.com/health/feeds/profile/ui/" + profileId +"/-/DEMOGRAPHICS";
getDemoGrInfoQuery = new Query(new URL(queryURL));
Feed dempGrResultFeed;
globals = new Globals();
dempGrResultFeed = healthService.query(getDemoGrInfoQuery, Feed.class);
And thus we used to get the Feed using the URL.
And now I want to skip the login process and use the above authToken for retrieving the feed. How can this be done?
Any help would be really appreciated!!!
Thanks in advance,
As the standard OAuth procedure is supposed to work, you open the OAuth URL in a WebView (or anything similar) with all the required parameters, users provide Google (not your app) with their user name and password, then google gives you a token which you can use for your future communications.
This way the user doesn't have to give you their login credentials. They give it only to google, and google gives you a token which will authenticate your app every time you use it.
I think you should be good to go with this, since it requires you to open a WebView only once, unless the user logs out of google using your application or denies access to your application.
After getting the token, you just start polling google with that token and never ask user for their login credentials. quite seamless.
UPDATE
After our conversation in chat, let me tell you that you'll have to register an application with google, which will give you an appID, this appID will be used by your Android app to tell google that it is requesting permission on behalf of the Application which this appID refers to.
UPDATE 2
open the Google OAUth with all the parameters, google will give you a code
use that code and create a POST request again to google, and google will now return a long lasting AccessToken
You can then use this AccessToken in all your future communications

Categories

Resources