Android expiry time of In-app subscription item in android - android

I am trying to get expiry time or status of subscription to ensure if user is paying regularly for my item or not . When i query using
Purchase monthlySubscription = inv.getPurchase("itemName");
or
ArrayList<String> ownedSkus = ownedItems.getStringArrayList("INAPP_PURCHASE_ITEM_LIST");
It returns following data
{
"packageName":"com.abcPackage",
"productId":"auto1week",
"purchaseTime":1453369299644,
"purchaseState":0,
"developerPayload":"PAY_LOAD",
"purchaseToken":"TOKEN",
"autoRenewing":true
}
The problem is , purchaseTime remains same after several weeks which is supposed to be change after every purchase.
I tried google Play developers API
https://developers.google.com/android-publisher/#subscriptions
but i am having a hard time implementing it on my android device .
I will be grateful if someone can guide me step by step process to get this data on android device.
https://developers.google.com/android-publisher/api-ref/purchases/subscriptions
Any help in this regard will be highly appreciated.

Not sure if this will help, but here is my server side code (in java) that connects to the developer API and returns the expiration of the subscription.
I created a Service Account in the Google Developer Console, and followed the somewhat obtuse instructions to create a key file in src/resources/keys.json. APPLICATION_NAME is the package name of my app, and PRODUCT_ID is the subscription ID from the Google PLAY developer console.
Sorry it's not really 'step by step' as you asked for, but I also am doing verification on the server side instead of on the client. I suppose on the client you could do some sort of soft-verification by checking purchaseState == 0 (1=cancelled, 2=refunded), and autoRenewing==true. You may get stuck there if they cancel though, since you are still supposed to provide service through the duration of the subscription.
public static Long doSomeWork(String token){
log.debug("Google Validation: Doing some work:" + token);
try{
// Creating new Trusted Transport
HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
// Getting Auth Creds
Credential cred = getAuthCredential();
// Building Android Publisher API call
AndroidPublisher ap = new AndroidPublisher.Builder(httpTransport, JSON_FACTORY, cred)
.setApplicationName(APPLICATION_NAME).build();
// Get Subscription
AndroidPublisher.Purchases.Subscriptions.Get get = ap.purchases().subscriptions().get(
APPLICATION_NAME,
PRODUCT_ID,
token);
SubscriptionPurchase subscription = get.execute();
log.debug(subscription.toPrettyString());
log.debug("DONE (not null)");
return subscription.getExpiryTimeMillis();
} catch(IOException ex) {
ex.printStackTrace();
} catch (GeneralSecurityException ex2) {
ex2.printStackTrace();
}
log.debug("DONE (failure) (0)");
return 0L;
}
private static Credential getAuthCredential(){
log.debug("getAuthCredential");
try{
//Read the credentials from the keys file. This file is obtained from the
// Google Developer Console (not the Play Developer Console
InputStream is = GoogleReceiptValidation.class.getClassLoader().getResourceAsStream("keys.json");
String str = IOUtils.toString(is);
is.close();
JSONObject obj = new JSONObject(str);
InputStream stream = new ByteArrayInputStream(obj.toString().getBytes());
//This is apparently "beta functionality".
GoogleCredential creds = GoogleCredential.fromStream(stream);
creds = creds.createScoped(Collections.singleton(AndroidPublisherScopes.ANDROIDPUBLISHER));
return creds;
} catch (IOException ex){
ex.printStackTrace();
} catch (JSONException ex2){
ex2.printStackTrace();
}
log.debug("No Creds found - returning null");
return null;
}

Related

Verifying in-app purchase from Google Play

I'm working on an Azure Functions app that will make calls to Google to verify in-app purchases users make in my mobile app.
I'm using the Google.Apis.AndroidPublisher.v3 NuGet package with the following code to verify an in-app purchase.
public async Task<bool> IsValidPurchase(string bundleId, string receiptId, string purchaseToken, string developerPayload)
{
try
{
var isValidPurchase = true;
var request = _googleService.Purchases.Products.Get(bundleId, receiptId, purchaseToken);
var purchaseState = await request.ExecuteAsync();
if (purchaseState.DeveloperPayload != developerPayload)
isValidPurchase = false;
if (purchaseState.PurchaseState != 0)
isValidPurchase = false;
return isValidPurchase;
}
catch (Exception e)
{
throw new Exception(e.Message);
}
}
I keep getting "Bad Request - Invalid Value" error.
My mobile users will be purchasing auto-renewing subscriptions and after a successful purchase on an Android phone, I get the following response -- see image below:
The verification method asks for bundleId, receiptId, purchaseToken and developerPayload.
The bundleId and purchaseToken are pretty clear so I'm thinking the error I get is either due to receiptId or developerPayload.
I tried using both Id and ProductId values from the response -- see image above -- but I keep getting the same error:
Bad request - Invalid Value
Any idea what's wrong here? Am I using the wrong values? I'd appreciate some pointers here.

