@ITcat 주문&결제 API

usnim·2023년 11월 13일
0

project

목록 보기
7/20

주문 & 결제

원하는 공연을 클릭 해 공연상세페이지로 이동 후
날짜와 시간을 선택 후 카카오맵 api를 사용해 지도에서 공연장의 위치 좌표를 표시해주고 좌석을 선택해 결제하기 버튼을 클릭

선택한 공연 정보들을 DB에서 불러오고 주문에 필요한 금액과 현 유저의 포인트를 조회해서 포인트가 부족하면 경고문구를 출력 후
카카오페이 api를 활용해서 포인트를 충전할 수 있도록 구현

카카오페이 api로 포인트를 충전하고 결제를 완료하면
(QR코드를 작성해 DB와 AWS S3에 업로드)
유저의 마이페이지 부분에서 결제완료한 결제내역과 그 내역과 좌석을 보여줄 수 있게했다.

orderController.js

const orderService = require("../services/orderService");

const addReservation = async (req, res) => {
  try {
    const userId = req.user.id;
    const { seatIds, itemOptionsId } = req.body;
    const price = parseInt(req.body.price);
    const itemId = req.params.itemId;

    const result = await orderService.addReservation(
      userId,
      itemId,
      seatIds,
      itemOptionsId,
      price
    );

    return res.status(200).json({ message: "success", data: result });
  } catch (error) {
    return res.status(error.statusCode || 500).json({ message: error.message });
  }
};

const getReservation = async (req, res) => {
  try {
    const userId = req.user.id;
    const status = req.query.status;

    const readReservation = await orderService.getReservation(userId, status);

    return res.status(200).json({ data: readReservation });
  } catch (error) {
    return res.status(error.statusCode || 500).json({ message: error.message });
  }
};

const pointCharging = async (req, res) => {
  try {
    const userId = req.user.id;
    const point = parseInt(req.body.point);
    const remainingPoint = parseInt(req.body.remainingPoint);

    const chargingPoint = await orderService.pointCharge(
      userId,
      point,
      remainingPoint
    );

    return res.status(200).json({
      message: "pointChange",
      data: chargingPoint,
    });
  } catch (error) {
    return res.status(error.statusCode || 500).json({ message: error.message });
  }
};

const pointDeduction = async (req, res) => {
  try {
    const userId = req.user.id;
    const s3BucketName = process.env.AWS_S3_BUCKET;

    const remainingPoint = parseInt(req.body.remainingPoint);
    const totalAmount = parseInt(req.body.totalAmount);
    const { reservationIds, seatIds, seatNames, title, date, time, itemId } =
      req.body;

    const paymentComplete = await orderService.paymentComplete(
      userId,
      reservationIds,
      seatIds,
      itemId,
      remainingPoint,
      totalAmount
    );

    const qrcode = await orderService.generateQRCodeAndUpload(
      reservationIds,
      userId,
      title,
      date,
      time,
      seatNames,
      s3BucketName
    );

    return res
      .status(200)
      .json({ message: "pointChange", data: paymentComplete, qrcode: qrcode });
  } catch (error) {
    return res.status(error.statusCode || 500).json({ message: error.message });
  }
};

module.exports = {
  addReservation,
  getReservation,
  pointCharging,
  pointDeduction,
};

orderService.js

const orderDao = require("../models/orderDao");
const QRCode = require("qrcode");
const AWS = require("aws-sdk");
const appDataSource = require("../utils/database");

const s3 = new AWS.S3({
  region: process.env.AWS_REGION,
  credentials: {
    accessKeyId: process.env.AWS_ACCESS_KEY_ID,
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
  },
});

const addReservation = async (userId, itemId, seatIds, itemOptionId, price) => {
  // seatId가 배열일 경우에 대한 처리
  if (Array.isArray(seatIds)) {
    return Promise.all(
      seatIds.map((seatId) =>
        orderDao.createReservation(userId, itemId, seatId, itemOptionId, price)
      )
    );
  }

  // 배열이 아니면 그대로 createReservation 호출
  return orderDao.createReservation(
    userId,
    itemId,
    seatIds,
    itemOptionId,
    price
  );
};

const getReservation = async (userId, status) => {
  const statusQuery = async () => {
    const statusOption = {
      complete: "complete",
      cancel: "cancel",
      default: "pending",
    };
    return statusOption[status] || statusOption.default;
  };
  const statusMatch = await statusQuery(status);

  const result = await orderDao.selectReservation(userId, statusMatch);

  return result;
};

const pointCharge = async (userId, point, remainingPoint) => {
  const resultPoint = point + remainingPoint;
  const result = await orderDao.updateUserCredit(userId, resultPoint);

  return result;
};

