From d978c9a2427a91396ae7a815a6eb293a10fc6dfc Mon Sep 17 00:00:00 2001 From: sw326 Date: Wed, 18 Sep 2024 21:15:49 +0900 Subject: [PATCH] fix : using estimateId, commissionId both. error code for payments updated --- src/api/payment.ts | 18 +++++-- src/hooks/usePayment.ts | 22 +++++--- src/pages/members/EstimateDetail.tsx | 4 +- src/pages/members/PaymentPage.tsx | 50 ++++++++++++++---- src/types/portone.ts | 50 ++++++++---------- src/utils/errorHandler.ts | 79 +++++++++++++++++++++++++--- src/utils/paymentAdapter.ts | 9 ++-- 7 files changed, 171 insertions(+), 61 deletions(-) diff --git a/src/api/payment.ts b/src/api/payment.ts index bd1e14a..b52c52a 100644 --- a/src/api/payment.ts +++ b/src/api/payment.ts @@ -1,19 +1,29 @@ import api from './axiosConfig'; -import { PaymentRequest, PaymentResponse } from '../types/portone'; +import { + CompletePaymentRequest, + CompletePaymentResponse, + PaymentRequest, + PaymentResponse, +} from '../types/portone'; export const getPaymentData = async ( estimateId: number, + commissionId: number, ): Promise => { const response = await api.get( - `/payments/data/${estimateId}`, + `/payments/data?estimateId=${estimateId}&commissionId=${commissionId}`, ); return response.data; }; export const completePayment = async ( impUid: string, -): Promise => { - const response = await api.get(`/payments/${impUid}`); + paymentData: CompletePaymentRequest, +): Promise => { + const response = await api.post( + `/payments/complete/${impUid}`, + paymentData, + ); return response.data; }; diff --git a/src/hooks/usePayment.ts b/src/hooks/usePayment.ts index cb9676e..d914ecc 100644 --- a/src/hooks/usePayment.ts +++ b/src/hooks/usePayment.ts @@ -1,17 +1,27 @@ import { useMutation, useQuery } from '@tanstack/react-query'; import * as api from '../api/payment'; -import { PaymentRequest, PaymentResponse } from '../types/portone'; +import { + CompletePaymentRequest, + CompletePaymentResponse, + PaymentRequest, + PaymentResponse, +} from '../types/portone'; -export const useGetPaymentData = (estimateId: number) => { +export const useGetPaymentData = (estimateId: number, commissionId: number) => { return useQuery({ - queryKey: ['paymentData', estimateId], - queryFn: () => api.getPaymentData(estimateId), + queryKey: ['paymentData', estimateId, commissionId], + queryFn: () => api.getPaymentData(estimateId, commissionId), }); }; export const useCompletePayment = () => { - return useMutation({ - mutationFn: (impUid: string) => api.completePayment(impUid), + return useMutation< + CompletePaymentResponse, + Error, + { impUid: string; paymentData: CompletePaymentRequest } + >({ + mutationFn: ({ impUid, paymentData }) => + api.completePayment(impUid, paymentData), }); }; diff --git a/src/pages/members/EstimateDetail.tsx b/src/pages/members/EstimateDetail.tsx index 5cb1331..b0f3b2e 100644 --- a/src/pages/members/EstimateDetail.tsx +++ b/src/pages/members/EstimateDetail.tsx @@ -38,7 +38,9 @@ const EstimateDetail: React.FC = () => { ); const handlePaymentNavigation = () => { - navigate('/payment', { state: { estimateId: data.id } }); + navigate('/payment', { + state: { estimateId: data.id, commissionId: data.commissionId }, + }); }; const InfoItem: React.FC<{ diff --git a/src/pages/members/PaymentPage.tsx b/src/pages/members/PaymentPage.tsx index 46af11a..54364aa 100644 --- a/src/pages/members/PaymentPage.tsx +++ b/src/pages/members/PaymentPage.tsx @@ -6,6 +6,7 @@ import { PayMethod, RequestPayParams, RequestPayResponse, + CompletePaymentRequest, } from '../../types/portone'; import LoadingSpinner from '../../utils/LoadingSpinner'; import { showErrorNotification } from '../../utils/errorHandler'; @@ -23,7 +24,10 @@ type ExtendedPayMethod = PayMethod | 'SIMPLE_PAY'; const PaymentPage: React.FC = () => { const navigate = useNavigate(); const location = useLocation(); - const { estimateId } = location.state as { estimateId: number }; + const { estimateId, commissionId } = location.state as { + estimateId: number; + commissionId: number; + }; const [paymentMethod, setPaymentMethod] = useState('card'); const [simplePayMethod, setSimplePayMethod] = useState(''); @@ -33,9 +37,11 @@ const PaymentPage: React.FC = () => { data: paymentData, isLoading: isDataLoading, error, - } = useGetPaymentData(estimateId); + } = useGetPaymentData(estimateId, commissionId); const completePaymentMutation = useCompletePayment(); + console.log('Payment Data:', paymentData); + useEffect(() => { const jquery = document.createElement('script'); jquery.src = 'https://code.jquery.com/jquery-1.12.4.min.js'; @@ -45,7 +51,7 @@ const PaymentPage: React.FC = () => { document.head.appendChild(iamport); jquery.onload = () => { - if (iamport.readyState === 'complete') { + if ((iamport as any).readyState === 'complete') { setIsScriptLoading(false); } else { iamport.onload = () => setIsScriptLoading(false); @@ -79,17 +85,43 @@ const PaymentPage: React.FC = () => { ); IMP.request_pay(data, callback); + console.log(data, callback); }; const callback = async (response: RequestPayResponse) => { - const { success, error_msg, imp_uid } = response; + const { + success, + error_msg, + imp_uid, + merchant_uid, + pay_method, + buyer_email, + buyer_tel, + } = response; if (success && imp_uid) { try { - const result = await completePaymentMutation.mutateAsync(imp_uid); - navigate('/payment-success', { - state: { paymentInfo: result.response }, + const paymentData: CompletePaymentRequest = { + imp_uid, + merchant_uid: merchant_uid || '', + pay_method: (pay_method as PayMethod) || 'card', + buyer_email: buyer_email || '', + buyer_tel: buyer_tel || '', + }; + + const result = await completePaymentMutation.mutateAsync({ + impUid: imp_uid, + paymentData, }); + + if (result.code === 0) { + // Assuming 0 means success, adjust as needed + navigate('/payment-success', { + state: { paymentInfo: result.response }, + }); + } else { + showErrorNotification(`결제 완료 처리 실패: ${result.message}`); + } } catch (error) { showErrorNotification('결제 완료 처리 중 오류가 발생했습니다.'); } @@ -158,7 +190,7 @@ const PaymentPage: React.FC = () => {
청소 서비스 금액 - {paymentData.amount.toLocaleString()}원 + {paymentData.estimate_amount.toLocaleString()}원
@@ -184,7 +216,7 @@ const PaymentPage: React.FC = () => { } className="w-full bg-brand text-white py-3 px-4 rounded-lg hover:bg-brand-dark transition-colors disabled:opacity-50 disabled:cursor-not-allowed text-lg font-bold" > - {paymentData.amount.toLocaleString()}원 결제하기 + {paymentData.estimate_amount.toLocaleString()}원 결제하기 diff --git a/src/types/portone.ts b/src/types/portone.ts index 4285184..63fef9c 100644 --- a/src/types/portone.ts +++ b/src/types/portone.ts @@ -8,45 +8,23 @@ export type PayMethod = | 'samsungpay' | 'ssgpay'; -export interface EstimateDetail { - id: number; - commissionId: number; - partnerId: number; - price: number; - fixedDate: string; - statement: string; - status: string; - size: number; - desiredDate: string; - significant: string; - commissionStatus: string; - phoneNumber: string; - managerName: string; - companyName: string; +export enum CleanType { + NORMAL = 'NORMAL', + SPECIAL = 'SPECIAL', } -export interface EstimateAndCommissionResponseDto { +// 서버와 통신하는 인터페이스 +export interface PaymentRequest { estimate_id: number; commission_id: number; estimate_amount: number; member_nick: string; member_phone_number: string; member_email: string; + clean_type: CleanType; } -// 서버와 통신하는 인터페이스 (스네이크 케이스) -export interface PaymentRequest { - pg: string; - payMethod: PayMethod; - merchantUid: string; - name: string; - amount: number; - buyerName: string; - buyerTel: string; - buyerEmail: string; -} - -// 포트원 관련 인터페이스 (스네이크 케이스) +// 포트원 관련 인터페이스 export interface PortonePaymentRequest { pg: string; pay_method: PayMethod; @@ -58,6 +36,20 @@ export interface PortonePaymentRequest { buyer_email: string; } +export interface CompletePaymentRequest { + imp_uid: string; + merchant_uid: string; + pay_method: PayMethod; + buyer_email: string; + buyer_tel: string; +} + +export interface CompletePaymentResponse { + code: number; + message: string; + response: any; // 실제 응답 구조에 맞게 타입을 정의해야 합니다. +} + export interface PaymentResponse { code: number; message: string; diff --git a/src/utils/errorHandler.ts b/src/utils/errorHandler.ts index 458bda1..90e790a 100644 --- a/src/utils/errorHandler.ts +++ b/src/utils/errorHandler.ts @@ -1,11 +1,58 @@ -import toast from "react-hot-toast"; +import toast from 'react-hot-toast'; type ErrorCode = - | 1000 | 1001 | 1002 | 1004 | 1005 | 1006 | 1007 | 1008 | 1009 - | 2001 | 2002 | 2003 | 2004 | 2005 | 2006 | 2007 | 2008 - | 3001 | 3002 | 3003 - | 4001 | 4002 | 4003 | 4004 | 4005 | 4006 | 4007 | 4008 | 4009 | 4010 | 4011 - | 5001 | 5002 | 5003 | 5004 | 5005; + | 1000 + | 1001 + | 1002 + | 1004 + | 1005 + | 1006 + | 1007 + | 1008 + | 1009 + | 1010 + | 1011 + | 1012 + | 1013 + | 1014 + | 1015 + | 1016 + | 2001 + | 2002 + | 2003 + | 2004 + | 2005 + | 2006 + | 2007 + | 2008 + | 2009 + | 3001 + | 3002 + | 3003 + | 4001 + | 4002 + | 4003 + | 4004 + | 4005 + | 4006 + | 4007 + | 4008 + | 4009 + | 4010 + | 4011 + | 4012 + | 4013 + | 5001 + | 5002 + | 5003 + | 5004 + | 5005 + | 6000 + | 6001 + | 6002 + | 6003 + | 6004 + | 6005; const errorMessages: Record = { 1000: '필수 정보를 입력해주세요.', @@ -17,6 +64,13 @@ const errorMessages: Record = { 1007: '카카오 사용자 정보 응답을 처리하는 중 오류가 발생했습니다.', 1008: '회원가입 요청이 유효하지 않습니다.', 1009: '유효하지 않은 인증 코드입니다.', + 1010: 'JSON 포맷에 적합하지 않은 요청입니다.', + 1011: '잘못된 요청입니다.', + 1012: '탈퇴한 회원입니다.', + 1013: '잘못된 결제 요청입니다.', + 1014: '결제 급액이 일치하지 않습니다.', + 1015: '결제가 이미 완료되었습니다.', + 1016: '결제가 이미 취소되었습니다.', 2001: '인증되지 않은 사용자입니다.', 2002: '로그인이 필요합니다.', 2003: 'Access Token이 만료되었습니다.', @@ -25,6 +79,7 @@ const errorMessages: Record = { 2006: '이메일 인증이 완료되지 않았습니다.', 2007: '이메일 인증 코드가 만료되었습니다.', 2008: '인증 시도 횟수 초과입니다.', + 2009: '인증 코드를 찾을 수 없습니다.', 3001: '판매자가 아닙니다.', 3002: '구매자가 아닙니다.', 3003: '청소의뢰가 존재하지 않거나 권한이 없습니다.', @@ -39,11 +94,19 @@ const errorMessages: Record = { 4009: '계좌번호를 찾을 수 없습니다.', 4010: '존재하지 않는 아이디입니다.', 4011: '존재하지 않는 이메일입니다.', + 4012: '필수 요청 파라미터가 누락되었습니다.', + 4013: '결제를 찾을 수 없습니다.', 5001: '이미 가입된 사용자입니다.', 5002: '중복된 이메일입니다.', 5003: '중복된 닉네임입니다.', 5004: '중복된 전화번호입니다.', 5005: '중복된 회사명입니다.', + 6000: '요청 사업자번호 또는 정보가 100개를 초과하였습니다.', + 6001: '파일 실행에 실패', + 6002: '파일 컴파일에 실패', + 6003: '시스템 오류가 발생했습니다.', + 6004: '결제 처리에 실패했습니다.', + 6005: '결제 취소 처리에 실패했습니다.', }; export const handleApiError = (error: any): string => { @@ -55,6 +118,6 @@ export const handleApiError = (error: any): string => { }; export const showErrorNotification = (message: string) => { - toast.error(message) + toast.error(message); console.error(message); -}; \ No newline at end of file +}; diff --git a/src/utils/paymentAdapter.ts b/src/utils/paymentAdapter.ts index 24a2bd3..e1f1837 100644 --- a/src/utils/paymentAdapter.ts +++ b/src/utils/paymentAdapter.ts @@ -9,9 +9,10 @@ export const adaptEstimateToPayment = ( pg: 'smartro.iamport01m', pay_method: selectedPayMethod as PayMethod, merchant_uid: `ORDER_${uuidv4().replace(/-/g, '')}`, - amount: paymentData.amount, - buyer_name: paymentData.buyerName, - buyer_tel: paymentData.buyerTel, - buyer_email: paymentData.buyerEmail, + name: `${paymentData.clean_type}`, + amount: paymentData.estimate_amount, + buyer_name: paymentData.member_nick, + buyer_tel: paymentData.member_phone_number, + buyer_email: paymentData.member_email, }; };