How multiple users use google cloud speech at the same time

I'm building an app that uses Google Cloud Speech.
I have a Google Service account key in my app, and I use it to call the API.
It works well when used by one user, but does not work when multiple users use it at the same time.
For example, only one user is available or all are unavailable.
The rights of the service account key are project owner.
I think it's a service account key issue...
How do I fix it?
private class AccessTokenTask extends AsyncTask<Void, Void, AccessToken> {
#Override
protected AccessToken doInBackground(Void... voids) {
final SharedPreferences prefs = mContext.getSharedPreferences(PREFS, Context.MODE_PRIVATE);
String tokenValue = prefs.getString(PREF_ACCESS_TOKEN_VALUE, null);
long expirationTime = prefs.getLong(PREF_ACCESS_TOKEN_EXPIRATION_TIME, -1);
// Check if the current token is still valid for a while
if (tokenValue != null && expirationTime > 0) {
if (expirationTime > System.currentTimeMillis() + ACCESS_TOKEN_EXPIRATION_TOLERANCE) {
return new AccessToken(tokenValue, new Date(expirationTime));
}
}
final InputStream stream = mContext.getResources().openRawResource(R.raw.credential);
try {
final GoogleCredentials credentials = GoogleCredentials.fromStream(stream).createScoped(SCOPE);
final AccessToken token = credentials.refreshAccessToken();
prefs.edit()
.putString(PREF_ACCESS_TOKEN_VALUE, token.getTokenValue())
.putLong(PREF_ACCESS_TOKEN_EXPIRATION_TIME, token.getExpirationTime().getTime())
.apply();
return token;
} catch (IOException e) {
Log.e(TAG, "Failed to obtain access token.", e);
}
return null;
}
#Override
protected void onPostExecute(AccessToken accessToken) {
mAccessTokenTask = null;
final ManagedChannel channel = new OkHttpChannelProvider()
.builderForAddress(GOOGLE_API_HOSTNAME, GOOGLE_API_PORT)
.nameResolverFactory(new DnsNameResolverProvider())
.intercept(new GoogleCredentialsInterceptor(new GoogleCredentials(accessToken)
.createScoped(SCOPE)))
.build();
mApi = SpeechGrpc.newStub(channel);
// Schedule access token refresh before it expires
if (mHandler != null) {
mHandler.postDelayed(mFetchAccessTokenRunnable,
Math.max(accessToken.getExpirationTime().getTime() - System.currentTimeMillis() - ACCESS_TOKEN_FETCH_MARGIN, ACCESS_TOKEN_EXPIRATION_TOLERANCE));
}
}
}
This code is the code that calls 'credential.json' file on Android and gets 'Access token'.
The server for this app is python and communicates via http.
https://github.com/GoogleCloudPlatform/android-docs-samples/tree/master/speech/Speech
The description in the link above tells you to delegate the authentication to the server.
I want to write that part with python code.
What should I do?
In the link you provided in the description, they suggest you to read first the basic authentication concepts document. In your case, use a service account for the Android application.
I understand that you have already been able to provide end user credentials to a Google Cloud Platform API, as for example Cloud Speech API.
If you want to authenticate multiple users to your application you should use instead Firebase authentication. The link contains a brief explanation and a tutorial.
There are several Python client libraries for GCP that you can use, depending on what operations do you want to perform on the server. And regarding Python authentication on the server side, this documentation shows how the authentication for Google Cloud Storage works (have this example in mind as a reference).

Create GoogleCredential by Access Token

I want to design an Android file viewer for Google Drive.
At first, I implemented the app by using of the Google Android API, as follows,
private void retrieveNextPage(){
if(mHasMore == false)
return;
Query query = new Query.Builder().setPageToken(mNextPageToken).build();
com.google.android.gms.drive.Drive.DriveApi.query(getGoogleApiClient(), query).setResultCallback(metadataBufferResultResultCallback);
}
However, the Android Drive API only allows the app to view and fetch the files that created by itself. I cannot access other files on the drive through the app.
Therefore, I turned to another option, directly manipulate the Java Drive API.
According to the example on developer guide for Java,
https://developers.google.com/drive/web/quickstart/quickstart-java
The users have to manually copy and paste the "Authorization Code" between the browser and app, which is not a practical way to acquire the Access Token in Android.
To come out a new way, I used the GoogleAuthUtil in Android API to acquire the Access Token, coincided with the GoogleCredential and Drive in Java API to fetch the file list, as follows,
private static List<File> retrieveFiles(Drive service) throws IOException{
List<File> result = new ArrayList<File>();
Files.List request = service.files().list();
do {
try{
FileList fileList = request.execute();
result.addAll(fileList.getItems());
request.setPageToken(fileList.getNextPageToken());
}catch (IOException e){
Log.d(dbgT + "JavaRetrieveFiles", "Retrieved Failed");
request.setPageToken(null);
}
}while (request.getPageToken() != null && request.getPageToken().length() > 0);
return result;
}
private class RetrieveTokenTask extends AsyncTask<String, Void, String>{
#Override
protected String doInBackground(String... params){
String accountName = params[0];
String scopes = "oauth2:" + "https://www.googleapis.com/auth/drive";
String token = null;
try{
token = GoogleAuthUtil.getToken(getApplicationContext(), accountName, scopes);
}
catch (IOException e){
Log.e(excpTAG, "IO Exception: " + e.getMessage());
}
catch (UserRecoverableAuthException e){
startActivityForResult(e.getIntent(), REQ_SIGN_IN_REQUIRED);
}
catch (GoogleAuthException e)
{
Log.e(excpTAG, "GoogleAuthException: " + e.getMessage());
}
return token;
}
#Override
protected void onPostExecute(String s){
super.onPostExecute(s);
//Get Access Token
Log.d( dbgT + "Token", s);
EditText tokenText = (EditText) findViewById(R.id.tokenText);
tokenText.setText(s);
EditText fileNameText = (EditText) findViewById(R.id.editTextMeta);
GoogleCredential credential = new GoogleCredential().setAccessToken(s);
HttpTransport httpTransport = new NetHttpTransport();
JsonFactory jsonFactory = new JacksonFactory();
Drive service = new Drive.Builder(httpTransport, jsonFactory, null).setHttpRequestInitializer(credential).build();
List<File> fileList;
try{
fileList = retrieveFiles(service);
for(int i=0; i< fileList.size(); i++)
fileNameText.append(fileList.get(i).getTitle());
}catch(IOException e){
Log.d(dbgT + "RetrieveFileList", "IO Exception" );
}
}
}
Unfortunately, the app always crashes by the causing of NetworkOnMainThreadException when request.execute() in retrieveFiles is invoked.
I checked my access token s, it is usually in form of ya29.xxx...etc., and it can also be passed to my other .NET program for retrieving files from Google Drive. Therefore I can certain the access token is correct.
So my question is, how to create a correct GoogleCredential by using of access token, instead of applying authorization code in setFromTokenResponse ?
Thanks in advance.
Many thanks for Andy's tips, this problem is simply caused by the network operations occurs on the main thread, which is a very basic newbie error.
The Drive in Google Drive SDK for Java, using network libraries without any background/thread worker, and now it is functional after I put the retrieveFiles() into background.
Applying the GoogleAuthUtil in Google Play Android SDK to acquire the access token, and followed by GoogleCredential+Drive in Java SDK that use the token to do the file operation in Google Drive.
This is a right way to avoid the scope restriction in Android SDK for Google Drive, allowing the developers to acquire the full permissive of accessing Google Drive.

Retrieve Google+ activities returns 403, "Access Not Configured" in Android, using GoogleIO13 sample app code

I am tryng to use the Google IO 2013 code sample, specifically the SocialStreamFragment to display google plus post based on a hashtag search.
Steps done:
Integrated SocialStreamFragment.java class and associated clasees, etc, in my project.
In the Console API , I have enabled Google+ API for my project
In the Console API I generated a Simple API Access key for Android Apps, configuring my app's package and debug/prod keys as allowed app.
I copied the Simple Access Key as Config.API_KEY.
However the code below to get the Activities/Posts fails with IOException : 403, "Access Not Configured".
public List<Activity> loadInBackground() {
mIsLoading = true;
// Set up the HTTP transport and JSON factory
HttpTransport httpTransport = new NetHttpTransport();
JsonFactory jsonFactory = new GsonFactory();
// Set up the main Google+ class
Plus plus = new Plus.Builder(httpTransport, jsonFactory, null)
.setApplicationName(NetUtils.getUserAgent(getContext()))
.setGoogleClientRequestInitializer(
new CommonGoogleClientRequestInitializer(Config.API_KEY))
.build();
ActivityFeed activities = null;
try {
activities = plus.activities().search(mSearchString)
.setPageToken(mNextPageToken)
.setOrderBy("recent")
.setMaxResults(MAX_RESULTS_PER_REQUEST)
.setFields(PLUS_RESULT_FIELDS)
.execute();
mHasError = false;
mNextPageToken = activities.getNextPageToken();
} catch (IOException e) {
e.printStackTrace();
mHasError = true;
mNextPageToken = null;The
}
return (activities != null) ? activities.getItems() : null;
}
What am I missing or doing wrong?
My project has also in API Console configured Client IDs for installed applications. Can this be a problem?
Have you got the Google+ API enabled in the API console?

