I am running into a problem with a particular part of my code. I have managed to isolate it to this one chunk. It is a POST request which sends back a paymentSuccess : true
object if everything is successful. The front end will then redirect the user to the /confirmation
URL.
However, as soon as the POST request is completed, the following error appears: Error: Can't set headers after they are sent.
Currently stuck on what could be causing the problem and have looked at other questions. I don't see anything interfering and hope that someone could provide some insight.
Below is my code, it is a bit lengthly, I know:
// POST Registration Database Queries
router.post('/checkout', function(req, res, next) {
// Constants for use in queries
var dateRegistered = new Date();
var userIDs = [];
var billingID;
// Register athletes if they don't already exist
pool.getConnection(function(err, connection){
connection.beginTransaction(function(err) {
if (err) {
throw err;
}
// Check to if users are already registered and register them in the database if not
_.each(req.body.users, function(val, key){
var existsQuery = 'SELECT `userId`, `firstName`, `lastName`, `email`, `dateRegistered` from Database.Users WHERE firstName = ? AND lastName = ? AND email = ?';
// Check to see if the user is already in the database
connection.query(existsQuery, [val.firstName, val.lastName, val.email], function(err, result){
if (err) {
return connection.rollback(function() {
console.log(err);
res.send({ registerSuccess: false });
});
} else if(_.isEmpty(result)){ // If the user doesn't exist
var registerQuery = 'INSERT INTO Database.Users (`firstName`, `lastName`, `email`, `dateRegistered`) VALUE ( ?, ?, ?, ?)';
connection.query(registerQuery, [val.firstName, val.lastName, val.email, dateRegistered], function(err, result){
if (err) {
return connection.rollback(function() {
console.log(err);
res.send({ registerSuccess: false });
});
} else {
console.log(result);
connection.commit(function(err) {
if (err) {
return connection.rollback(function() {
console.log(err);
res.send({ registerSuccess: false });
});
} else {
console.log("User registered successfully!");
userIDs.push(result.insertId);
res.send({ registerSuccess: true });
}
});
}
});
} else { // If the user already exists
console.log("User already exists!");
userIDs.push(result[0].userId);
res.send({ registerSuccess: true });
}
});
});
});
});
// Register billing user into the database if they don't already exist
pool.getConnection(function(err, connection){
connection.beginTransaction(function(err) {
if (err) {
throw err;
}
// Confirm if payment is successful before continuing
gateway.transaction.sale({
amount: req.body.payment.totalCost,
paymentMethodNonce: req.body.paymentData.paymentNonce,
customer: {
firstName: req.body.billing.firstName,
lastName: req.body.billing.lastName,
// company: "Braintree",
phone: req.body.billing.phoneNumber,
// fax: "312-555-12346",
// website: "http://www.example.com",
email: req.body.billing.email
},
billing: {
firstName: req.body.billing.firstName,
lastName: req.body.billing.lastName,
// company: "Braintree",
streetAddress: req.body.billing.address,
// extendedAddress: "Suite 403",
locality: req.body.billing.city,
region: req.body.billing.province,
postalCode: req.body.billing.postalCode,
countryCodeAlpha2: "CA"
},
options: {
submitForSettlement: true
},
}, function(err, result) {
var paymentResult = result;
if (result.success == true) {
// If the payment is successful, make the queries for the entries
var existsQuery = 'SELECT `userId`, `firstName`, `lastName`, `email`, `dateRegistered` from Database.Users WHERE firstName = ? AND lastName = ? AND email = ?';
// Check to see if the user is already in the database
connection.query(existsQuery, [req.body.billing.firstName, req.body.billing.lastName, req.body.billing.email], function(err, result){
if (err) {
return connection.rollback(function() {
console.log(err);
res.send({ paymentSuccess : false });
});
} else if(_.isEmpty(result)){ // If the user doesn't already exist
console.log("Billing user does not exist!");
sql = 'INSERT INTO Database.Users (`firstName`, `lastName`, `email`, `address`, `cellNumber`, `city`, `phoneNumber`, `postalCode`, `province`, `dateRegistered`) VALUE ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
data = [req.body.billing.firstName, req.body.billing.lastName, req.body.billing.email, req.body.billing.address, req.body.billing.cellNumber, req.body.billing.city, req.body.billing.phoneNumber, req.body.billing.postalCode, req.body.billing.province, dateRegistered];
} else { // If the user already exists
console.log("Billing user already exists!");
billingID = result[0].userId;
sql = 'UPDATE Database.Users SET `address` = ?, `cellNumber` = ?, `city` = ?, `phoneNumber` = ?, `postalCode` = ?, `province` = ? WHERE userID = ?';
data = [req.body.billing.address, req.body.billing.cellNumber, req.body.billing.city, req.body.billing.phoneNumber, req.body.billing.postalCode, req.body.billing.province, billingID];
}
// Add or update the billing user
connection.query(sql, data, function(err, result){
if (err) {
return connection.rollback(function() {
console.log(err);
res.send({ paymentSuccess : false });
});
} else {
// Insert transaction into the database
var fees = (paymentResult.transaction.amount * .029) + 0.3;
var timeStamp = new Date();
var rawResponse = JSON.stringify(paymentResult.transaction);
var transactionID = paymentResult.transaction.id;
var totalCost = paymentResult.transaction.amount;
var transaction = [billingID, paymentResult.transaction.id, paymentResult.transaction.merchantAccountId, paymentResult.transaction.amount, fees, paymentResult.transaction.currencyIsoCode, paymentResult.transaction.paymentInstrumentType, timeStamp, 0, 0, rawResponse];
connection.query('INSERT INTO Database.Transactions (userID, braintreeTransactionID, braintreeMerchantAccount, amount, fees, currency, paymentInstrumentType, timeStamp, processed, refunded, rawResponse) VALUE (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', transaction, function(err, result) {
if (err) {
return connection.rollback(function() {
console.log(err);
res.send({ paymentSuccess : false });
});
} else {
console.log("Transaction has been recorded!");
// Record tournament entries
for(j = 0; j < userIDs.length; j++){
var tournamentUser = [userIDs[j], req.body.tournament.chosen, result.insertId, "Temp Val"];
var sql = "INSERT INTO Database.tournamentEntries (userID, tournamentID, transactionID, skillLevel) VALUE (?, ?, ?, ?)";
connection.query(sql, tournamentUser, function(err, result){
if (err) {
return connection.rollback(function() {
console.log(err);
res.send({ paymentSuccess : false });
});
} else {
connection.commit(function(err) {
if (err) {
return connection.rollback(function() {
console.log(err);
res.send({ paymentSuccess: false });
});
} else {
console.log("Tournament entries were successful!");
userIDs.push(result.insertId);
res.send({ paymentSuccess : true });
}
});
}
});
}
}
});
}
});
});
} else { // Payment has failed
res.send({ paymentSuccess : false });
}
});
});
});
});
_.each
is a synchronous/blocking call. However, inside that you are make db.query
calls which are asynchronous. So basically you will not know which of the callbacks are sending error or data and whether your code should stop at that.
Due to asynchronous nature of your methods, even if one the res.send
method is called due to error or success, other async calls are not terminated since they are already fired up by _.each
method.
You would need to fire those either one by one or parallel but most importantly need a final callback which should get called when any one them has error or all of them completed successfully and then only you should make the res.send
method.
I would suggest having look at the async
npm module. Look at async.each
or async.whilst
method. This can probably be written using async as follows -
async.each(req.body.users, function(item, callback){
// call your db methods here ...
// once they are done for error call callback(error) when successfull call with callback(null)
}, function(err){
// the final call back
// if any of the query produced an error, err would equal that error
if(error){
res.send("error");
}
else
{
// success
res.send("success");
}
});
I see that you have two registrations going on inside the same route and you are calling res.send from inside both of them.
So in that case, the one that finishes first is the response you get on the client side and the one that finished next is causing the error.
Instead of calling res.send from everywhere you can call it inside a callback and inside the callback you can check if both the registrations are completed and only then send the response.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.