From a6c139b3d19a1c2dc5415d05f7e2fe74253d667d Mon Sep 17 00:00:00 2001 From: fiore Date: Wed, 12 Mar 2025 17:40:06 +0900 Subject: [PATCH] =?UTF-8?q?=EC=BD=94=EC=9D=B8=20=EA=B4=80=EB=A0=A8=20API?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.js | 4 +- routes/coins.js | 246 ++++++++++++++++++++++++++++++++++++++++++++++++ routes/users.js | 6 +- 3 files changed, 252 insertions(+), 4 deletions(-) create mode 100644 routes/coins.js diff --git a/app.js b/app.js index 8a5f2e2..6dafb29 100644 --- a/app.js +++ b/app.js @@ -9,6 +9,7 @@ var MongoClient = mongodb.MongoClient; var indexRouter = require('./routes/index'); var usersRouter = require('./routes/users'); var leaderboardRouter = require('./routes/leaderboard'); +var coinsRouter = require('./routes/coins'); const session = require('express-session'); var fileStore = require('session-file-store')(session); @@ -47,7 +48,7 @@ async function connectDB(){ // 연결 종료 처리 process.on("SIGINT", async ()=> { - await database.close(); + await client.close(); console.log("Database Connected"); process.exit(0); }) @@ -75,6 +76,7 @@ app.use(express.static(path.join(__dirname, 'public'))); app.use('/', indexRouter); app.use('/users', usersRouter); app.use('/leaderboard', leaderboardRouter); +app.use('/coins', coinsRouter); // catch 404 and forward to error handler app.use(function(req, res, next) { diff --git a/routes/coins.js b/routes/coins.js new file mode 100644 index 0000000..f87f472 --- /dev/null +++ b/routes/coins.js @@ -0,0 +1,246 @@ +var express = require('express'); +const {ObjectId} = require("mongodb"); +var router = express.Router(); + +// 인증 확인 미들웨어 +const isAuthenticated = (req, res, next) => { + if (req.session && req.session.isAuthenticated) { + next(); + } else { + res.status(401).json({ result: 'AUTH_REQUIRED', message: '로그인이 필요합니다.' }); + } +}; + +// 코인 잔액 조회 +router.get('/', isAuthenticated, async function(req, res) { + try { + const userId = req.session.userId; + const database = req.app.get('database'); + const users = database.collection('users'); + + const user = await users.findOne({ _id: new ObjectId(userId) }); + + if (!user) { + return res.status(404).json({ result: 'USER_NOT_FOUND', message: '사용자를 찾을 수 없습니다.' }); + } + + // 코인 필드가 없는 경우 기본값 0으로 설정 + const coins = user.coins || 0; + + res.json({ + result: 'SUCCESS', + coins: coins + }); + + } catch (err) { + console.log("코인 잔액 조회 중 오류 발생 : ", err); + res.status(500).json({ result: 'ERROR', message: '서버 오류가 발생했습니다.' }); + } +}); + + +// 코인 차감 (100 코인) +router.post('/deduct', isAuthenticated, async function(req, res) { + try { + const userId = req.session.userId; + const deductAmount = 100; // 고정 차감 금액 + const database = req.app.get('database'); + const users = database.collection('users'); + + // 트랜잭션이 이상적이지만, 간단한 구현을 위해 findOne과 updateOne으로 처리 + const user = await users.findOne({ _id: new ObjectId(userId) }); + + if (!user) { + return res.status(404).json({ result: 'USER_NOT_FOUND', message: '사용자를 찾을 수 없습니다.' }); + } + + // 코인 필드가 없는 경우 기본값 0으로 설정 + const currentCoins = user.coins || 0; + + // 코인이 부족한 경우 + if (currentCoins < deductAmount) { + return res.status(400).json({ + result: 'INSUFFICIENT_COINS', + message: '코인이 부족합니다.', + currentCoins: currentCoins + }); + } + + // 코인 차감 + const updateResult = await users.updateOne( + { _id: new ObjectId(userId) }, + { $inc: { coins: -deductAmount } } + ); + + if (updateResult.modifiedCount !== 1) { + return res.status(500).json({ result: 'UPDATE_FAILED', message: '코인 차감에 실패했습니다.' }); + } + + // 코인 차감 로그 + // const coinLogs = database.collection('coinLogs'); + // await coinLogs.insertOne({ + // userId: new ObjectId(userId), + // type: 'DEDUCT', + // amount: deductAmount, + // timestamp: new Date(), + // balance: currentCoins - deductAmount + // }); + + res.json({ + result: 'SUCCESS', + message: `${deductAmount} 코인이 차감되었습니다.`, + deducted: deductAmount, + remainingCoins: currentCoins - deductAmount + }); + + } catch (err) { + console.log("코인 차감 중 오류 발생 : ", err); + res.status(500).json({ result: 'ERROR', message: '서버 오류가 발생했습니다.' }); + } +}); + +// 코인 충전 (광고 시청 후 500 코인) +router.post('/recharge/ad', isAuthenticated, async function(req, res) { + try { + const userId = req.session.userId; + const rechargeAmount = 500; // 고정 충전 금액 + const database = req.app.get('database'); + const users = database.collection('users'); + + // 광고 시청 완료 여부 확인 (클라이언트에서 전송) + const { adCompleted } = req.body; + + if (!adCompleted) { + return res.status(400).json({ result: 'AD_NOT_COMPLETED', message: '광고 시청이 완료되지 않았습니다.' }); + } + + // 광고 ID 또는 추가 검증 로직이 필요하다면 여기에 구현 + // 트랜잭션이 이상적이지만, 간단한 구현을 위해 updateOne으로 처리 + const updateResult = await users.updateOne( + { _id: new ObjectId(userId) }, + { $inc: { coins: rechargeAmount } } + ); + + if (updateResult.modifiedCount !== 1) { + return res.status(500).json({ result: 'UPDATE_FAILED', message: '코인 충전에 실패했습니다.' }); + } + + // 업데이트된 사용자 정보 가져오기 + const updatedUser = await users.findOne({ _id: new ObjectId(userId) }); + const currentCoins = updatedUser.coins || 0; + + // 코인 충전 로그 + // const coinLogs = database.collection('coinLogs'); + // await coinLogs.insertOne({ + // userId: new ObjectId(userId), + // type: 'RECHARGE', + // amount: rechargeAmount, + // adId: req.body.adId, // 광고 ID가 있다면 + // timestamp: new Date(), + // balance: currentCoins + // }); + + res.json({ + result: 'SUCCESS', + message: `광고 시청으로 ${rechargeAmount} 코인이 충전되었습니다.`, + recharged: rechargeAmount, + currentCoins: currentCoins + }); + + } catch (err) { + console.log("코인 충전 중 오류 발생 : ", err); + res.status(500).json({ result: 'ERROR', message: '서버 오류가 발생했습니다.' }); + } +}); + +// 구매를 통한 코인 충전 (충전 수량은 파라미터로 전달 받음) +router.post('/purchase', isAuthenticated, async function(req, res) { + try { + const userId = req.session.userId; + const database = req.app.get('database'); + const users = database.collection('users'); + + // 구매 정보 확인 + const { amount, paymentId, paymentType } = req.body; + + // 필수 파라미터 검증 + if (!amount || amount <= 0) { + return res.status(400).json({ + result: 'INVALID_PARAMETER', + message: '유효한 충전 수량을 입력해주세요.' + }); + } + + // 결제 검증 + // 이 부분은 실제 결제 검증 로직으로 대체해야 합니다 + const paymentVerified = await verifyPayment(paymentId, amount, paymentType); + + if (!paymentVerified) { + return res.status(400).json({ + result: 'PAYMENT_VERIFICATION_FAILED', + message: '결제 검증에 실패했습니다.' + }); + } + + // 코인 충전 + const updateResult = await users.updateOne( + { _id: new ObjectId(userId) }, + { $inc: { coins: amount } } + ); + + if (updateResult.modifiedCount !== 1) { + // 결제는 성공했는데 코인 충전 실패 시 관리자에게 알림 필요 + console.error(`사용자 ${userId}의 코인 충전 실패. 결제ID: ${paymentId}`); + return res.status(500).json({ result: 'UPDATE_FAILED', message: '코인 충전에 실패했습니다.' }); + } + + // 업데이트된 사용자 정보 가져오기 + const updatedUser = await users.findOne({ _id: new ObjectId(userId) }); + const currentCoins = updatedUser.coins || 0; + + // 코인 충전 로그 + // const coinLogs = database.collection('coinLogs'); + // await coinLogs.insertOne({ + // userId: new ObjectId(userId), + // type: 'PURCHASE', + // amount: amount, + // paymentId: paymentId, + // paymentType: paymentType, + // timestamp: new Date(), + // balance: currentCoins + // }); + + // 결제 내역 저장 + // const payments = database.collection('payments'); + // await payments.insertOne({ + // userId: new ObjectId(userId), + // paymentId: paymentId, + // paymentType: paymentType, + // amount: amount, + // status: 'COMPLETED', + // timestamp: new Date() + // }); + + res.json({ + result: 'SUCCESS', + message: `${amount} 코인이 성공적으로 충전되었습니다.`, + purchased: amount, + currentCoins: currentCoins + }); + + } catch (err) { + console.log("구매 코인 충전 중 오류 발생 : ", err); + res.status(500).json({ result: 'ERROR', message: '서버 오류가 발생했습니다.' }); + } +}); + +// 결제 검증 함수 (실제 결제 서비스에 맞게 구현 필요) +async function verifyPayment(paymentId, amount, paymentType) { + // 여기에 실제 결제 검증 로직 구현 + // 예: 결제 서비스 API 호출하여 결제 상태 확인 + + // 테스트 목적으로 항상 true 반환 (실제 구현에서는 제거) + return true; +} + +module.exports = router; \ No newline at end of file diff --git a/routes/users.js b/routes/users.js index d1269d6..85c06a5 100644 --- a/routes/users.js +++ b/routes/users.js @@ -56,7 +56,7 @@ router.post('/signup', async function (req, res, next) { score:0, win:0, lose:0, - coin: 1000 + coins: 1000 }); res.status(201).send("사용자가 성공적으로 생성되었습니다."); @@ -91,14 +91,14 @@ router.post("/signin", async function (req, res, next) { req.session.profileImageIndex = existingUser.profileImageIndex || 0; req.session.rating = existingUser.rating; req.session.score = existingUser.score; - req.session.coin = existingUser.coin; + req.session.coins = existingUser.coins; res.json({ result: ResponseType.SUCCESS, imageindex: existingUser.imageindex, rating: existingUser.rating, score: existingUser.score, - coin: existingUser.coin, + coins: existingUser.coins, }); } else { res.json({result : ResponseType.INVALID_PASSWORD});