Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
156 views
in Technique[技术] by (71.8m points)

node.js - Try/catch sends email and creates DB entry even though it shouldn't

I have a problem regarding try/catch in Node.JS, to be more exact in Express. I have an app for selling tickets for festivals, concerts etc. and there is limited number of tickets for each event. However when my server detects that there are no tickets left, creates one anyway and sends mail with the ticket.

app.post(
  "/api/tickets",
  [
    body("email").trim().isEmail().isLength({ min: 8 }).normalizeEmail(),
    body("firstName")
      .trim()
      .isString()
      .isLength({ min: 2 })
      .matches(/^[^0-9_!??÷??/\+=@#$%?&*(){}|~<>;:[]]{2,}$/),
    body("lastName")
      .trim()
      .isString()
      .isLength({ min: 2 })
      .matches(/^[^0-9_!??÷??/\+=@#$%?&*(){}|~<>;:[]]{2,}$/),
    body("phoneNumber")
      .trim()
      .isString()
      .isLength({ min: 8 })
      .matches(
        /^((?<!w)((?(+|00)?48)?)?[ -]?d{3}[ -]?d{3}[ -]?d{3}(?!w))$/
      ),
  ],
  async (req: any, res: any, next: any) => {
    try {
      const errors = validationResult(req);
      if (!errors.isEmpty()) {
        let err = new StatusError("Error while validating body", 400);
        next(err);
      }

      const { id, email, firstName, lastName, phoneNumber } = req.body;

      let eventFound: any;
      const event = await Event.findById(id, (error, result) => {
        if (!error) eventFound = result;
        else {
          let err = new StatusError("No event found", 404);
          next(err);
        }
      });

      Ticket.find({ eventId: event?.id }, (error, tickets) => {
        if (!error) {
          if (event?.toJSON().maxTicketsAmount - 1 < tickets.length) {
            let err = new StatusError("No tickets left", 403);
            next(err);
          }
        } else {
          let err = new StatusError("No tickets found", 404);
          next(err);
        }
      });

      const paymentIntent = await stripe.paymentIntents.create({
        amount: eventFound.ticketPrice, // NEEDS TO BE ABOVE SOME VALUE!!!!!!!
        currency: "pln",
        payment_method_types: ["card"],
        receipt_email: email,
        metadata: { integration_check: "accept a payment" },
      });
      if (!paymentIntent) {
        let err = new StatusError("Creating payment intent failed", 400);
        next(err);
      }

      const ticket = new Ticket({
        email: email.trim(),
        firstName: firstName.trim(),
        lastName: lastName.trim(),
        phoneNumber: phoneNumber.trim(),
        eventId: eventFound.id,
        purchaseDate: new Date(),
      });
      ticket.save((error) => {
        if (error) {
          let err = new StatusError("Error while saving to DB", 500);
          next(err);
        }
      });

      const qr = await toDataURL(ticket.id);
      console.log(ticket.id);

      if (!qr) {
        let err = new StatusError("Error while creating QR Code", 400);
        next(err);
      }

      const mailTemplate = `
      <h1>Hello ${firstName} ${lastName}</h1>
      <p>Thanks for buying ticket for ${eventFound.nameOfEvent}, in ${eventFound.place}, taking place on ${eventFound.dateOfEvent}</p>
      <img src="${qr}">
      `;

      let message = {
        from: env.email,
        to: email,
        subject: `Ticket for ${eventFound.nameOfEvent}`,
        html: mailTemplate,
      };
      transporter.sendMail(message, (error, info) => {
        if (error) {
          return res.status(500).json({ error: error });
        } else console.log("Mail sent:", info.response);
      });

      return res.status(200).send(paymentIntent.client_secret);
    } catch (error) {
      next(error);
    }
  }
);

Middleware for error handling:

app.use(function (err: any, req: any, res: any, next: any) {
  console.error(err.message);
  if (!err.statusCode) err.statusCode = 500;
  res.status(err.statusCode).send(err.message);
});

Custom error class:

class StatusError extends Error {
  code: number;
  constructor(message: string, code: number) {
    super();
    this.message = message;
    this.code = code;
  }
}

export default StatusError;
question from:https://stackoverflow.com/questions/65832631/try-catch-sends-email-and-creates-db-entry-even-though-it-shouldnt

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

The problem is that you your checking of tickets is async, so the rest of the function will execute no matter what. You need to wait for the result before you can decide what to do. The easiest is to nest the remainder of the route handler in the callback of the Ticket.find:

async (req: any, res: any, next: any) => {
  try {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      let err = new StatusError("Error while validating body", 400);
      next(err);
    }
    
    const { id, email, firstName, lastName, phoneNumber } = req.body;
    
    let eventFound: any;
    const event = await Event.findById(id, (error, result) => {
      if (!error) eventFound = result;
      else {
        let err = new StatusError("No event found", 404);
        next(err);
      }
    });
    
    Ticket.find({ eventId: event?.id }, (error, tickets) => {
      if (!error) {
        if (event?.toJSON().maxTicketsAmount - 1 < tickets.length) {
          let err = new StatusError("No tickets left", 403);
          next(err);
          return;
        }
      } else {
        let err = new StatusError("No tickets found", 404);
        next(err);
        return;
      }

      const paymentIntent = await stripe.paymentIntents.create({
        amount: eventFound.ticketPrice, // NEEDS TO BE ABOVE SOME VALUE!!!!!!!
        currency: "pln",
        payment_method_types: ["card"],
        receipt_email: email,
        metadata: { integration_check: "accept a payment" },
      });
      if (!paymentIntent) {
        let err = new StatusError("Creating payment intent failed", 400);
        next(err);
      }
      
      const ticket = new Ticket({
        email: email.trim(),
        firstName: firstName.trim(),
        lastName: lastName.trim(),
        phoneNumber: phoneNumber.trim(),
        eventId: eventFound.id,
        purchaseDate: new Date(),
      });
      ticket.save((error) => {
        if (error) {
          let err = new StatusError("Error while saving to DB", 500);
          next(err);
        }
      });
      
      const qr = await toDataURL(ticket.id);
      console.log(ticket.id);
      
      if (!qr) {
        let err = new StatusError("Error while creating QR Code", 400);
        next(err);
      }
      
      const mailTemplate = `
        <h1>Hello ${firstName} ${lastName}</h1>
        <p>Thanks for buying ticket for ${eventFound.nameOfEvent}, in ${eventFound.place}, taking place on ${eventFound.dateOfEvent}</p>
        <img src="${qr}">
        `;
        
      let message = {
        from: env.email,
        to: email,
        subject: `Ticket for ${eventFound.nameOfEvent}`,
        html: mailTemplate,
      };
      transporter.sendMail(message, (error, info) => {
        if (error) {
          return res.status(500).json({ error: error });
        } else console.log("Mail sent:", info.response);
      });
      
      return res.status(200).send(paymentIntent.client_secret);
    });
    
  } catch (error) {
    next(error);
  }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

2.1m questions

2.1m answers

60 comments

57.0k users

...