I am trying to set up a node.js server to validate receipts from AppStore connect In App Purchases I have set up. I have followed this https://github.com/voltrue2/in-app-purchase library but I'm getting an error response saying my receipt is not defined and failed to validate receipt. I'm I doing anything wrong here. I'm still testing on a local server. I want to get it to work before hosting it on Heroku. What would I be doing wrong here?
const iap = require('in-app-purchase');
iap.config({
applePassword: 'MySecretKey',
test: true
});
iap.setup()
.then(() => {
iap.validateOnce(receipt, appleSecretString).then(onSuccess).catch(onError);
})
.catch((error) => {
if (error) {
console.log('Validation error' + error);
}
});
iap.validate(iap.APPLE, function (error, appleResponse) {
console.log(iap.APPLE);
if (error) {
console.log('Failed to validate receipt' + error);
}
if (iap.isValidated(appleResponse)) {
console.log('Validation successful');
}
});
Here are the logs
iapserver:server Listening on port 3000 +0ms
Validation errorReferenceError: receipt is not defined
apple
Failed to validate receiptError: failed to validate purchase
From the error is seems like you're not actually passing the receipt file into the validateOnce function. Somewhere before that code runs you need to set:
const receipt = req.query.receipt;
And invoke your API with something like:
http://localhost:3000/verifyReceipt?receipt=<YOUR_RECEIPT_DATA>
You can verify your receipt beforehand manually to make sure it's valid.
Putting this all together, you'd get something like:
var express = require('express');
var app = express();
const iap = require('in-app-purchase');
app.get('/verifyReceipt', function(req, res){
const receipt = req.query.receipt;
iap.config({
applePassword: 'MySecretKey',
test: true
});
iap.setup()
.then(() => {
iap.validateOnce(receipt, appleSecretString).then(onSuccess).catch(onError);
})
.catch((error) => {
if (error) {
console.log('Validation error' + error);
res.status(400).send({valid: false});
}
});
iap.validate(iap.APPLE, function (error, appleResponse) {
console.log(iap.APPLE);
if (error) {
console.log('Failed to validate receipt' + error);
res.status(400).send({valid: false});
}
if (iap.isValidated(appleResponse)) {
console.log('Validation successful');
res.status(200).send({valid: true});
}
});
});
app.listen(3000);
Note that this will only verify the receipt at purchase, if you're implementing subscriptions you also need to periodically refresh the receipt to make sure they user didn't cancel. Here's a good guide on implementing subscriptions: iOS Subscriptions are Hard
Related
when I send a get request on postman session id is not generated instead it goes on catch block and shows an error occured, please help me solve this problem
const authenticator = new IamAuthenticator({
apikey: process.env.WA_ASSISTANT_APIKEY,
});
const assistant = new AssistantV2({
version: "2019-02-28",
authenticator: authenticator,
url: process.env.WA_ASSISTANT_APIKEY,
});
router.get("/session", async (req, res) => {
//if successfull
try{
const session = await assistant.createSession({
assistantId: process.env.WA_ASSISTANT_ID,
});
res.json(session["result"]);
//error
}catch(err){
res.send("An Error occured while processiong your request!");
console.log(err);
}
})
This is the code
Error
The error suggests that your app is trying to connect to Watson Assistant on localhost. I suspect the error is due to your line -
url: process.env.WA_ASSISTANT_APIKEY
APIKey is very unlikely to be a url.
I have created a simple login api, when I try texting the api in postman am getting the following error:
Error: Can't set headers after they are sent to the client using node js
code that I have written is as follows:
//verify user
exports.verifyuser = (req, res) => {
User.findOne(req.body.email)
.then(user => {
if(!user) {
return res.status(404).send({
message:"no user with email id found"
});
}
res.send(user).then(function(res){
console.log('verify db res',res)
});
}).catch(err => {
if(err.kind === 'ObjectId') {
return res.status(404).send({
message: "Note not found with id "
});
}
return res.status(500).send({
message: "Error retrieving note with id "
});
});
};
Being new to express am unable to recognize where have I gone wrong, also am not getting the correct response like below :
here I have added abc3#gmail.com to the url but am getting response for aaaa#gmail.com
Kindly help me out here, any suggestions would be very helpful. Thank you in advance.
I'm trying to save a variable to a text file, but if the variable isn't found when using spotifyApi.clientCredentialsGrant(), then I want my server to redirect to app.get('/error', function(req, res) {}); which displays a different webpage, but it's returning the error:
(node:11484) UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
How can I get around this error to display the webpage error.html?
I don't have access to EJS or window.location because it conflicts with other files and it's a node.js program, respectively.
app.get('/', function (req, res) {
res.sendFile(path.join(__dirname, '/public', 'homepage.html'));
try {
spotifyApi.clientCredentialsGrant()
.then(function (data) {
// Save the access token so that it's used in future calls
client_cred_access_token = data.body['access_token'];
console.log(client_cred_access_token);
console.log('Client Credentials Success!');
}, function (err) {
console.log('Something went wrong when retrieving an access token', err.message);
throw err;
});
fs.writeFile("./public/client_cred_token.txt", '', function (err) {
console.log('Clearing previous access token');
});
fs.writeFile("./public/client_cred_token.txt", client_cred_access_token, function (err) {
if (err) return console.log(err);
});
fs.readFile('./public/client_cred_token.txt', function (err, data) {
if (err) throw err;
console.log("Saved Client Credentials as: %s", data)
});
}
catch (err) {
res.redirect('/error');
}
});
Key takeaway from the accepted answer is to not send any HTML/files to the server until it's confirmed which one is needed.
You are calling res.sendFile() first and then if you later get an error, you are also calling res.redirect('/error') which means you'll be trying to send two responses to one http request which triggers the error you see. You can't do that.
The solution is to call res.sendFile() at the end of all your other operations so you can then call it when successful and call res.redirect() when there's an error and thus only call one or the other.
In a difference from the other answer here, I've shown you how to code this properly using asynchronous file I/O so the design could be used in a real server designed to serve the needs of more than one user.
const fsp = require('fs').promises;
app.get('/', async function (req, res) {
try {
let data = await spotifyApi.clientCredentialsGrant();
// Save the access token so that it's used in future calls
client_cred_access_token = data.body['access_token'];
console.log(client_cred_access_token);
console.log('Client Credentials Success!');
await fsp.writeFile("./public/client_cred_token.txt", client_cred_access_token);
let writtenData = await fsp.readFile('./public/client_cred_token.txt');
console.log("Saved Client Credentials as: %s", writtenData);
res.sendFile(path.join(__dirname, '/public', 'homepage.html'));
} catch (err) {
console.log(err);
res.redirect('/error');
}
});
app.get('/', function (req, res) {
try {
spotifyApi.clientCredentialsGrant().then(function (data) {
// Save the access token so that it's used in future calls
let client_cred_access_token = data.body['access_token'];
console.log(client_cred_access_token);
console.log('Client Credentials Success!');
// truncate token file
fs.truncateSync("./public/client_cred_token.txt");
// write token to file
fs.writeFileSync("./public/client_cred_token.txt", client_cred_access_token);
// read token from file again
// NOTE: you could use `client_cred_access_token` here
let data = fs.readFileSync('./public/client_cred_token.txt');
console.log("Saved Client Credentials as: %s", data)
// send homepage to client when no error is thrown
res.sendFile(path.join(__dirname, '/public', 'homepage.html'));
}, function (err) {
console.log('Something went wrong when retrieving an access token', err.message);
throw err;
});
} catch (err) {
res.redirect('/error');
}
});
I swapped all asynchron file opreations with the syncron one.
They throw an error and you dont have to deal with callback chain/flow.
Also i moved the sendFile(...) at the botom in the try block, so when a error is thrown from any syncrhonus function call the sendFile is not reached, and your redirect can be sent to the client.
Otherwise you would send the homepage.html to the client, with all headers, and a redirect is not possible.
I am using mochajs for my mean applications server side test cases.The test cases are showing an error, Error: expected 200 "OK", got 403 "Forbidden".
I tried using console logs inside the functions that I am trying to test, but they never get executed. I am unable to find out the issue. Can someone help me with this.
My controller
exports.signin = function(req, res) {
req.body.username = new Buffer(req.body.username, base64').toString('ascii');
User.findOne({
username: req.body.username.toLowerCase()
}, function(err, user) {
if (err) {
//do something
} else if (!user) {
//do something
} else {
//do something
}
});
};
My test case
it('should be able to signin if the credentials are valid', function(done) {
var obj = {
username: new Buffer('demo').toString('base64'),
password: new Buffer('demouser').toString('base64')
};
agent.post('/auth/signin')
.send(obj)
.expect(200)
.end(function(signinErr, res) {
if (signinErr){
console.log(signinErr);
done(signinErr);
}
else{
var temp = res.body;
console.log(temp);
done();
}
});
});
And my route
app.route('/auth/signin').post(users.signin);
I was facing the same issue. I found out that we were forcing SSL in our application using the middleware express-force-ssl by app.use(force-ssl). We used a flag to control this behavior. When I made that false, the test cases ran fine.
So if you are not controlling it with the flag. Try removing that module and running your test cases.
I am doing a basic authentication example. I have Node, Express, and Cookie. I make and store a cookie once the user logs in. Upon refreshing the page, I want to use the cookie to show that the user is still logged in on the response, and provide the information related to that user.
Server side:
// If I put the app.get('/'.....) up here I get the response, but not the page HTML/JS/CSS/etc...
// This uses the /app as the root directory
app.use(express.static(__dirname + '/app'));
// Here I get the page HTML/JS/CSS/etc but can't capture the cookie
app.get('/', function(req, res) {
console.log('I want to get here');
if(req.headers.cookie){
// This parses the cookies into a usable array
var incoming_cookies = cookie.parse(req.headers.cookie);
Person.find({...})
.then( function(results) {
if (weDontFindSomeone) {
console.log('user not found');
res.status(400).send('user not found');
} else {
if (incoming_cookies.uname === loggedIn.uname) {
console.log('Starting with a cookie logged in');
res.status(200).send(results);
} else {
console.log('Some other problem with cookies');
res.status(400).send('Some other problem with cookies');
}
}
})
.catch(function(error){
res.status(400).send('some other error in starting, error: ' + error);
});
} else {
res.status(200).send('Starting from scratch.');
}
});
How do I capture the cookies on the request to the homepage and use that to determine what is served to the client?
Do I put it in the JS on the client side?
If you want to suggest another node module, PLEASE show a working example in a plkr, fiddle, web page example, or the like. I do better studying working code, as it has taken me a bit long to get to this pont :)
Setting the cookie, also on the server side:
app.post('/api/login', function (req, res) {
console.log('Getting a login request');
if (weValidateTheCredentials) {
Person.find({...})
.then(function(personResults) {
if (personResults.rowCount === 0) {
res.status(400).send('user not found');
} else {
console.log('Logging in at \'/api/login\', and sending a cookie.');
// 3 hours max age
res.cookie('UID', req.body.uid, {maxAge: 10800000});
res.cookie('uname', req.body.uname, {maxAge: 10800000});
res.status(200).send(personResults);
}
})
.catch(function(error){
res.status(400).send('some other error in logging in, error: ' + error);
});
} else {
res.status(400).send('login requires a uname and pwhash');
}
});
The method with Cookie is a bit devious, you can use cookie-parser, It's made for express.
It is really simple, there is a example on the home page:
var express = require('express')
var cookieParser = require('cookie-parser')
var app = express()
app.use(cookieParser())
app.get('/', function(req, res) {
console.log("Cookies: ", req.cookies)
})
app.listen(8080)
// curl command that sends an HTTP request with two cookies
// curl http://127.0.0.1:8080 --cookie "Cho=Kim;Greet=Hello"
Or with your code:
var cookieParser = require('cookie-parser');
// Add cookie parser
app.use(cookieParser());
app.get('/', function(req, res) {
console.log('I want to get here');
if(req.cookies){
Person.find({...})
.then( function(results) {
if (weDontFindSomeone) {
console.log('user not found');
res.status(400).send('user not found');
} else {
if (req.cookies.uname === loggedIn.uname) {
console.log('Starting with a cookie logged in');
res.status(200).send(results);
} else {
console.log('Some other problem with cookies');
res.status(400).send('Some other problem with cookies');
}
}
})
.catch(function(error){
res.status(400).send('some other error in starting, error: ' + error);
});
} else {
res.status(200).send('Starting from scratch.');
}
});
// This uses the /app as the root directory
app.use(express.static(__dirname + '/app'));
I was mixing the paradigm of what should be handled by the server and what should be handled by the client.
Using Jquery addon 'jquery-cookie-master', I can check the cookie on the request of the client side with if ($.cookie('attributeWanted')){...}