Android - unable to use OAuth access token to retrieve Google Reader feeds

I need to obtain OAuth2 authentication token to pass it to the server so it can fetch list of Google Reader feeds for the user. Server is .NET - I have no access to it or to it's code but most likely it is using unofficial Reader API
I was able to use Android Account manager to obtain valid token for this purpose with the following code (notice that authTokenType="reader")
Account account = accounts[0];
manager.getAuthToken(account, "reader", null, this, new AccountManagerCallback<Bundle>() {
public void run(AccountManagerFuture<Bundle> future) {
try {
// If the user has authorized your application to use the tasks API
// a token is available.
String token = future.getResult().getString(AccountManager.KEY_AUTHTOKEN);
// Now you can send the token to API...
cacheManager.putString(GOOGLE_AUTH, token);
GoogleReaderManager.startAddFeedActivity(AddGoogleReaderSourcesActivity.this);
finish();
} catch (OperationCanceledException e) {
Log.e(TAG, "User cancelled", e);
finish();
} catch (Exception e) {
Log.e(TAG, "Failed to obtain Google reader API_KEY", e);
}
}
}, null);
The code above works fine when I send token to the server side .Net app: the app is able to retrieve the list of Reader feeds.
The problem is that this only works for "Google inside" devices. On Nook I have no such luck since there's no way that I was able to find to add Google account to the account manager. So I'm trying to it using OAuth 2 protocol as described here
It works fine as far as obtaining the token: User approves the app from the mobile page which returns the code token which then mobile app exchanges for the Auth token. However this token will not work with the server process. I have a feeling that perhaps I'm using the wrong scope in this URL:
https://accounts.google.com/o/oauth2/auth?response_type=code&scope=https://www.google.com/reader/api/0/subscription/list&redirect_uri=http://localhost&approval_prompt=force&state=/ok&client_id={apps.client.id}
Scopes that I did try in various combinations:
https://www.google.com/reader/api
https://www.google.com/reader/api/0
https://www.google.com/reader/api/0/subscription/list
https://www.google.com/reader/api+https://www.google.com/reader/atom
Here's example of JSON that is returned from get token POST
{"expires_in":3600,
"token_type":"Bearer",
"access_token":"ya29.AHES6ZSEvuUb6Bvd2DNoMnnN_UnfxirZmf_RQjn7LptFLfI",
"refresh_token":"1\/bUwa5MyOtP6VyWqaIEKgfPh08LNdawJ5Qxz6-qZrHg0"}
Am I messing up scope or token type? Not sure how to change a token type. Any other ideas?
P.S. Google account login page asks: Manage your data in Google Reader, that's why I suspect that the scope is wrong
I got it working for https://www.google.com/reader/api/0/subscription/list. So thought of sharing with you.
I have valid access_token:
This is what i tried to resolve it (partially) :
Google provides OAuth 2.o playgound; where they actually simulate all aspects of OAuth 2.0 as well as final API call to fetch data.
I found this very helpful as it clearly shows what is being sent to request.
Here is the URL : https://developers.google.com/oauthplayground/
Using this, i tweaked my api call below and it works :)
public static String getReaderContent(String accessToken){
String url = "https://www.google.com/reader/api/0/subscription/list" ;
HttpClient client = new HttpClient();
GetMethod method = new GetMethod(url);
String response="";
method.setRequestHeader("Authorization", "OAuth "+accessToken);
try {
int statusCode = client.executeMethod(method);
String response= method.getResponseBodyAsString();
System.out.println("response " + responseStr);
} catch (HttpException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return response;
}
So this works properly fine for getting subscription list; but have not been able to make it work for reader api which you have mentioned in your question.
Let me know if you have got way around google reader API.

Categories

Resources