원하는 공연을 클릭 해 공연상세페이지로 이동 후
날짜와 시간을 선택 후 카카오맵 api를 사용해 지도에서 공연장의 위치 좌표를 표시해주고 좌석을 선택해 결제하기 버튼을 클릭
선택한 공연 정보들을 DB에서 불러오고 주문에 필요한 금액과 현 유저의 포인트를 조회해서 포인트가 부족하면 경고문구를 출력 후
카카오페이 api를 활용해서 포인트를 충전할 수 있도록 구현
카카오페이 api로 포인트를 충전하고 결제를 완료하면
(QR코드를 작성해 DB와 AWS S3에 업로드)
유저의 마이페이지 부분에서 결제완료한 결제내역과 그 내역과 좌석을 보여줄 수 있게했다.
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,
};
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,
};
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,
};