Can't retrieve the play store reviews for my app - google-api-nodejs-client

I am trying to get the reviews for my app from the playstore using the new reviews api from the android publisher service.
The app key is me.jadi (https://play.google.com/store/apps/details?id=me.jadi) as you can see it have reviews posted for it.
Here is the code I'm using:
var google = require('googleapis');
var secrets = require('./secrets.json');
var androidpublisher = google.androidpublisher('v2');
var authClient = new google.auth.JWT(
secrets.client_email, null, secrets.private_key,
['https://www.googleapis.com/auth/androidpublisher'], null);
authClient.authorize(function (err, tokens) {
if (err) {
return console.log(err);
}
androidpublisher.reviews.list({ auth: authClient, packageName: 'me.jadi' }, function (err, resp) {
if (err) {
return console.log(err);
}
});
});
It doesn't contain any errors for the auth nor for the actual service request. But the result is always an empty object.
So I'm trying to identify the problem,
is there something wrong with the code
do I need to opt-in specifically somewhere to use the API
does the API have any limitations, like geographic (the service is allowed only for the US devs)
or maybe the service have some bugs because it is still in beta

I found the answer my self, and I'll post it here for future reference.
The problem with my specific case was that I didn't have reviews posted or modified in the last week.
And the API documentation clearly states that it will keep history of the reviews in the last seven days.
Once I get a new review I tried the code and the review was successfully retrieved.

Related

Check subscription validity using Flutters in-app-purchase plugin?

I'm trying to implement in-app purchases using the official Flutter In-App-Purchase plugin. I've got things working, except I can't figure out how to tell if a users subscription is still active or if it expired. Even after I canceled my test subscription, the values I get after connecting and doing queryPastPurchases() are the same as when the subscription was active:
productId: test_subscription_1
transactiondate: 1565682346568
status: null
verificationData
source: IAPSource.GooglePlay
localVerificationData: {
"orderId":"GPA.1234-1234-1234-12345",
"packageName":"com.example.myapp",
"productId":"test_subscription_1",
"purchaseTime":1565682346568,
"purchaseState":0,
"purchaseToken":"<long string>",
"autoRenewing":false
}
serverVerificationData: "<long string>"
Am I supposed to simply hard code my subscription period and compare the current time to purchaseTime + the subscription period? Will that even work across auto-renewals? What if the user changes the date on his phone to a year ago? It seems like there should be some value that should either give me the expiration time or at least a boolean true/false to indicate if the subscription is still valid?
The official in-app purchase plugin handles making the purchase but doesn't supply all of the backend infrastructure you need to handle auto-renewing subscriptions specifically.
The short answer to your question is send this purchase info up to your server and manage subscription status there. Alternatively you can look into a managed solution like purchases_flutter: https://pub.dev/packages/purchases_flutter/
I have used ‘purchases_flutter‘ and the process is straightforward. You can check the status of the subscription by calling the methods which comes with the plugin. Check out this article which includes an example https://medium.com/flutter-community/in-app-purchases-with-flutter-a-comprehensive-step-by-step-tutorial-b96065d79a21
For anyone still having issues, there's a simple solution to validate the receipt on iOS
Here's a simple js snippet that you can use to fetch the actual receipt from Apple and use it to validate the subscription
Note
You will need to generate app specific password for the app from with apple developer account
Further help
https://developer.apple.com/documentation/appstorereceipts/expiration_intent
const axios = require('axios');
const iosValidateReceipt = async (transactionReceipt, isTest = true) =>
new Promise(async (resolve, reject) => {
const url = isTest ? 'https://sandbox.itunes.apple.com/verifyReceipt' : 'https://buy.itunes.apple.com/verifyReceipt';
const data = {
'receipt-data': transactionReceipt,
password: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
};
console.log('iosValidateReceipt - input - ', { url, data });
try {
const response = await axios.post(url, data);
console.log('iosValidateReceipt - success - ', JSON.stringify(response.data, null, 2));
resolve(response.data);
} catch (err) {
console.log('iosValidateReceipt - error -', err);
reject(err);
}
});

How to get the Authentication Provider for actions-on-google on Node using account linking with Auth0?

I have Javascript App running under Node v8.11.2 which uses the Actions-On-Google library. I'm using the V2 API. I have account linking set up with Auth0 and am using the SignIn helper intent. Auth0 is set up to use Google, Facebook and Twitter.
The scopes I use are OPENID, OFFLINE_ACCESS, PROFILE and EMAIL.
Everything is working fine and when the User is authenticated I get an Access Token returned.
My question is, how do I get the Authentication Provider that was selected by the User so that I can use the Access Token correctly to retrieve profile elements such as the display name, email address etc??
The signin object passed to the Sign In Confirmation intent handler just contains the following regardless of the provider selected: -
{"#type":"type.googleapis.com/google.actions.v2.SignInValue","status":"OK"}
Any help greatly appreciated as I have a deadline and this is driving me a bit crazy now!
Thanks,
Shaun
If your question is about how to get the required information when you have your accessToken available then you could use what is shown in this answer.
In node this looks like that:
let link = "https://www.googleapis.com/oauth2/v1/userinfo?access_token="+accessToken;
return new Promise(resolve => {
request(link,(error, response, body) => {
if (!error && response.statusCode === 200) {
let data = JSON.parse(body);
let name = data.given_name ? data.given_name : '';
conv.ask(new SimpleResponse({
speech: "Hello "+ name + "!",
text: "Hello "+ name + "!"
}));
resolve();
} else {
console.log("Error in request promise: "+error);
resolve();
}
})
})
Everything you need should be in the data object.
Hope it helps.

Sails Rest API using Passport-Facebook-Token Strategy Driving Me Nuts

I have a mobile front-end that already has facebook authetication working. I have a Sails REST API that stores user data, posts etc.. I want to add security where facebook users can only POST GET DELETE PUT their own data.
I've read a almost every tutorial for facebook authenticating a web-app, but haven't found many for authenticating with a mobile app to protect the user data. I've tried to get Passport-Facebook-Token working but I just don't understand the little documentation available. I'm coming from a objective-C background so in the node learning curve now.
Here's the link to what I'm working with but I'm obviously missing something: https://github.com/drudge/passport-facebook-token
I have:
AuthController.js
module.exports = {
facebook: function(req, res) {
passport.authenticate('facebook-token', function(error, user, info) {
// do stuff with user
res.ok();
})(req, res);
}
};
api/services/protocols/passport.js
(with some other stuff from default passport sails-generate-auth)
var FacebookTokenStrategy = require('passport-facebook-token');
passport.use('facebook-token', new FacebookTokenStrategy({
clientID : "<my_id>",
clientSecret : "<my_secret>"
},
function(accessToken, refreshToken, profile, done) {
// console.log(profile);
var user = {
'email': profile.emails[0].value,
'name' : profile.name.givenName + ' ' + profile.name.familyName,
'id' : profile.id,
'token': accessToken
}
// You can perform any necessary actions with your user at this point,
// e.g. internal verification against a users table,
// creating new user entries, etc.
return done(null, user); // the user object we just made gets passed to the route's controller as `req.user`
}
));
Do I have to do something with config/routes to make sure it only allows users with access_tokens? I just can't find any resources out there. Passport doesn't even list Passport-Facebook-Token strategy as an option on their site.
thank you for the help

Apigee push notification - one

Apigee's push notification is documented here.
http://apigee.com/docs/api-baas/content/introducing-push-notifications
I tried this with the js sdk that Apigee provides here http://apigee.com/docs/app-services/content/installing-apigee-sdk-javascript. It looks like only the client can generate a notification to itself?
But I have a scenario where I would like to push notifications to multiple clients from a nodejs job that runs once every hour. Something like this, but from the nodejs sdk not from the js sdk.
var devicePath = "devices;ql=*/notifications";
How do I do this?
As remus points out above, you can do this with the usergrid module (https://www.npmjs.com/package/usergrid).
You are basically trying to construct an API call that looks like this (sending a message by referencing a device):
https://api.usergrid.com/myorg/myapp/devices/deviceUUID/notifications?access_token= access_token_goes_here '{"payloads":{"androidDev":"Hello World!!"}}'
Or like this (sending a message by referencing a user who is connected to a device)
https://api.usergrid.com/myorg/myapp/users/fred/notifications?access_token=access_token_goes_here '{"payloads":{"androidDev":"Hello World!!"}}'
You can do this with code that looks something like this:
var options = {
method:'POST',
endpoint:'devices/deviceUUID/notifications',
body:{ 'payloads':{'androidDev':'Hello World!!'} }
};
client.request(options, function (err, data) {
if (err) {
//error - POST failed
} else {
//data will contain raw results from API call
//success - POST worked
}
});
or
var options = {
method:'POST',
endpoint:'users/fred/notifications',
body:{ 'payloads':{'androidDev':'Hello World!!'} }
};
client.request(options, function (err, data) {
if (err) {
//error - POST failed
} else {
//data will contain raw results from API call
//success - POST worked
}
});
Note: the second call, that posts to the users/username/notifications endpoint assumes that you have already made a connection between the user and their device (e.g. POST /users/fred/devices/deviceUUID).

Why am I getting this 'undefined' error?

I'm working on a Meteor project, and for some reason this profile template refuses to work.
I'm using the following code, as well as the accounts-password and accounts-entry packages for user management:
this.route('profile', {
path: '/profile/:username',
data: function() {
var userDoc = Meteor.users.findOne({"username": this.params.username});
var bookCursor = Books.find({owner: userDoc._id});
return {
theUser: userDoc,
theBooks: bookCursor
};
}
});
When I try to go to the profile URL for my test accounts ('misutowolf', and 'test2', respectively), I am given the following error in Chrome's dev console: Exception from Deps recompute function: TypeError: Cannot read property '_id' of undefined, pointing to the use of userDoc._id in the call to Books.find().
This makes no sense whatsoever, as I was able to find a user document with the names in question using meteor mongo with both usernames, in the form db.users.find({username: "misutowolf"}) and db.users.find({username: "test2"}).
I am very confused, not sure what is causing this issue at all.
By default Meteor only publish the currently logged in user info via an automatically setup publication.
What you need to do is push to the client the user info (username) you're trying to use, because if you don't do that, the user you're accessing is not published to the client and you get an undefined error when accessing its _id.
First, setup a dedicated publication (on the server) :
Meteor.publish("userByUsername",function(username){
return Meteor.users.find({
username:username
});
});
Then waitOn this publication in your route :
waitOn:function(){
return this.subscribe("userByUsername",this.params.username);
}
Finally, guard against accessing the user document until it is pushed to the client because even if you are waiting on the subscription, the data method might actually get called even if the subscription is not ready yet.
data: function() {
var userDoc = Meteor.users.findOne({"username": this.params.username});
if(!userDoc){
return;
}
// ...
}