From d5e0388e8bcca8717a0659780fd9e15448060049 Mon Sep 17 00:00:00 2001 From: KangYeonbae Date: Wed, 4 Dec 2024 18:24:11 +0900 Subject: [PATCH] =?UTF-8?q?=EC=B9=B4=EC=B9=B4=EC=98=A4=EA=B2=B0=EC=A0=9C?= =?UTF-8?q?=20=ED=8C=9D=EC=97=85=20>=20=ED=8E=98=EC=9D=B4=EC=A7=80?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.js | 31 ++------ src/pages/PaymentApproval.js | 35 --------- src/pages/PaymentResult.js | 73 ++++++++++--------- src/pages/booking.js | 134 ++++++++++++----------------------- 4 files changed, 84 insertions(+), 189 deletions(-) delete mode 100644 src/pages/PaymentApproval.js diff --git a/src/App.js b/src/App.js index 0af9e06..e1b66b9 100644 --- a/src/App.js +++ b/src/App.js @@ -22,7 +22,7 @@ import SearchResults from './pages/searchResults'; import NotFound from './pages/notFound'; import Contact from './pages/contact'; import Settings from './pages/Settings'; -import PaymentApproval from './pages/PaymentApproval'; +import PaymentResult from './pages/PaymentResult'; // 컨텍스트 및 상수 import { AuthContext } from './utils/AuthContext'; @@ -35,7 +35,6 @@ import ScrollToTop from './components/ScrollToTop'; import LoadingSpinner from './components/LoadingSpinner'; import SearchBar from './components/SearchBar'; import ThemeToggle from './components/ThemeToggle'; -import PaymentResult from './pages/PaymentResult'; // 보호된 라우트 컴포넌트 const PrivateRoute = ({ children, requiredRole }) => { @@ -195,7 +194,8 @@ function App() { } /> } /> } /> - } /> + } /> + } /> {/* 공간 관련 라우트 */} {ItemType.map(({type}) => ( @@ -216,32 +216,9 @@ function App() { } /> - - - - } /> + - - } /> - - {/* 결제 결과 처리 라우트 */} - - - - } /> - - - - } /> - - - } /> {/* 404 페이지 */} diff --git a/src/pages/PaymentApproval.js b/src/pages/PaymentApproval.js deleted file mode 100644 index fe587a5..0000000 --- a/src/pages/PaymentApproval.js +++ /dev/null @@ -1,35 +0,0 @@ -// PaymentApproval.js -import { useEffect } from 'react'; -import { useLocation } from 'react-router-dom'; - -const PaymentApproval = () => { - const location = useLocation(); - - useEffect(() => { - // 부모 창이 존재하는 경우에만 실행 - if (window.opener) { - // 메시지 전송 - window.opener.postMessage( - { - type: 'KAKAO_PAYMENT_SUCCESS', - url: window.location.href - }, - '*' - ); - - // 잠시 후 창 닫기 (메시지가 확실히 전달되도록) - setTimeout(() => { - window.close(); - }, 500); - } - }, []); - - return ( -
-

결제가 완료되었습니다

-

곧 창이 자동으로 닫힙니다.

-
- ); -}; - -export default PaymentApproval; \ No newline at end of file diff --git a/src/pages/PaymentResult.js b/src/pages/PaymentResult.js index 60e4b43..ec829bf 100644 --- a/src/pages/PaymentResult.js +++ b/src/pages/PaymentResult.js @@ -1,46 +1,45 @@ +// PaymentResult.js import { useEffect, useState } from 'react'; import { useSearchParams, useNavigate } from 'react-router-dom'; import { handlePaymentResult } from '../utils/paymentService'; import "../styles/booking.css" +// PaymentResult.js export default function PaymentResult() { - const [searchParams] = useSearchParams(); - const navigate = useNavigate(); - const [status, setStatus] = useState('결제 처리중...'); + const [searchParams] = useSearchParams(); + const navigate = useNavigate(); + const [status, setStatus] = useState('결제 처리중...'); - useEffect(() => { - const processPaymentResult = async () => { - try { - const result = await handlePaymentResult(searchParams); - setStatus('결제가 완료되었습니다.'); - - // 결제 성공 시 4단계로 이동 - setTimeout(() => { - navigate('/booking', { - state: { - paymentSuccess: true, - orderNumber: searchParams.get('order_number') - } - }); - }, 3000); - } catch (error) { - setStatus('결제 처리 중 오류가 발생했습니다.'); - setTimeout(() => { - navigate('/booking'); - }, 3000); - } - }; + useEffect(() => { + const processPayment = async () => { + const orderNumber = searchParams.get('order_number'); + const pgToken = searchParams.get('pg_token'); - processPaymentResult(); - }, [searchParams, navigate]); + if (orderNumber && pgToken) { + try { + const result = await handlePaymentResult(searchParams); + if (result) { + setStatus('결제가 완료되었습니다.'); + setTimeout(() => { + navigate('/booking', { + state: { paymentSuccess: true } + }); + }, 1500); + } + } catch (error) { + setStatus('결제 처리 중 오류가 발생했습니다.'); + setTimeout(() => navigate('/booking'), 2000); + } + } + }; - return ( -
-