const paymentComplete = async (
  userId,
  reservationIds,
  seatIds,
  itemId,
  remainingPoint,
  totalAmount
) => {
  const resultPoint = remainingPoint - totalAmount;
  const sale = reservationIds.length;

  try {
    await appDataSource.transaction(async (transaction) => {
      await orderDao.updateUserCredit(userId, resultPoint);
      await orderDao.updateReservationStatusComplete(
        userId,
        reservationIds,
        transaction
      );
      await orderDao.updateSeatBookingStatus(seatIds, transaction);
      await orderDao.increaseItemSale(sale, itemId, transaction);
    });
  } catch (error) {
    console.error("Error in paymentComplete transaction:", error);
    throw error;
  }

  return;
};

const generateQRCodeAndUpload = async (
  reservationIds,
  userId,
  title,
  date,
  time,
  seatNames,
  s3BucketName
) => {
  try {
    const data = {
      유저아이디: userId,
      공연이름: title,
      공연날짜: date,
      공연시간: time,
      좌석번호: seatNames.map((seat) => seat.trim()).join(", "),
    };

    const dataText = JSON.stringify(data);

    const qrCodeImageBuffer = await QRCode.toBuffer(dataText);

    const combinedReservationIds = reservationIds.join("-");

    const s3UploadParams = {
      Bucket: s3BucketName,
      Key: `qrcodes/${combinedReservationIds}.png`,
      Body: qrCodeImageBuffer,
      ContentType: "image/png",
    };

    const s3UploadResult = await s3.upload(s3UploadParams).promise();

    const imageUrl = s3UploadResult.Location;

    const qrcode = await orderDao.updateReservationQRcodeUrl(
      imageUrl,
      reservationIds
    );

    return qrcode;
  } catch (error) {
    console.error("Error in generateQRCodeAndUpload:", error);
    throw error;
  }
};

module.exports = {
  addReservation,
  getReservation,
  pointCharge,
  paymentComplete,
  generateQRCodeAndUpload,
};

orderDao.js

const appDataSource = require("../utils/database");

const createReservation = async (
  userId,
  itemId,
  seatIds,
  itemOptionId,
  price
) => {
  const result = await appDataSource.query(
    `
  INSERT 
  INTO reservations (user_id , item_id , seat_id,item_options_id , amount)
  VALUES
  (?,?,?,?,?)
  `,
    [userId, itemId, seatIds, itemOptionId, price]
  );

  return result;
};

const selectReservation = async (userId, statusMatch) => {
  const result = await appDataSource.query(
    `
    SELECT r.id AS reservationId ,
		r.user_id AS userId ,
		u.credit AS remainingPoint,
		i.title AS title,
		i.image AS image,
    i.id AS itemId,
    io.id AS itemOptionId,
		DATE_FORMAT(io.event_date , '%Y-%m-%d')AS date,
		io.event_time AS time,
		l.name AS locationName,
    r.qrcode_url AS qrcodeUrl,
    s.id AS seatId,
		CONCAT(s.seat_row , s.seat_col) AS seatName,
		CAST(r.amount AS UNSIGNED) AS amount
		FROM reservations r
		JOIN users u ON r.user_id = u.id
		JOIN items i ON r.item_id = i.id 
		JOIN item_options io ON r.item_options_id = io.id 
		JOIN seats s ON r.seat_id = s.id 
		JOIN locations l ON s.location_id  = l.id 
		WHERE r.status  = '${statusMatch}' AND r.user_id = ?;
            `,
    [userId]
  );

  return result;
};

const updateUserCredit = async (userId, resultPoint) => {
  const result = await appDataSource.query(
    `
  UPDATE users 
  SET credit = ?
  WHERE id = ?
  `,
    [resultPoint, userId]
  );
  return result;
};

const updateReservationStatusComplete = async (
  userId,
  reservationIds,
  transaction
) => {
  const result = await transaction.query(
    `
  UPDATE reservations
  SET status = 'complete'
  WHERE id IN (?) AND user_id = ?
  `,
    [reservationIds, userId]
  );
  return result;
};

const increaseItemSale = async (sale, itemId, transaction) => {
  const result = await transaction.query(
    `
  UPDATE items
  SET sale = sale + ?
  WHERE id = ?
  `,
    [sale, itemId]
  );
  return result;
};

const updateSeatBookingStatus = async (seatIds, transaction) => {
  const result = await transaction.query(
    `
  UPDATE seats
  SET is_booked = 1
  WHERE id IN (?)
  `,
    [seatIds]
  );
  return result;
};

const updateReservationQRcodeUrl = async (url, reservationIds) => {
  const result = await appDataSource.query(
    `
  UPDATE reservations
  SET qrcode_url = ?
  WHERE id IN (?)
  `,
    [url, reservationIds]
  );
  return result;
};

module.exports = {
  createReservation,
  selectReservation,
  updateUserCredit,
  updateReservationStatusComplete,
  updateSeatBookingStatus,
  increaseItemSale,
  updateReservationQRcodeUrl,
};
profile
🤦‍♂️

0개의 댓글