Commit c4a8991d authored by Jiwon Yoon's avatar Jiwon Yoon
Browse files

gmail 연동

parent 619844f9
...@@ -14,6 +14,7 @@ import AdminPage from "./pages/AdminPage/AdminPage"; ...@@ -14,6 +14,7 @@ import AdminPage from "./pages/AdminPage/AdminPage";
import TicketingPage from "./pages/TicketingPage"; import TicketingPage from "./pages/TicketingPage";
import TicketingSeatPage from './pages/TicketingSeatPage' import TicketingSeatPage from './pages/TicketingSeatPage'
import SearchPage from "./pages/SearchPage"; import SearchPage from "./pages/SearchPage";
import Payment from "./pages/PaymentPage";
const AppContext = React.createContext(); const AppContext = React.createContext();
...@@ -36,6 +37,7 @@ function App() { ...@@ -36,6 +37,7 @@ function App() {
<Route path="/movie/:movieId" component={MoviePage} /> <Route path="/movie/:movieId" component={MoviePage} />
<Route path="/ticket/seat" component={TicketingSeatPage} /> <Route path="/ticket/seat" component={TicketingSeatPage} />
<Route path="/ticket" component={TicketingPage} /> <Route path="/ticket" component={TicketingPage} />
<Route path="/payment" component={Payment} />
<Route path="/search" component={SearchPage} /> <Route path="/search" component={SearchPage} />
<Route path="/admin" component={AdminPage} /> <Route path="/admin" component={AdminPage} />
</Switch> </Switch>
......
...@@ -12,7 +12,7 @@ const CountButton = (props) => { ...@@ -12,7 +12,7 @@ const CountButton = (props) => {
} }
} }
return ( return (
<span className=""> <div className="">
<button type="button" name="backbutton" style={{ backgroundColor: "black", border: "0" }} onClick={handleCount}> <button type="button" name="backbutton" style={{ backgroundColor: "black", border: "0" }} onClick={handleCount}>
<img src="/images/icons8-back-24.png" name="backbutton" alt="<" /> <img src="/images/icons8-back-24.png" name="backbutton" alt="<" />
</button> </button>
...@@ -20,7 +20,7 @@ const CountButton = (props) => { ...@@ -20,7 +20,7 @@ const CountButton = (props) => {
<button type="button" name="forwardbutton" min="0" style={{ backgroundColor: "black", border: "0" }} onClick={handleCount}> <button type="button" name="forwardbutton" min="0" style={{ backgroundColor: "black", border: "0" }} onClick={handleCount}>
<img src="/images/icons8-forward-24.png" name="forwardbutton" alt=">" /> <img src="/images/icons8-forward-24.png" name="forwardbutton" alt=">" />
</button> </button>
</span> </div>
) )
} }
......
import axios from 'axios'
const Kakaopay = (props) => {
async function handleClick() {
try {
const response = await axios.post('/api/kakaopay/test/single', {
cid: 'TC0ONETIME',
partner_order_id: 'orderNum',
partner_user_id: 'userName',
item_name: props.ticketInfo.title,
quantity: props.ticketInfo.teenager+props.ticketInfo.adult+props.ticketInfo.elderly,
total_amount: props.ticketInfo.teenager * 7000 + props.ticketInfo.adult * 8000 + props.ticketInfo.elderly * 6000,
vat_amount: 0,
tax_free_amount: 0,
approval_url: 'http://localhost:3000/',
fail_url: 'http://localhost:3000/',
cancel_url: 'http://localhost:3000/',
})
console.log(response.data)
if (response.data) {
window.location.href = response.data.redirect_url
}
} catch (error) {
console.log(error)
}
}
return (
<>
<button onClick={handleClick} style={{ backgroundColor: "black", border: '0' }}>
<img src="/images/payment_icon_yellow_medium.png" />
</button>
</>
)
}
export default Kakaopay
\ No newline at end of file
...@@ -9,7 +9,7 @@ const MoviePage = ({ location }) => { ...@@ -9,7 +9,7 @@ const MoviePage = ({ location }) => {
stillCuts: [], stillCuts: [],
cast: "", cast: "",
director: "", director: "",
genres: [], // genres: [],
attendance: "" attendance: ""
}) })
const [state, setState] = useState(0) const [state, setState] = useState(0)
...@@ -27,15 +27,15 @@ const MoviePage = ({ location }) => { ...@@ -27,15 +27,15 @@ const MoviePage = ({ location }) => {
const casts = castsInfo.reduce((acc, cur, idx) => { const casts = castsInfo.reduce((acc, cur, idx) => {
if (idx !== 0) return acc + ', ' + cur if (idx !== 0) return acc + ', ' + cur
else return acc + cur else return acc + cur
},"") }, "")
console.log(castsInfo) console.log(castsInfo)
const directorsInfo = await credits.crew.filter(element => element.job === "Director") const directorsInfo = await credits.crew.filter(element => element.job === "Director")
const directors = directorsInfo.reduce((acc, cur, idx) => { const directors = directorsInfo.reduce((acc, cur, idx) => {
if (idx !== 0) return acc + ', ' + cur.name if (idx !== 0) return acc + ', ' + cur.name
else return acc + cur.name else return acc + cur.name
},"") }, "")
console.log("directorInfo=",directorsInfo) console.log("directorInfo=", directorsInfo)
setMovieInfo({ setMovieInfo({
...movieInfo, ...movieInfo,
stillCuts: still, stillCuts: still,
...@@ -81,7 +81,10 @@ const MoviePage = ({ location }) => { ...@@ -81,7 +81,10 @@ const MoviePage = ({ location }) => {
<p>예매율: 0% 누적관객수: {movieInfo.attendance}</p> <p>예매율: 0% 누적관객수: {movieInfo.attendance}</p>
<p>감독: {movieInfo.director}</p> <p>감독: {movieInfo.director}</p>
<p>출연: {movieInfo.cast}</p> <p>출연: {movieInfo.cast}</p>
<p>장르: {movieInfo.genres.map(e => e)}</p> <p>장르: {movieInfo.genres.reduce((acc, cur, idx) => {
if (idx !== 0) return acc + ', ' + cur.name
else return acc + cur.name
}, "")}</p>
<p>개봉일:{movieInfo.release_date}</p> <p>개봉일:{movieInfo.release_date}</p>
<div className="text-end"> <div className="text-end">
<Link to={{ <Link to={{
......
import axios from 'axios'
import { useState } from 'react'
import Kakaopay from '../components/Kakaopay'
const Payment = ({ location }) => {
const [ticketInfo, setTicketInfo] = useState({ ...location.state })
async function SendMail(e) {
try {
const response = await axios.post('/api/email/send',{
email:e.target.name
})
console.log(response.data)
} catch (error) {
console.log(error)
}
}
return (
<div className="container" style={{ color: "white" }}>
{console.log(ticketInfo)}
<div className="row justify-content-center my-5">
<div className="col-sm-4 mb-3 ">
<h3 className="py-2 text-white text-center" style={{ border: "3px solid #000000", borderBottom: "3px solid #FEDC00" }}>결제하기</h3>
</div>
</div>
<div className="row justify-content-center">
<div className="col-sm-8 text-center">
<h5 className="mb-3">결제방법</h5>
<img src="/images/naverpay_button.png" />
<Kakaopay ticketInfo={ticketInfo} setTicketInfo={setTicketInfo} />
</div>
<div className="col-sm-4 p-3 text-center rounded-3" style={{ backgroundColor: "#252525" }}>
<img style={{ maxHeight: "10rem" }} src={`https://image.tmdb.org/t/p/original${ticketInfo.poster_path}`} alt="영화포스터" />
<h5 className="my-3">{ticketInfo.title}</h5>
<div>{ticketInfo.theater}</div>
<div>{ticketInfo.time}</div>
<div className="mb-3">{ticketInfo.selectedCinemaNum} {ticketInfo.selectedSeats}</div>
<div className="rounded-3 p-3" style={{backgroundColor:'#404040'}}>
<div>청소년: {ticketInfo.teenager}</div>
<div>성인: {ticketInfo.adult}</div>
<div>경로우대: {ticketInfo.elderly}</div>
<div> 결제금액: {ticketInfo.teenager*7000 +ticketInfo.adult*8000+ticketInfo.elderly*6000}</div>
</div>
</div>
</div>
<div>
<button type="button" name="jiwon5393@naver.com" onClick={SendMail}>메일발송</button>
</div>
</div>
)
}
export default Payment
\ No newline at end of file
...@@ -9,8 +9,8 @@ const TicketingPage = ({ location }) => { ...@@ -9,8 +9,8 @@ const TicketingPage = ({ location }) => {
const [ticketInfo, setTicketInfo] = useState({ const [ticketInfo, setTicketInfo] = useState({
...location.state, ...location.state,
theater:"", theater:"",
selectedCinemaNum: 0, selectedCinemaNum: 3,
time: {} time: "2021/07/21 10:00"
}) })
const [theaterInfo, setTheaterInfo] = useState({ const [theaterInfo, setTheaterInfo] = useState({
theater: ["Butter Studio 조치원"], theater: ["Butter Studio 조치원"],
...@@ -62,8 +62,8 @@ const TicketingPage = ({ location }) => { ...@@ -62,8 +62,8 @@ const TicketingPage = ({ location }) => {
? <ul> ? <ul>
<li>영화: {movieInfo.title}</li> <li>영화: {movieInfo.title}</li>
<li>극장: {ticketInfo.theater}</li> <li>극장: {ticketInfo.theater}</li>
<li>일시: </li> <li>일시: {ticketInfo.time}</li>
<li>상영관: </li> <li>상영관: {ticketInfo.selectedCinemaNum}</li>
</ul> </ul>
: <div></div>} : <div></div>}
</div> </div>
......
...@@ -22,25 +22,33 @@ const TicketingSeatPage = ({ location }) => { ...@@ -22,25 +22,33 @@ const TicketingSeatPage = ({ location }) => {
</div> </div>
<div className="row justify-content-center my-3"> <div className="row justify-content-center my-3">
<div className="col-sm-6 mb-4 text-center"> <div className="col-sm-6 mb-4 text-center">
<ul className="mr-2" style={{ listStyle: 'none' }}> <div className="row">
<li>
<span className="">일반</span> <div className="col-sm-6 text-end">
<CountButton name="adult" count={count} setCount={setCount} /> <div className="my-1">일반</div>
</li> <div className="my-1">청소년</div>
<li> <div className="my-1">경로우대</div>
<span className="">청소년</span> </div>
<CountButton name="teenager" count={count} setCount={setCount} /> <div className="col-sm-6 text-start">
</li> <CountButton name="adult" count={count} setCount={setCount} />
<li> <CountButton name="teenager" count={count} setCount={setCount} />
<span className="">경로우대</span> <CountButton name="elderly" count={count} setCount={setCount} />
<CountButton name="elderly" count={count} setCount={setCount} /> </div>
</li> </div>
</ul> {/* <span className="">일반</span>
<CountButton name="adult" count={count} setCount={setCount} />
<span className="">청소년</span>
<CountButton name="teenager" count={count} setCount={setCount} />
<span className="">경로우대</span>
<CountButton name="elderly" count={count} setCount={setCount} /> */}
</div> </div>
<div className="col-sm-6 mb-4 p-2 text-center" style={{ backgroundColor: '#252525' }}> <div className="col-sm-6 mb-4 p-2 text-center" style={{ backgroundColor: '#252525' }}>
<div>{ticketInfo.theater} | 3</div> <div>{ticketInfo.theater} | {ticketInfo.selectedCinemaNum}</div>
<div>{ticketInfo.title}</div> <div>{ticketInfo.title}</div>
<div>2021/07/21 10:00 ~ 11:30</div> <div>{ticketInfo.time}</div>
</div> </div>
</div> </div>
<div className="row justify-content-center border p-5 "> <div className="row justify-content-center border p-5 ">
...@@ -52,7 +60,7 @@ const TicketingSeatPage = ({ location }) => { ...@@ -52,7 +60,7 @@ const TicketingSeatPage = ({ location }) => {
<p>선택불가</p> <p>선택불가</p>
</div> </div>
</div> </div>
<div className="row p-3 my-5" style={{ backgroundColor: "#252525" }}> <div className="row p-3 mt-5" style={{ backgroundColor: "#252525" }}>
<div className="col-sm-3 border-end text-center"> <div className="col-sm-3 border-end text-center">
{ticketInfo {ticketInfo
? <img style={{ maxHeight: "10rem" }} src={`https://image.tmdb.org/t/p/original${ticketInfo.poster_path}`} alt="영화포스터" /> ? <img style={{ maxHeight: "10rem" }} src={`https://image.tmdb.org/t/p/original${ticketInfo.poster_path}`} alt="영화포스터" />
...@@ -76,7 +84,7 @@ const TicketingSeatPage = ({ location }) => { ...@@ -76,7 +84,7 @@ const TicketingSeatPage = ({ location }) => {
? ?
<Link to={{ <Link to={{
pathname: `/payment`, pathname: `/payment`,
state: { } state: { ...ticketInfo, selectedSeats: selectedSeats, ...count }
}}> }}>
<img className="border border-3 rounded-3" src="/images/icons8-arrow-white.png" alt="예매하기" /> <img className="border border-3 rounded-3" src="/images/icons8-arrow-white.png" alt="예매하기" />
</Link> </Link>
......
...@@ -1087,6 +1087,11 @@ ...@@ -1087,6 +1087,11 @@
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
}, },
"nodemailer": {
"version": "6.6.3",
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.6.3.tgz",
"integrity": "sha512-faZFufgTMrphYoDjvyVpbpJcYzwyFnbAMmQtj1lVBYAUSm3SOy2fIdd9+Mr4UxPosBa0JRw9bJoIwQn+nswiew=="
},
"nodemon": { "nodemon": {
"version": "2.0.12", "version": "2.0.12",
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.12.tgz", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.12.tgz",
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
"express": "^4.17.1", "express": "^4.17.1",
"jsonwebtoken": "^8.5.1", "jsonwebtoken": "^8.5.1",
"multer": "^1.4.2", "multer": "^1.4.2",
"nodemailer": "^6.6.3",
"pg": "^8.6.0", "pg": "^8.6.0",
"pg-hstore": "^2.3.4", "pg-hstore": "^2.3.4",
"sequelize": "^6.6.4" "sequelize": "^6.6.4"
......
...@@ -5,6 +5,7 @@ const config = { ...@@ -5,6 +5,7 @@ const config = {
jwtExpires: '7d', jwtExpires: '7d',
cookieName: 'butterStudio', cookieName: 'butterStudio',
cookieMaxAge: 60 * 60 * 24 * 7 * 1000, cookieMaxAge: 60 * 60 * 24 * 7 * 1000,
kakaoAdminKey: 'e3ce7106688a35e072e2630daa9d7250',
} }
export default config export default config
\ No newline at end of file
import nodemailer from "nodemailer"
const SendMail = async (req,res) => {
// console.log(req.body)
const {email} = req.body
console.log(email)
const sendMail = async (email) => {
// 메일을 전달해줄 객체
const transporter = nodemailer.createTransport({
// service: "gmail",
host: 'smtp.gmail.com',
port: 465,
secure: true,
auth: {
type: "OAuth2",
user: "angelayoon99@gmail.com",
clientId: process.env.GMAIL_CLIENTID,
clientSecret: process.env.GMAIL_CLIENTSECRET,
accessToken: process.env.GMAIL_ACCESS_TOKEN,
refreshToken: process.env.GMAIL_REFRESH_TOKEN,
},
tls: {
rejectUnauthorized: false,
},
});
// 메일 옵션
const mailOptions = {
from: `윤지원 <angelayoon99@gmail.com>`,
to: "jiwon5393@naver.com",
subject: "사용자 계정 확인용 메일.",
text: "Test Mail from Test Server.",
};
// 메일 전송
try {
const mailResult = await transporter.sendMail(mailOptions);
console.log(`Mail sent - ID : ${mailResult.messageId}`);
} catch (err) {
console.log("Mail Sending Failuer.");
console.log(err);
}
}
sendMail(email);
}
export default { SendMail }
\ No newline at end of file
import axios from 'axios'
import config from "../config/app.config.js";
const success = (req, res) => {
return res.json({
message: 'Success'
})
}
const fail = (req, res) => {
return res.json({
message: 'Failed'
})
}
const cancel = (req, res) => {
return res.json({
message: 'Canceled'
})
}
const singleTest = async (req, res) => {
try {
const item = req.body
const data = []
for (let property in item) {
let encodedKey = encodeURIComponent(property);
let encodedValue = encodeURIComponent(item[property]);
data.push(encodedKey + "=" + encodedValue);
}
const bodyData = data.join('&')
const response = await axios.post('https://kapi.kakao.com/v1/payment/ready', bodyData, {
headers: {
'Authorization': `KakaoAK ${config.kakaoAdminKey}`,
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
},
})
const resp = response.data
console.log('resp', resp)
res.json({ redirect_url: resp.next_redirect_pc_url })
} catch (error) {
console.log(error)
}
}
export default { success, fail, cancel, singleTest }
\ No newline at end of file
...@@ -35,9 +35,6 @@ const Reservation = ReservationModel(sequelize) ...@@ -35,9 +35,6 @@ const Reservation = ReservationModel(sequelize)
User.belongsTo(Role); User.belongsTo(Role);
Role.hasOne(User); Role.hasOne(User);
User.belongsTo(Role);
Role.hasOne(User);
export { export {
sequelize, sequelize,
User, User,
......
import express from "express";
import EmailCtrl from '../controllers/email.controller.js'
const router = express.Router();
router.route("/send")
.post(EmailCtrl.SendMail)
export default router
\ No newline at end of file
...@@ -2,11 +2,15 @@ import express from "express"; ...@@ -2,11 +2,15 @@ import express from "express";
import userRouter from './user.route.js' import userRouter from './user.route.js'
import movieRouter from './movie.route.js' import movieRouter from './movie.route.js'
import cinemaRouter from "./cinema.route.js"; import cinemaRouter from "./cinema.route.js";
import kakaopayRouter from "./kakaopay.route.js";
import emailRouter from './email.route.js'
const router = express.Router(); const router = express.Router();
router.use('/movie', movieRouter) router.use('/movie', movieRouter)
router.use('/auth', userRouter) router.use('/auth', userRouter)
router.use('/cinema', cinemaRouter) router.use('/cinema', cinemaRouter)
router.use('/kakaopay',kakaopayRouter)
router.use('/email',emailRouter)
export default router; export default router;
\ No newline at end of file
import express from 'express'
import kakaopayCtrl from '../controllers/kakaopay.controller.js'
const router = express.Router()
router.route('/success')
.get(kakaopayCtrl.success)
router.route('/fail')
.get(kakaopayCtrl.fail)
router.route('/cancel')
.get(kakaopayCtrl.cancel)
router.route('/test/single')
.post(kakaopayCtrl.singleTest)
export default router
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment