Skip to content

Commit

Permalink
fix : using estimateId, commissionId both. error code for payments up…
Browse files Browse the repository at this point in the history
…dated
  • Loading branch information
sw326 committed Sep 18, 2024
1 parent 43d559c commit d978c9a
Show file tree
Hide file tree
Showing 7 changed files with 171 additions and 61 deletions.
18 changes: 14 additions & 4 deletions src/api/payment.ts
Original file line number Diff line number Diff line change
@@ -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<PaymentRequest> => {
const response = await api.get<PaymentRequest>(
`/payments/data/${estimateId}`,
`/payments/data?estimateId=${estimateId}&commissionId=${commissionId}`,
);
return response.data;
};

export const completePayment = async (
impUid: string,
): Promise<PaymentResponse> => {
const response = await api.get<PaymentResponse>(`/payments/${impUid}`);
paymentData: CompletePaymentRequest,
): Promise<CompletePaymentResponse> => {
const response = await api.post<CompletePaymentResponse>(
`/payments/complete/${impUid}`,
paymentData,
);
return response.data;
};

Expand Down
22 changes: 16 additions & 6 deletions src/hooks/usePayment.ts
Original file line number Diff line number Diff line change
@@ -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<PaymentRequest, Error>({
queryKey: ['paymentData', estimateId],
queryFn: () => api.getPaymentData(estimateId),
queryKey: ['paymentData', estimateId, commissionId],
queryFn: () => api.getPaymentData(estimateId, commissionId),
});
};

export const useCompletePayment = () => {
return useMutation<PaymentResponse, Error, string>({
mutationFn: (impUid: string) => api.completePayment(impUid),
return useMutation<
CompletePaymentResponse,
Error,
{ impUid: string; paymentData: CompletePaymentRequest }
>({
mutationFn: ({ impUid, paymentData }) =>
api.completePayment(impUid, paymentData),
});
};

Expand Down
4 changes: 3 additions & 1 deletion src/pages/members/EstimateDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<{
Expand Down
50 changes: 41 additions & 9 deletions src/pages/members/PaymentPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
PayMethod,
RequestPayParams,
RequestPayResponse,
CompletePaymentRequest,
} from '../../types/portone';
import LoadingSpinner from '../../utils/LoadingSpinner';
import { showErrorNotification } from '../../utils/errorHandler';
Expand All @@ -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<ExtendedPayMethod>('card');
const [simplePayMethod, setSimplePayMethod] = useState<string>('');
Expand All @@ -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';
Expand All @@ -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);
Expand Down Expand Up @@ -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('결제 완료 처리 중 오류가 발생했습니다.');
}
Expand Down Expand Up @@ -158,7 +190,7 @@ const PaymentPage: React.FC = () => {
<div className="flex justify-between items-center text-lg">
<span>청소 서비스 금액</span>
<span className="font-bold">
{paymentData.amount.toLocaleString()}
{paymentData.estimate_amount.toLocaleString()}
</span>
</div>
</div>
Expand All @@ -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()}원 결제하기
</button>
</div>
</div>
Expand Down
50 changes: 21 additions & 29 deletions src/types/portone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
79 changes: 71 additions & 8 deletions src/utils/errorHandler.ts
Original file line number Diff line number Diff line change
@@ -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<ErrorCode, string> = {
1000: '필수 정보를 입력해주세요.',
Expand All @@ -17,6 +64,13 @@ const errorMessages: Record<ErrorCode, string> = {
1007: '카카오 사용자 정보 응답을 처리하는 중 오류가 발생했습니다.',
1008: '회원가입 요청이 유효하지 않습니다.',
1009: '유효하지 않은 인증 코드입니다.',
1010: 'JSON 포맷에 적합하지 않은 요청입니다.',
1011: '잘못된 요청입니다.',
1012: '탈퇴한 회원입니다.',
1013: '잘못된 결제 요청입니다.',
1014: '결제 급액이 일치하지 않습니다.',
1015: '결제가 이미 완료되었습니다.',
1016: '결제가 이미 취소되었습니다.',
2001: '인증되지 않은 사용자입니다.',
2002: '로그인이 필요합니다.',
2003: 'Access Token이 만료되었습니다.',
Expand All @@ -25,6 +79,7 @@ const errorMessages: Record<ErrorCode, string> = {
2006: '이메일 인증이 완료되지 않았습니다.',
2007: '이메일 인증 코드가 만료되었습니다.',
2008: '인증 시도 횟수 초과입니다.',
2009: '인증 코드를 찾을 수 없습니다.',
3001: '판매자가 아닙니다.',
3002: '구매자가 아닙니다.',
3003: '청소의뢰가 존재하지 않거나 권한이 없습니다.',
Expand All @@ -39,11 +94,19 @@ const errorMessages: Record<ErrorCode, string> = {
4009: '계좌번호를 찾을 수 없습니다.',
4010: '존재하지 않는 아이디입니다.',
4011: '존재하지 않는 이메일입니다.',
4012: '필수 요청 파라미터가 누락되었습니다.',
4013: '결제를 찾을 수 없습니다.',
5001: '이미 가입된 사용자입니다.',
5002: '중복된 이메일입니다.',
5003: '중복된 닉네임입니다.',
5004: '중복된 전화번호입니다.',
5005: '중복된 회사명입니다.',
6000: '요청 사업자번호 또는 정보가 100개를 초과하였습니다.',
6001: '파일 실행에 실패',
6002: '파일 컴파일에 실패',
6003: '시스템 오류가 발생했습니다.',
6004: '결제 처리에 실패했습니다.',
6005: '결제 취소 처리에 실패했습니다.',
};

export const handleApiError = (error: any): string => {
Expand All @@ -55,6 +118,6 @@ export const handleApiError = (error: any): string => {
};

export const showErrorNotification = (message: string) => {
toast.error(message)
toast.error(message);
console.error(message);
};
};
9 changes: 5 additions & 4 deletions src/utils/paymentAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
};

0 comments on commit d978c9a

Please sign in to comment.