결제 처리 결과

-

{status}

-

- 잠시 후 자동으로 이동합니다... -

-
- ); -} + processPayment(); + }, [searchParams, navigate]); + + return ( +
+

결제 처리 결과

+

{status}

+
+ ); +} \ No newline at end of file diff --git a/src/pages/booking.js b/src/pages/booking.js index cab89dd..e62190d 100644 --- a/src/pages/booking.js +++ b/src/pages/booking.js @@ -40,104 +40,58 @@ export default function BookingForm() { } }; -// BookingForm.js -const handleSubmit = async (e) => { - e.preventDefault(); - - if (step < 3) { - setStep(prev => prev + 1); - } - else if (step === 3) { - if (!bookingData.agreement) { - alert('예약 정책에 동의해주세요.'); - return; - } - - try { - setPaymentProcessing(true); - if (bookingData.paymentMethod === 'kakao') { - const paymentData = usageUnit === 'DAY' - ? { - date: bookingData.date, - user_name: bookingData.name, - phone: bookingData.phone, - email: bookingData.email - } - : { - start_time: bookingData.start_time, - end_time: bookingData.end_time, - user_name: bookingData.name, - phone: bookingData.phone, - email: bookingData.email - }; - - const response = await initiateKakaoPayment( - paymentData, - totalPrice, - bookingInfo.spaceId - ); - if (response.next_redirect_pc_url) { - // 카카오페이 결제창 팝업으로 열기 - const width = 450; - const height = 650; - const left = window.screen.width / 2 - width / 2; - const top = window.screen.height / 2 - height / 2; - - // 결제 완료 후 처리를 위한 메시지 리스너 - const handleMessage = async (e) => { - if (e.data.type === 'KAKAO_PAYMENT_SUCCESS') { - const approvalUrl = e.data.url; - const urlParams = new URLSearchParams(new URL(approvalUrl).search); - const orderNumber = urlParams.get('order_number'); - const pgToken = urlParams.get('pg_token'); - - const result = await handlePaymentApproval(orderNumber, pgToken); - if (result.success) { - setStep(4); // 결제 성공 시 step 4로 이동 + + const handleSubmit = async (e) => { + e.preventDefault(); + + if (step < 3) { + setStep(prev => prev + 1); + } + else if (step === 3) { + if (!bookingData.agreement) { + alert('예약 정책에 동의해주세요.'); + return; + } + + try { + setPaymentProcessing(true); + if (bookingData.paymentMethod === 'kakao') { + const paymentData = usageUnit === 'DAY' + ? { + date: bookingData.date, + user_name: bookingData.name, + phone: bookingData.phone, + email: bookingData.email } - } - }; - - window.addEventListener('message', handleMessage); - - const popup = window.open( - response.next_redirect_pc_url, - 'KakaoPay', - `width=${width},height=${height},left=${left},top=${top}` + : { + start_time: bookingData.start_time, + end_time: bookingData.end_time, + user_name: bookingData.name, + phone: bookingData.phone, + email: bookingData.email + }; + + const response = await initiateKakaoPayment( + paymentData, + totalPrice, + bookingInfo.spaceId ); - - // 팝업 닫힘 감지 - const checkPopupClosed = setInterval(() => { - if (popup.closed) { - clearInterval(checkPopupClosed); - window.removeEventListener('message', handleMessage); - setPaymentProcessing(false); - } - }, 500); + + if (response.next_redirect_pc_url) { + // 팝업 대신 현재 창에서 리다이렉트 + window.location.href = response.next_redirect_pc_url; + } } + } catch (error) { + alert('결제 처리 중 오류가 발생했습니다.'); + console.error('Payment error:', error); + setPaymentProcessing(false); } - } catch (error) { - alert('결제 처리 중 오류가 발생했습니다.'); - console.error('Payment error:', error); - setPaymentProcessing(false); - } - } -}; - - - - // 결제 상태 확인 함수 - const checkPaymentStatus = async (tid) => { - try { - const response = await fetch(`/api/payments/status/${tid}`); - const data = await response.json(); - return data; - } catch (error) { - throw new Error('Payment status check failed'); } }; + useEffect(() => { const paymentSuccess = location.state?.paymentSuccess; if (paymentSuccess) {