Merge branch 'main' of https://gitea.bpsoft.co.kr/nicepayments/nice-app-web
This commit is contained in:
@@ -84,6 +84,17 @@ export interface ExtensionAccountHolderSearchRequestParams extends ExtensionRequ
|
|||||||
|
|
||||||
export interface ExtensionAccountHolderSearchRequestResponse {
|
export interface ExtensionAccountHolderSearchRequestResponse {
|
||||||
status: boolean;
|
status: boolean;
|
||||||
|
data?: string;
|
||||||
|
error?: {
|
||||||
|
root?: string;
|
||||||
|
errKey?: string;
|
||||||
|
code?: string;
|
||||||
|
message?: string;
|
||||||
|
timestamp?: string;
|
||||||
|
details?: {
|
||||||
|
[key: string]: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ExtensionAccountHolderSearchDownloadExcelParams extends ExtensionRequestParams { // Request
|
export interface ExtensionAccountHolderSearchDownloadExcelParams extends ExtensionRequestParams { // Request
|
||||||
|
|||||||
@@ -32,6 +32,17 @@ export interface ExtensionFundAccountTransferRegistParams {
|
|||||||
|
|
||||||
export interface ExtensionFundAccountTransferRegistResponse {
|
export interface ExtensionFundAccountTransferRegistResponse {
|
||||||
status: boolean;
|
status: boolean;
|
||||||
|
data?: string;
|
||||||
|
error?: {
|
||||||
|
root?: string;
|
||||||
|
errKey?: string;
|
||||||
|
code?: string;
|
||||||
|
message?: string;
|
||||||
|
timestamp?: string;
|
||||||
|
details?: {
|
||||||
|
[key: string]: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface ExtensionFundAccountTransferRequestParams {
|
export interface ExtensionFundAccountTransferRequestParams {
|
||||||
@@ -39,7 +50,18 @@ export interface ExtensionFundAccountTransferRequestParams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ExtensionFundAccountTransferRequestResponse {
|
export interface ExtensionFundAccountTransferRequestResponse {
|
||||||
status: boolean
|
status: boolean;
|
||||||
|
data?: string;
|
||||||
|
error?: {
|
||||||
|
root?: string;
|
||||||
|
errKey?: string;
|
||||||
|
code?: string;
|
||||||
|
message?: string;
|
||||||
|
timestamp?: string;
|
||||||
|
details?: {
|
||||||
|
[key: string]: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum FundAccountSearchCl {
|
export enum FundAccountSearchCl {
|
||||||
|
|||||||
@@ -105,4 +105,30 @@ export interface ExtensionKeyinApplyParams extends ExtensionRequestParams {
|
|||||||
|
|
||||||
export interface ExtensionKeyinApplyResponse {
|
export interface ExtensionKeyinApplyResponse {
|
||||||
status: boolean;
|
status: boolean;
|
||||||
|
data?: {
|
||||||
|
tid?: string | null;
|
||||||
|
approvalNumber?: string | null;
|
||||||
|
approvalDateTime?: string | null;
|
||||||
|
resultCode?: string;
|
||||||
|
resultMessage?: string;
|
||||||
|
cardName?: string | null;
|
||||||
|
cardCode?: string | null;
|
||||||
|
amount?: number | null;
|
||||||
|
installmentMonth?: string | null;
|
||||||
|
merchantNumber?: string | null;
|
||||||
|
acquirerCode?: string | null;
|
||||||
|
acquirerName?: string | null;
|
||||||
|
slipNumber?: string | null;
|
||||||
|
success?: boolean;
|
||||||
|
}
|
||||||
|
error?: {
|
||||||
|
root?: string;
|
||||||
|
errKey?: string;
|
||||||
|
code?: string;
|
||||||
|
resultMessage?: string;
|
||||||
|
timestamp?: string;
|
||||||
|
details?: {
|
||||||
|
path?: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
@@ -198,7 +198,19 @@ export interface ExtensionLinkPayRequestParams extends ExtensionRequestParams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ExtensionLinkPayRequestResponse {
|
export interface ExtensionLinkPayRequestResponse {
|
||||||
status: boolean
|
status: boolean;
|
||||||
|
error?: {
|
||||||
|
root?: string;
|
||||||
|
errKey?: string;
|
||||||
|
code?: string;
|
||||||
|
message?: string;
|
||||||
|
timestamp?: string;
|
||||||
|
details?: {
|
||||||
|
validationErrors?: {
|
||||||
|
[key: string]: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LinkPaymentFormData {
|
export interface LinkPaymentFormData {
|
||||||
|
|||||||
@@ -8,6 +8,10 @@ export interface ExtensionPayoutRequestParams {
|
|||||||
};
|
};
|
||||||
export interface ExtensionPayoutRequestResponse {
|
export interface ExtensionPayoutRequestResponse {
|
||||||
status: boolean;
|
status: boolean;
|
||||||
|
|
||||||
|
error?: {
|
||||||
|
message?: string
|
||||||
|
}
|
||||||
};
|
};
|
||||||
export enum PayoutSearchDateType {
|
export enum PayoutSearchDateType {
|
||||||
REQUEST_DATE = 'REQUEST_DATE',
|
REQUEST_DATE = 'REQUEST_DATE',
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
|||||||
import { useSetOnBack } from '@/widgets/sub-layout/use-sub-layout';
|
import { useSetOnBack } from '@/widgets/sub-layout/use-sub-layout';
|
||||||
import { SingleDatePicker } from '@/shared/ui/filter/single-date-picker';
|
import { SingleDatePicker } from '@/shared/ui/filter/single-date-picker';
|
||||||
import { LinkPaymentFormData, LinkPaymentSendMethod } from '@/entities/additional-service/model/link-pay/types';
|
import { LinkPaymentFormData, LinkPaymentSendMethod } from '@/entities/additional-service/model/link-pay/types';
|
||||||
|
import { NumericFormat } from 'react-number-format';
|
||||||
|
import { ChangeEvent } from 'react';
|
||||||
|
|
||||||
interface LinkPaymentStep1Props {
|
interface LinkPaymentStep1Props {
|
||||||
formData: LinkPaymentFormData;
|
formData: LinkPaymentFormData;
|
||||||
@@ -25,9 +27,7 @@ export const LinkPaymentStep1 = ({ formData, setFormData }: LinkPaymentStep1Prop
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleAmountChange = (value: string) => {
|
const handleAmountChange = (value: string) => {
|
||||||
// 숫자만 추출
|
|
||||||
const onlyNumbers = value.replace(/[^0-9]/g, '');
|
const onlyNumbers = value.replace(/[^0-9]/g, '');
|
||||||
// 빈 문자열이면 0, 아니면 숫자로 변환 (앞의 0 제거됨)
|
|
||||||
const numericValue = onlyNumbers === '' ? 0 : parseInt(onlyNumbers, 10);
|
const numericValue = onlyNumbers === '' ? 0 : parseInt(onlyNumbers, 10);
|
||||||
setFormData({ ...formData, amount: numericValue });
|
setFormData({ ...formData, amount: numericValue });
|
||||||
};
|
};
|
||||||
@@ -93,13 +93,15 @@ export const LinkPaymentStep1 = ({ formData, setFormData }: LinkPaymentStep1Prop
|
|||||||
<div className="issue-row gap-10">
|
<div className="issue-row gap-10">
|
||||||
<div className="issue-label">상품가격</div>
|
<div className="issue-label">상품가격</div>
|
||||||
<div className="issue-field">
|
<div className="issue-field">
|
||||||
<input
|
<NumericFormat
|
||||||
type="text"
|
value={formData.amount}
|
||||||
placeholder="0"
|
allowNegative={false}
|
||||||
value={formData.amount === 0 ? '' : formData.amount}
|
displayType="input"
|
||||||
onChange={(e) => handleAmountChange(e.target.value)}
|
thousandSeparator={true}
|
||||||
inputMode="numeric"
|
onValueChange={(values) => {
|
||||||
pattern="[0-9]*"
|
const { floatValue } = values;
|
||||||
|
setFormData({...formData, amount: floatValue ?? 0});
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import {ProcessStep} from "@/entities/transaction/model/types";
|
import { ProcessStep } from "@/entities/transaction/model/types";
|
||||||
import {useSetOnBack} from "@/widgets/sub-layout/use-sub-layout";
|
import { useSetOnBack } from "@/widgets/sub-layout/use-sub-layout";
|
||||||
import { IdentityType, Language } from '@/entities/additional-service/model/types'
|
import { IdentityType, Language } from '@/entities/additional-service/model/types'
|
||||||
import { LinkContentType, LinkPaymentFormData } from "@/entities/additional-service/model/link-pay/types";
|
import { LinkContentType, LinkPaymentFormData } from "@/entities/additional-service/model/link-pay/types";
|
||||||
|
import { PatternFormat } from 'react-number-format';
|
||||||
|
|
||||||
export interface LinkPaymentStep2Props {
|
export interface LinkPaymentStep2Props {
|
||||||
setProcessStep: ((processStep: ProcessStep) => void);
|
setProcessStep: ((processStep: ProcessStep) => void);
|
||||||
@@ -18,6 +19,7 @@ export const LinkPaymentStep2 = ({
|
|||||||
setProcessStep(ProcessStep.One);
|
setProcessStep(ProcessStep.One);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
const handleInputChange = (field: string, value: string) => {
|
const handleInputChange = (field: string, value: string) => {
|
||||||
setFormData({ ...formData, [field]: value });
|
setFormData({ ...formData, [field]: value });
|
||||||
};
|
};
|
||||||
@@ -53,34 +55,39 @@ export const LinkPaymentStep2 = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="issue-row gap-10">
|
{formData.sendMethod === 'EMAIL' &&
|
||||||
<div className="issue-label wid-105">구매자 이메일</div>
|
<div className="issue-row gap-10">
|
||||||
<div className="issue-field">
|
<div className="issue-label wid-105">구매자 이메일</div>
|
||||||
<input
|
<div className="issue-field">
|
||||||
type="text"
|
<input
|
||||||
placeholder="test@nicepay.co.kr"
|
type="text"
|
||||||
value={formData.email}
|
placeholder="test@nicepay.co.kr"
|
||||||
onChange={(e) => handleInputChange('email', e.target.value)}
|
value={formData.email}
|
||||||
/>
|
onChange={(e) => handleInputChange('email', e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
}
|
||||||
|
|
||||||
<div className="issue-row gap-10">
|
{(formData.sendMethod === 'SMS' || formData.sendMethod === 'KAKAO') &&
|
||||||
<div className="issue-label wid-105">구매자<br/>휴대폰 번호</div>
|
<div className="issue-row gap-10">
|
||||||
<div className="issue-field">
|
<div className="issue-label wid-105">구매자<br />휴대폰 번호</div>
|
||||||
<input
|
<div className="issue-field">
|
||||||
type="text"
|
<input
|
||||||
placeholder="01012345678"
|
type="text"
|
||||||
value={formData.phoneNumber}
|
placeholder="01012345678"
|
||||||
onChange={(e) => handleInputChange('phoneNumber', e.target.value)}
|
value={formData.phoneNumber}
|
||||||
/>
|
onChange={(e) => handleInputChange('phoneNumber', e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
}
|
||||||
|
|
||||||
|
|
||||||
<div className="issue-row gap-10 beetween">
|
<div className="issue-row gap-10 beetween">
|
||||||
<div className="issue-label wid-105">구매자 정보 대조</div>
|
<div className="issue-label wid-105">구매자 정보 대조</div>
|
||||||
<label className="settings-switch">
|
<label className="settings-switch">
|
||||||
<input type="checkbox" checked={formData.isIdentity} onChange={(e) => handleIdentityToggle(e.target.checked)}/>
|
<input type="checkbox" checked={formData.isIdentity} onChange={(e) => handleIdentityToggle(e.target.checked)} />
|
||||||
<span className="slider"></span>
|
<span className="slider"></span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@@ -89,14 +96,16 @@ export const LinkPaymentStep2 = ({
|
|||||||
<div className="issue-field">
|
<div className="issue-field">
|
||||||
<div className="chip-row">
|
<div className="chip-row">
|
||||||
<span
|
<span
|
||||||
className={`keyword-tag flex-1 ${formData.identityType === IdentityType.INDIVIDUAL ? 'active' : ''}`}
|
className={`keyword-tag flex-1 ${formData.identityType === IdentityType.INDIVIDUAL ? 'active' : ''} ${!formData.isIdentity ? 'disabled' : ''}`}
|
||||||
onClick={() => handleIdendityTypeChange(IdentityType.INDIVIDUAL)}
|
onClick={() => formData.isIdentity && handleIdendityTypeChange(IdentityType.INDIVIDUAL)}
|
||||||
|
style={!formData.isIdentity ? { opacity: 0.5, cursor: 'not-allowed' } : {}}
|
||||||
>
|
>
|
||||||
개인
|
개인
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
className={`keyword-tag flex-1 ${formData.identityType === IdentityType.CORPORATE ? 'active' : ''}`}
|
className={`keyword-tag flex-1 ${formData.identityType === IdentityType.CORPORATE ? 'active' : ''} ${!formData.isIdentity ? 'disabled' : ''}`}
|
||||||
onClick={() => handleIdendityTypeChange(IdentityType.CORPORATE)}
|
onClick={() => formData.isIdentity && handleIdendityTypeChange(IdentityType.CORPORATE)}
|
||||||
|
style={!formData.isIdentity ? { opacity: 0.5, cursor: 'not-allowed' } : {}}
|
||||||
>
|
>
|
||||||
법인
|
법인
|
||||||
</span>
|
</span>
|
||||||
@@ -107,12 +116,31 @@ export const LinkPaymentStep2 = ({
|
|||||||
<div className="issue-row gap-10">
|
<div className="issue-row gap-10">
|
||||||
<div className="issue-label wid-105"></div>
|
<div className="issue-label wid-105"></div>
|
||||||
<div className='issue-field'>
|
<div className='issue-field'>
|
||||||
<input
|
{formData.identityType === IdentityType.CORPORATE ? (
|
||||||
type="number"
|
<PatternFormat
|
||||||
placeholder="생년월일 6자리"
|
format="##########"
|
||||||
value={formData.identityValue}
|
placeholder="사업자번호 10자리"
|
||||||
onChange={(e) => handleInputChange('identityValue', e.target.value)}
|
value={formData.identityValue}
|
||||||
/>
|
valueIsNumericString
|
||||||
|
onValueChange={(values) => {
|
||||||
|
const { value } = values;
|
||||||
|
handleInputChange('identityValue', value);
|
||||||
|
}}
|
||||||
|
disabled={!formData.isIdentity}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<PatternFormat
|
||||||
|
format="######"
|
||||||
|
placeholder="생년월일 6자리"
|
||||||
|
value={formData.identityValue}
|
||||||
|
valueIsNumericString
|
||||||
|
onValueChange={(values) => {
|
||||||
|
const { value } = values;
|
||||||
|
handleInputChange('identityValue', value);
|
||||||
|
}}
|
||||||
|
disabled={!formData.isIdentity}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import {
|
|||||||
import { useExtensionAccountHolderSearchRequestMutation } from '@/entities/additional-service/api/account-holder-search/use-extension-account-holder-search-reqeust-mutation';
|
import { useExtensionAccountHolderSearchRequestMutation } from '@/entities/additional-service/api/account-holder-search/use-extension-account-holder-search-reqeust-mutation';
|
||||||
import { ExtensionAccountHolderSearchRequestParams } from '@/entities/additional-service/model/account-holder-search/types';
|
import { ExtensionAccountHolderSearchRequestParams } from '@/entities/additional-service/model/account-holder-search/types';
|
||||||
import { useStore } from '@/shared/model/store';
|
import { useStore } from '@/shared/model/store';
|
||||||
|
import { NumericFormat } from 'react-number-format';
|
||||||
|
import { snackBar } from '@/shared/lib';
|
||||||
|
|
||||||
export const AccountHolderSearchRequestPage = () => {
|
export const AccountHolderSearchRequestPage = () => {
|
||||||
const { navigate } = useNavigate();
|
const { navigate } = useNavigate();
|
||||||
@@ -48,11 +50,21 @@ export const AccountHolderSearchRequestPage = () => {
|
|||||||
|
|
||||||
accountHolderSearchRequest(reuqestParams)
|
accountHolderSearchRequest(reuqestParams)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
console.log("계좌성명 조회 조회 신청 성공 응답: ", response.status)
|
if (response.status) {
|
||||||
navigate(PATHS.additionalService.accountHolderSearch.list);
|
console.log("계좌성명 조회 조회 신청 성공 응답: ", response.status)
|
||||||
|
snackBar("계좌성명 조회 신청을 성공하였습니다.")
|
||||||
|
navigate(PATHS.additionalService.accountHolderSearch.list);
|
||||||
|
} else {
|
||||||
|
const errorMessage = response.error?.message || '계좌성명 조회 신청이 실패하였습니다.';
|
||||||
|
snackBar(`[실패] ${errorMessage}`);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error("계좌성명 조회 조회 신청 실패: ", error)
|
console.error("계좌성명 조회 조회 신청 실패: ", error)
|
||||||
|
const errorMessage = error?.response?.data?.error?.message ||
|
||||||
|
error?.message ||
|
||||||
|
'계좌성명 조회 신청 중 오류가 발생했습니다.';
|
||||||
|
snackBar(`[실패] ${errorMessage}`);
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -109,12 +121,19 @@ export const AccountHolderSearchRequestPage = () => {
|
|||||||
<div className="billing-row">
|
<div className="billing-row">
|
||||||
<div className="billing-label">계좌번호</div>
|
<div className="billing-label">계좌번호</div>
|
||||||
<div className="billing-field">
|
<div className="billing-field">
|
||||||
<input
|
<NumericFormat
|
||||||
type="number"
|
|
||||||
placeholder=''
|
|
||||||
value={formData.accountNo}
|
value={formData.accountNo}
|
||||||
onChange={(e) => handleInputChange('accountNo', e.target.value)}
|
valueIsNumericString
|
||||||
></input>
|
allowNegative={false}
|
||||||
|
decimalScale={0}
|
||||||
|
isAllowed={(values) => {
|
||||||
|
const { value } = values;
|
||||||
|
return !value || value.length <= 14;
|
||||||
|
}}
|
||||||
|
onValueChange={(values) => {
|
||||||
|
setFormData({ ...formData, accountNo: values.value });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { ArsPaymentMethod, ExtensionArsApplyParams, ExtensionArsApplyResponse }
|
|||||||
import { ArsRequestSuccessPage } from './request-success-page';
|
import { ArsRequestSuccessPage } from './request-success-page';
|
||||||
import { useStore } from '@/shared/model/store';
|
import { useStore } from '@/shared/model/store';
|
||||||
import { snackBar } from '@/shared/lib';
|
import { snackBar } from '@/shared/lib';
|
||||||
|
import { NumericFormat, PatternFormat } from 'react-number-format';
|
||||||
|
|
||||||
export const ArsRequestPage = () => {
|
export const ArsRequestPage = () => {
|
||||||
const { navigate } = useNavigate();
|
const { navigate } = useNavigate();
|
||||||
@@ -58,17 +59,17 @@ export const ArsRequestPage = () => {
|
|||||||
arsPaymentMethod: arsPaymentMethod,
|
arsPaymentMethod: arsPaymentMethod,
|
||||||
};
|
};
|
||||||
arsApply(arsApplyParams)
|
arsApply(arsApplyParams)
|
||||||
.then((rs: ExtensionArsApplyResponse) => {
|
.then((rs: ExtensionArsApplyResponse) => {
|
||||||
if (rs.status === true) {
|
if (rs.status === true) {
|
||||||
setSuccessPageOn(true);
|
setSuccessPageOn(true);
|
||||||
} else {
|
} else {
|
||||||
const errorMessage = rs.error?.message || '신청을 실패하였습니다.';
|
const errorMessage = rs.error?.message || '신청을 실패하였습니다.';
|
||||||
snackBar(`[실패] ${errorMessage}`);
|
snackBar(`[실패] ${errorMessage}`);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
snackBar(`[실패] ${error?.response?.data?.message || error?.response?.data?.error?.message}` || '[실패] 신청을 실패하였습니다.')
|
snackBar(`[실패] ${error?.response?.data?.message || error?.response?.data?.error?.message}` || '[실패] 신청을 실패하였습니다.')
|
||||||
})
|
})
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -82,6 +83,11 @@ export const ArsRequestPage = () => {
|
|||||||
return phoneRegex.test(phone);
|
return phoneRegex.test(phone);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isValidEmail = (email: string) => {
|
||||||
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||||
|
return emailRegex.test(email);
|
||||||
|
};
|
||||||
|
|
||||||
const isFormValid = () => {
|
const isFormValid = () => {
|
||||||
return (
|
return (
|
||||||
mid.trim() !== '' &&
|
mid.trim() !== '' &&
|
||||||
@@ -164,16 +170,12 @@ export const ArsRequestPage = () => {
|
|||||||
<div className="billing-row">
|
<div className="billing-row">
|
||||||
<div className="billing-label">금액 <span>*</span></div>
|
<div className="billing-label">금액 <span>*</span></div>
|
||||||
<div className="billing-field">
|
<div className="billing-field">
|
||||||
<input
|
<NumericFormat
|
||||||
type="text"
|
|
||||||
value={amount}
|
value={amount}
|
||||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
allowNegative={false}
|
||||||
const onlyNumbers = e.target.value.replace(/[^0-9]/g, ''); // 숫자만 남김
|
displayType="input"
|
||||||
setAmount(onlyNumbers === '' ? 0 : parseInt(onlyNumbers));
|
onChange={(e: ChangeEvent<HTMLInputElement>) => setAmount(parseInt(e.target.value))}
|
||||||
}}
|
></NumericFormat>
|
||||||
inputMode="numeric" // 모바일 키보드 숫자 전용
|
|
||||||
pattern="[0-9]*" // 브라우저 기본 숫자만 유효하도록
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -228,6 +230,8 @@ export const ArsRequestPage = () => {
|
|||||||
value={email}
|
value={email}
|
||||||
placeholder='test@nicepay.co.kr'
|
placeholder='test@nicepay.co.kr'
|
||||||
onChange={(e: ChangeEvent<HTMLInputElement>) => setEamil(e.target.value)}
|
onChange={(e: ChangeEvent<HTMLInputElement>) => setEamil(e.target.value)}
|
||||||
|
className={email && !isValidEmail(email) ? 'error' : ''}
|
||||||
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -54,8 +54,18 @@ export const FundAccountTransferDetailPage = () => {
|
|||||||
seq: seq
|
seq: seq
|
||||||
};
|
};
|
||||||
extensionFundAccountTransferRequest(params).then((rs: ExtensionFundAccountTransferRequestResponse) => {
|
extensionFundAccountTransferRequest(params).then((rs: ExtensionFundAccountTransferRequestResponse) => {
|
||||||
callDetail();
|
if (rs.status) {
|
||||||
snackBar("이체요청을 성공하였습니다.")
|
callDetail();
|
||||||
|
snackBar("이체요청을 성공하였습니다.")
|
||||||
|
} else {
|
||||||
|
const errorMessage = rs.error?.message || '이체요청이 실패하였습니다.';
|
||||||
|
snackBar(`[실패] ${errorMessage}`);
|
||||||
|
}
|
||||||
|
}).catch((error) => {
|
||||||
|
const errorMessage = error?.response?.data?.error?.message ||
|
||||||
|
error?.message ||
|
||||||
|
'이체요청 중 오류가 발생했습니다.';
|
||||||
|
snackBar(`[실패] ${errorMessage}`);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -63,8 +63,14 @@ export const FundAccountTransferRequestPage = () => {
|
|||||||
snackBar("이체등록을 성공하였습니다.")
|
snackBar("이체등록을 성공하였습니다.")
|
||||||
resetForm();
|
resetForm();
|
||||||
} else {
|
} else {
|
||||||
snackBar("이체등록이 실패하였습니다.")
|
const errorMessage = rs.error?.message || '이체등록이 실패하였습니다.';
|
||||||
|
snackBar(`[실패] ${errorMessage}`);
|
||||||
}
|
}
|
||||||
|
}).catch((error) => {
|
||||||
|
const errorMessage = error?.response?.data?.error?.message ||
|
||||||
|
error?.message ||
|
||||||
|
'이체등록 중 오류가 발생했습니다.';
|
||||||
|
snackBar(`[실패] ${errorMessage}`);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -128,6 +134,10 @@ export const FundAccountTransferRequestPage = () => {
|
|||||||
valueIsNumericString
|
valueIsNumericString
|
||||||
allowNegative={false}
|
allowNegative={false}
|
||||||
decimalScale={0}
|
decimalScale={0}
|
||||||
|
isAllowed={(values) => {
|
||||||
|
const { value } = values;
|
||||||
|
return !value || value.length <= 14;
|
||||||
|
}}
|
||||||
onValueChange={(values) => setAccountNo(values.value)}
|
onValueChange={(values) => setAccountNo(values.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -138,7 +148,7 @@ export const FundAccountTransferRequestPage = () => {
|
|||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={accountName}
|
value={accountName}
|
||||||
onChange={(e: ChangeEvent<HTMLInputElement>) => setAccountName(e.target.value) }
|
onChange={(e: ChangeEvent<HTMLInputElement>) => setAccountName(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -148,8 +158,12 @@ export const FundAccountTransferRequestPage = () => {
|
|||||||
<NumericFormat
|
<NumericFormat
|
||||||
value={amount}
|
value={amount}
|
||||||
allowNegative={false}
|
allowNegative={false}
|
||||||
|
thousandSeparator={true}
|
||||||
displayType='input'
|
displayType='input'
|
||||||
onChange={(e: ChangeEvent<HTMLInputElement>) => setAmount(parseInt(e.target.value))}
|
onValueChange={(values) => {
|
||||||
|
const { floatValue } = values;
|
||||||
|
setAmount(floatValue ?? 0);
|
||||||
|
}}
|
||||||
></NumericFormat>
|
></NumericFormat>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -15,12 +15,14 @@ import { overlay } from 'overlay-kit';
|
|||||||
import { Dialog } from '@/shared/ui/dialogs/dialog';
|
import { Dialog } from '@/shared/ui/dialogs/dialog';
|
||||||
import { useStore } from '@/shared/model/store';
|
import { useStore } from '@/shared/model/store';
|
||||||
import { snackBar } from '@/shared/lib';
|
import { snackBar } from '@/shared/lib';
|
||||||
|
import { NumericFormat, PatternFormat } from 'react-number-format';
|
||||||
|
|
||||||
export const KeyInPaymentRequestPage = () => {
|
export const KeyInPaymentRequestPage = () => {
|
||||||
const { navigate } = useNavigate();
|
const { navigate } = useNavigate();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
|
||||||
const userMid = useStore.getState().UserStore.mid;
|
const userMid = useStore.getState().UserStore.mid;
|
||||||
|
const midOptions = useStore.getState().UserStore.selectOptionsMids
|
||||||
|
|
||||||
const [mid, setMid] = useState<string>(userMid || '');
|
const [mid, setMid] = useState<string>(userMid || '');
|
||||||
const [productName, setProductName] = useState<string>('');
|
const [productName, setProductName] = useState<string>('');
|
||||||
@@ -79,50 +81,24 @@ export const KeyInPaymentRequestPage = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
keyInApply(keyInApplyParams).then((rs) => {
|
keyInApply(keyInApplyParams).then((rs) => {
|
||||||
console.log('결제 응답:', rs);
|
if (rs.data?.success) {
|
||||||
if (rs.status) {
|
|
||||||
// 성공: 화면 유지 & 입력 내용 초기화
|
// 성공: 화면 유지 & 입력 내용 초기화
|
||||||
snackBar("KEY-IN 결제 신청을 성공하였습니다.")
|
snackBar("KEY-IN 결제 신청을 성공하였습니다.")
|
||||||
resetForm();
|
resetForm();
|
||||||
} else {
|
} else {
|
||||||
// 실패: 화면 유지 & 입력 내용 유지
|
// 실패: 화면 유지 & 입력 내용 유지
|
||||||
showErrorDialog('결제에 실패했습니다. 입력 내용을 확인해주세요.');
|
const errorMessage = rs.data?.resultMessage || rs.error?.resultMessage || '결제 신청에 실패했습니다.';
|
||||||
|
// HTML 태그 제거
|
||||||
|
const cleanMessage = errorMessage.replace(/<br\s*\/?>/gi, ' ').trim();
|
||||||
|
snackBar(`[실패] ${cleanMessage}`);
|
||||||
}
|
}
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
console.error('결제 실패:', error);
|
console.error('결제 실패:', error);
|
||||||
showErrorDialog(error?.message || '결제 요청 중 오류가 발생했습니다');
|
const errorMessage = error?.response?.data?.error?.resultMessage || error?.message || '결제 요청 중 오류가 발생했습니다';
|
||||||
|
snackBar(`[실패] ${errorMessage}`);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const showSuccessDialog = () => {
|
|
||||||
overlay.open(({ isOpen, close, unmount }) => {
|
|
||||||
return (
|
|
||||||
<Dialog
|
|
||||||
afterLeave={unmount}
|
|
||||||
open={isOpen}
|
|
||||||
onClose={close}
|
|
||||||
onConfirmClick={close}
|
|
||||||
message="결제가 성공적으로 처리되었습니다"
|
|
||||||
buttonLabel={['확인']}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const showErrorDialog = (errorMessage: string) => {
|
|
||||||
overlay.open(({ isOpen, close, unmount }) => {
|
|
||||||
return (
|
|
||||||
<Dialog
|
|
||||||
afterLeave={unmount}
|
|
||||||
open={isOpen}
|
|
||||||
onClose={close}
|
|
||||||
onConfirmClick={close}
|
|
||||||
message={errorMessage}
|
|
||||||
buttonLabel={['확인']}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const isValidPhoneNumber = (phone: string) => {
|
const isValidPhoneNumber = (phone: string) => {
|
||||||
const phoneRegex = /^01[0|1|6|7|8|9][0-9]{7,8}$/;
|
const phoneRegex = /^01[0|1|6|7|8|9][0-9]{7,8}$/;
|
||||||
@@ -139,24 +115,17 @@ export const KeyInPaymentRequestPage = () => {
|
|||||||
cardNo3.length === 4 && cardNo4.length === 4;
|
cardNo3.length === 4 && cardNo4.length === 4;
|
||||||
};
|
};
|
||||||
|
|
||||||
const isValidCardExpiration = () => {
|
|
||||||
if (expMon.length !== 2 || expYear.length !== 2) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const month = parseInt(expMon);
|
|
||||||
return month >= 1 && month <= 12;
|
|
||||||
};
|
|
||||||
|
|
||||||
const isFormValid = () => {
|
const isFormValid = () => {
|
||||||
return (
|
return (
|
||||||
mid.trim() !== '' &&
|
mid.trim() !== '' &&
|
||||||
productName.trim() !== '' &&
|
productName.trim() !== '' &&
|
||||||
amount > 0 &&
|
amount > 0 &&
|
||||||
customerName.trim() !== '' &&
|
customerName.trim() !== '' &&
|
||||||
|
expMon.trim() !== '' &&
|
||||||
|
expYear.trim() !== '' &&
|
||||||
isValidEmail(email) &&
|
isValidEmail(email) &&
|
||||||
isValidPhoneNumber(phoneNumber) &&
|
isValidPhoneNumber(phoneNumber) &&
|
||||||
isValidCardNumber() &&
|
isValidCardNumber()
|
||||||
isValidCardExpiration()
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -174,11 +143,19 @@ export const KeyInPaymentRequestPage = () => {
|
|||||||
<div className="billing-row">
|
<div className="billing-row">
|
||||||
<div className="billing-label">가맹점 <span>*</span></div>
|
<div className="billing-label">가맹점 <span>*</span></div>
|
||||||
<div className="billing-field">
|
<div className="billing-field">
|
||||||
<input
|
<select
|
||||||
type="text"
|
|
||||||
value={mid}
|
value={mid}
|
||||||
readOnly={true}
|
onChange={(e: ChangeEvent<HTMLSelectElement>) => setMid(e.target.value)}
|
||||||
/>
|
>
|
||||||
|
{
|
||||||
|
midOptions.map((value) => (
|
||||||
|
<option
|
||||||
|
key={value.value}
|
||||||
|
value={value.value}
|
||||||
|
>{value.name}</option>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -196,17 +173,16 @@ export const KeyInPaymentRequestPage = () => {
|
|||||||
<div className="billing-row">
|
<div className="billing-row">
|
||||||
<div className="billing-label">상품가격 <span>*</span></div>
|
<div className="billing-label">상품가격 <span>*</span></div>
|
||||||
<div className="billing-field">
|
<div className="billing-field">
|
||||||
<input
|
<NumericFormat
|
||||||
type="text"
|
value={amount}
|
||||||
value={amount === 0 ? '' : amount.toString()}
|
thousandSeparator={true}
|
||||||
placeholder="금액을 입력하세요"
|
allowNegative={false}
|
||||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
displayType="input"
|
||||||
const onlyNumbers = e.target.value.replace(/[^0-9]/g, ''); // 숫자만 남김
|
onValueChange={(values) => {
|
||||||
setAmount(onlyNumbers === '' ? 0 : parseInt(onlyNumbers, 10));
|
const { floatValue} = values;
|
||||||
|
setAmount( floatValue ?? 0);
|
||||||
}}
|
}}
|
||||||
inputMode="numeric" // 모바일 키보드 숫자 전용
|
></NumericFormat>
|
||||||
pattern="[0-9]*" // 브라우저 기본 숫자만 유효하도록
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -227,7 +203,6 @@ export const KeyInPaymentRequestPage = () => {
|
|||||||
<input
|
<input
|
||||||
type="email"
|
type="email"
|
||||||
value={email}
|
value={email}
|
||||||
placeholder='test@nicepay.co.kr'
|
|
||||||
onChange={(e: ChangeEvent<HTMLInputElement>) => setEmail(e.target.value)}
|
onChange={(e: ChangeEvent<HTMLInputElement>) => setEmail(e.target.value)}
|
||||||
className={email && !isValidEmail(email) ? 'error' : ''}
|
className={email && !isValidEmail(email) ? 'error' : ''}
|
||||||
/>
|
/>
|
||||||
@@ -240,7 +215,7 @@ export const KeyInPaymentRequestPage = () => {
|
|||||||
<input
|
<input
|
||||||
type="tel"
|
type="tel"
|
||||||
value={phoneNumber}
|
value={phoneNumber}
|
||||||
placeholder='01012345678'
|
placeholder='- 제외하고 입력'
|
||||||
onChange={(e: ChangeEvent<HTMLInputElement>) => {
|
onChange={(e: ChangeEvent<HTMLInputElement>) => {
|
||||||
const onlyNumbers = e.target.value.replace(/[^0-9]/g, '');
|
const onlyNumbers = e.target.value.replace(/[^0-9]/g, '');
|
||||||
setPhoneNumber(onlyNumbers);
|
setPhoneNumber(onlyNumbers);
|
||||||
@@ -256,92 +231,85 @@ export const KeyInPaymentRequestPage = () => {
|
|||||||
<div className="billing-row">
|
<div className="billing-row">
|
||||||
<div className="billing-label">카드번호 <span>*</span></div>
|
<div className="billing-label">카드번호 <span>*</span></div>
|
||||||
<div className="billing-field">
|
<div className="billing-field">
|
||||||
<input
|
<PatternFormat
|
||||||
type="text"
|
|
||||||
value={cardNo1}
|
value={cardNo1}
|
||||||
onChange={(e: ChangeEvent<HTMLInputElement>) => {
|
allowEmptyFormatting
|
||||||
const onlyNumbers = e.target.value.replace(/[^0-9]/g, '');
|
valueIsNumericString
|
||||||
if (onlyNumbers.length <= 4) setCardNo1(onlyNumbers);
|
format="####"
|
||||||
}}
|
onChange={(e: ChangeEvent<HTMLInputElement>) => setCardNo1(e.target.value)}
|
||||||
inputMode="numeric"
|
>
|
||||||
pattern="[0-9]*"
|
</PatternFormat>
|
||||||
maxLength={4}
|
|
||||||
placeholder="1234"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="billing-field">
|
<div className="billing-field">
|
||||||
<input
|
<PatternFormat
|
||||||
type="text"
|
|
||||||
value={cardNo2}
|
value={cardNo2}
|
||||||
onChange={(e: ChangeEvent<HTMLInputElement>) => {
|
allowEmptyFormatting
|
||||||
const onlyNumbers = e.target.value.replace(/[^0-9]/g, '');
|
valueIsNumericString
|
||||||
if (onlyNumbers.length <= 4) setCardNo2(onlyNumbers);
|
format="####"
|
||||||
}}
|
onChange={(e: ChangeEvent<HTMLInputElement>) => setCardNo2(e.target.value)}
|
||||||
inputMode="numeric"
|
>
|
||||||
pattern="[0-9]*"
|
</PatternFormat>
|
||||||
maxLength={4}
|
|
||||||
placeholder="5678"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="billing-field">
|
<div className="billing-field">
|
||||||
<input
|
<PatternFormat
|
||||||
type="text"
|
|
||||||
value={cardNo3}
|
value={cardNo3}
|
||||||
onChange={(e: ChangeEvent<HTMLInputElement>) => {
|
allowEmptyFormatting
|
||||||
const onlyNumbers = e.target.value.replace(/[^0-9]/g, '');
|
valueIsNumericString
|
||||||
if (onlyNumbers.length <= 4) setCardNo3(onlyNumbers);
|
format="####"
|
||||||
}}
|
onChange={(e: ChangeEvent<HTMLInputElement>) => setCardNo3(e.target.value)}
|
||||||
inputMode="numeric"
|
>
|
||||||
pattern="[0-9]*"
|
</PatternFormat>
|
||||||
maxLength={4}
|
|
||||||
placeholder="9012"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="billing-field">
|
<div className="billing-field">
|
||||||
<input
|
<PatternFormat
|
||||||
type="text"
|
|
||||||
value={cardNo4}
|
value={cardNo4}
|
||||||
onChange={(e: ChangeEvent<HTMLInputElement>) => {
|
allowEmptyFormatting
|
||||||
const onlyNumbers = e.target.value.replace(/[^0-9]/g, '');
|
valueIsNumericString
|
||||||
if (onlyNumbers.length <= 4) setCardNo4(onlyNumbers);
|
format="####"
|
||||||
}}
|
onChange={(e: ChangeEvent<HTMLInputElement>) => setCardNo4(e.target.value)}
|
||||||
inputMode="numeric"
|
>
|
||||||
pattern="[0-9]*"
|
</PatternFormat>
|
||||||
maxLength={4}
|
|
||||||
placeholder="3456"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="billing-row">
|
<div className="billing-row">
|
||||||
<div className="billing-label">유효기간(월/년)<span>*</span></div>
|
<div className="billing-label">유효기간(월/년)<span>*</span></div>
|
||||||
<div className="billing-field" style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
|
<div className="billing-field" style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
|
||||||
<input
|
<PatternFormat
|
||||||
type="text"
|
|
||||||
value={expMon}
|
|
||||||
onChange={(e: ChangeEvent<HTMLInputElement>) => {
|
|
||||||
const onlyNumbers = e.target.value.replace(/[^0-9]/g, '');
|
|
||||||
if (onlyNumbers.length <= 2) setExpMon(onlyNumbers);
|
|
||||||
}}
|
|
||||||
inputMode="numeric"
|
|
||||||
pattern="[0-9]*"
|
|
||||||
maxLength={2}
|
|
||||||
placeholder='MM'
|
placeholder='MM'
|
||||||
|
value={expMon}
|
||||||
|
valueIsNumericString
|
||||||
|
format='##'
|
||||||
|
isAllowed={(values) => {
|
||||||
|
const { value } = values;
|
||||||
|
if (!value) return true;
|
||||||
|
const numValue = parseInt(value);
|
||||||
|
return numValue >= 1 && numValue <= 12;
|
||||||
|
}}
|
||||||
|
onValueChange={(values) => {
|
||||||
|
const { value } = values;
|
||||||
|
setExpMon(value);
|
||||||
|
}}
|
||||||
|
onBlur={() => {
|
||||||
|
if (expMon.length === 1 && parseInt(expMon) >= 1 && parseInt(expMon) <= 9) {
|
||||||
|
setExpMon(expMon.padStart(2, '0'));
|
||||||
|
}
|
||||||
|
}}
|
||||||
style={{ flex: 1 }}
|
style={{ flex: 1 }}
|
||||||
|
inputMode="numeric"
|
||||||
/>
|
/>
|
||||||
<span>/</span>
|
<span>/</span>
|
||||||
<input
|
<PatternFormat
|
||||||
type="text"
|
|
||||||
value={expYear}
|
|
||||||
onChange={(e: ChangeEvent<HTMLInputElement>) => {
|
|
||||||
const onlyNumbers = e.target.value.replace(/[^0-9]/g, '');
|
|
||||||
if (onlyNumbers.length <= 2) setExpYear(onlyNumbers);
|
|
||||||
}}
|
|
||||||
inputMode="numeric"
|
|
||||||
pattern="[0-9]*"
|
|
||||||
maxLength={2}
|
|
||||||
placeholder='YY'
|
placeholder='YY'
|
||||||
|
value={expYear}
|
||||||
|
valueIsNumericString
|
||||||
|
format='##'
|
||||||
|
onValueChange={(values) => {
|
||||||
|
const { value } = values;
|
||||||
|
setExpYear(value);
|
||||||
|
}}
|
||||||
style={{ flex: 1 }}
|
style={{ flex: 1 }}
|
||||||
|
inputMode="numeric"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -354,8 +322,8 @@ export const KeyInPaymentRequestPage = () => {
|
|||||||
value={instmnt}
|
value={instmnt}
|
||||||
onChange={(e: ChangeEvent<HTMLSelectElement>) => setInstmnt(e.target.value)}
|
onChange={(e: ChangeEvent<HTMLSelectElement>) => setInstmnt(e.target.value)}
|
||||||
>
|
>
|
||||||
<option value="00">일시불 (무이자)</option>
|
<option value="00">일시불</option>
|
||||||
{amount >= 50000 && (
|
{/* {amount >= 50000 && (
|
||||||
<>
|
<>
|
||||||
<option value="02">2개월</option>
|
<option value="02">2개월</option>
|
||||||
<option value="03">3개월</option>
|
<option value="03">3개월</option>
|
||||||
@@ -369,7 +337,7 @@ export const KeyInPaymentRequestPage = () => {
|
|||||||
<option value="11">11개월</option>
|
<option value="11">11개월</option>
|
||||||
<option value="12">12개월</option>
|
<option value="12">12개월</option>
|
||||||
</>
|
</>
|
||||||
)}
|
)} */}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ import { useLocation } from 'react-router';
|
|||||||
import { IMAGE_ROOT } from "@/shared/constants/common";
|
import { IMAGE_ROOT } from "@/shared/constants/common";
|
||||||
import { PATHS } from '@/shared/constants/paths';
|
import { PATHS } from '@/shared/constants/paths';
|
||||||
import { useExtensionLinkPayRequestMutation } from '@/entities/additional-service/api/link-payment/use-extension-link-pay-request-mutation';
|
import { useExtensionLinkPayRequestMutation } from '@/entities/additional-service/api/link-payment/use-extension-link-pay-request-mutation';
|
||||||
import { ExtensionLinkPayRequestParams, LinkPaymentFormData } from '@/entities/additional-service/model/link-pay/types';
|
import { ExtensionLinkPayRequestParams, ExtensionLinkPayRequestResponse, LinkPaymentFormData } from '@/entities/additional-service/model/link-pay/types';
|
||||||
|
import { snackBar } from '@/shared/lib';
|
||||||
|
|
||||||
export const LinkPaymentApplyConfirmPage = () => {
|
export const LinkPaymentApplyConfirmPage = () => {
|
||||||
const { navigate } = useNavigate();
|
const { navigate } = useNavigate();
|
||||||
@@ -40,17 +41,30 @@ export const LinkPaymentApplyConfirmPage = () => {
|
|||||||
language: formData.language,
|
language: formData.language,
|
||||||
linkContentType: formData.linkContentType
|
linkContentType: formData.linkContentType
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log("Link Payment 요청 파라미터: ", requestParams);
|
|
||||||
|
|
||||||
linkPayRequest(requestParams)
|
linkPayRequest(requestParams)
|
||||||
.then((response) => {
|
.then((rs: ExtensionLinkPayRequestResponse) => {
|
||||||
console.log("Link Payment 성공 응답: ", response);
|
if (rs.status) {
|
||||||
navigate(PATHS.additionalService.linkPayment.confirmSuccess);
|
navigate(PATHS.additionalService.linkPayment.confirmSuccess);
|
||||||
|
} else {
|
||||||
|
// 응답은 성공했지만 status가 false인 경우
|
||||||
|
const validationErrors = rs.error?.details?.validationErrors;
|
||||||
|
if (validationErrors) {
|
||||||
|
// validation 에러 메시지들을 수집
|
||||||
|
const errorMessages = Object.values(validationErrors).join('\n');
|
||||||
|
snackBar(`[실패] ${errorMessages}`);
|
||||||
|
} else {
|
||||||
|
// 일반 에러 메시지
|
||||||
|
const errorMessage = rs.error?.message || '요청을 처리할 수 없습니다.';
|
||||||
|
snackBar(`[실패] ${errorMessage}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error("Link Payment 실패: ", error);
|
// 네트워크 에러 등 예외 상황
|
||||||
// 에러 처리 로직 추가 가능
|
const errorMessage = error?.response?.data?.error?.message ||
|
||||||
|
error?.message ||
|
||||||
|
'요청 중 오류가 발생했습니다';
|
||||||
|
snackBar(`[실패] ${errorMessage}`);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -66,18 +80,18 @@ export const LinkPaymentApplyConfirmPage = () => {
|
|||||||
<div className="attention-icon" aria-hidden="true">
|
<div className="attention-icon" aria-hidden="true">
|
||||||
<img src={IMAGE_ROOT + '/ico_alert.svg'} alt="주의" />
|
<img src={IMAGE_ROOT + '/ico_alert.svg'} alt="주의" />
|
||||||
</div>
|
</div>
|
||||||
<h1 className="preview-title">발송 메시지를<br/>최종 확인하세요</h1>
|
<h1 className="preview-title">발송 메시지를<br />최종 확인하세요</h1>
|
||||||
<div className="preview-result">
|
<div className="preview-result">
|
||||||
<p className="preview-text">
|
<p className="preview-text">
|
||||||
TEST 고객님, 안녕하세요?<br/>
|
{formData.buyerName} 고객님, 안녕하세요?<br />
|
||||||
나이스페이먼츠 주식회사에서 <br/>
|
나이스페이먼츠 주식회사에서 <br />
|
||||||
결제하실 내역 안내드립니다.<br/>
|
결제하실 내역 안내드립니다.<br />
|
||||||
아래 URL로 접속하시면 상세 내역 확인과 결제 진행이 가능합니다.<br/><br/>
|
아래 URL로 접속하시면 상세 내역 확인과 결제 진행이 가능합니다.<br /><br />
|
||||||
!${pay_url}<br/><br/>
|
!${pay_url}<br /><br />
|
||||||
<b>
|
<b>
|
||||||
가맹점 상호 : 나이스페이먼츠 주식회사<br/>
|
가맹점 상호 : 나이스페이먼츠 주식회사<br />
|
||||||
상품명 : {formData.goodsName}<br/>
|
상품명 : {formData.goodsName}<br />
|
||||||
금액 : {formData.amount}원
|
금액 : {formData.amount.toLocaleString()}원
|
||||||
</b>
|
</b>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import {useState} from 'react';
|
import { useState } from 'react';
|
||||||
import {LinkPaymentStep1} from '@/entities/additional-service/ui/link-payment/apply/link-payment-step1';
|
import { LinkPaymentStep1 } from '@/entities/additional-service/ui/link-payment/apply/link-payment-step1';
|
||||||
import {LinkPaymentStep2} from '@/entities/additional-service/ui/link-payment/apply/link-payment-step2';
|
import { LinkPaymentStep2 } from '@/entities/additional-service/ui/link-payment/apply/link-payment-step2';
|
||||||
import {HeaderType} from '@/entities/common/model/types';
|
import { HeaderType } from '@/entities/common/model/types';
|
||||||
import {ProcessStep} from '@/entities/transaction/model/types';
|
import { ProcessStep } from '@/entities/transaction/model/types';
|
||||||
import {useSetFooterMode, useSetHeaderTitle, useSetHeaderType} from '@/widgets/sub-layout/use-sub-layout';
|
import { useSetFooterMode, useSetHeaderTitle, useSetHeaderType } from '@/widgets/sub-layout/use-sub-layout';
|
||||||
import {useNavigate} from '@/shared/lib/hooks/use-navigate';
|
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
|
||||||
import {PATHS} from "@/shared/constants/paths";
|
import { PATHS } from "@/shared/constants/paths";
|
||||||
import { IdentityType, Language } from '@/entities/additional-service/model/types';
|
import { IdentityType, Language } from '@/entities/additional-service/model/types';
|
||||||
import { LinkContentType, LinkPaymentFormData, LinkPaymentSendMethod } from '@/entities/additional-service/model/link-pay/types';
|
import { LinkContentType, LinkPaymentFormData, LinkPaymentSendMethod } from '@/entities/additional-service/model/link-pay/types';
|
||||||
import { useStore } from '@/shared/model/store';
|
import { useStore } from '@/shared/model/store';
|
||||||
@@ -14,25 +14,25 @@ import moment from 'moment';
|
|||||||
|
|
||||||
|
|
||||||
export const LinkPaymentApplyPage = () => {
|
export const LinkPaymentApplyPage = () => {
|
||||||
const { navigate } = useNavigate();
|
const { navigate } = useNavigate();
|
||||||
const userMid = useStore.getState().UserStore.mid;
|
const userMid = useStore.getState().UserStore.mid;
|
||||||
const [processStep, setProcessStep] = useState<ProcessStep>(ProcessStep.One);
|
const [processStep, setProcessStep] = useState<ProcessStep>(ProcessStep.One);
|
||||||
const [formData, setFormData] = useState<LinkPaymentFormData>({
|
const [formData, setFormData] = useState<LinkPaymentFormData>({
|
||||||
mid: userMid,
|
mid: userMid,
|
||||||
sendMethod: LinkPaymentSendMethod.SMS,
|
sendMethod: LinkPaymentSendMethod.SMS,
|
||||||
goodsName: '',
|
goodsName: '',
|
||||||
amount: 0,
|
amount: 0,
|
||||||
moid: '',
|
moid: '',
|
||||||
paymentLimitDate: moment().format('YYYY.MM.DD'),
|
paymentLimitDate: moment().format('YYYY.MM.DD'),
|
||||||
buyerName: '',
|
buyerName: '',
|
||||||
email: '',
|
email: '',
|
||||||
phoneNumber: '',
|
phoneNumber: '',
|
||||||
isIdentity: false,
|
isIdentity: false,
|
||||||
identityType: IdentityType.INDIVIDUAL,
|
identityType: IdentityType.INDIVIDUAL,
|
||||||
identityValue: '',
|
identityValue: '',
|
||||||
language: Language.KR,
|
language: Language.KR,
|
||||||
linkContentType: LinkContentType.BASIC
|
linkContentType: LinkContentType.BASIC
|
||||||
});
|
});
|
||||||
|
|
||||||
useSetHeaderTitle('링크결제 신청');
|
useSetHeaderTitle('링크결제 신청');
|
||||||
useSetHeaderType(HeaderType.LeftArrow);
|
useSetHeaderType(HeaderType.LeftArrow);
|
||||||
@@ -64,15 +64,11 @@ export const LinkPaymentApplyPage = () => {
|
|||||||
|
|
||||||
// Step2 필수 필드 검증
|
// Step2 필수 필드 검증
|
||||||
const isStep2Valid = () => {
|
const isStep2Valid = () => {
|
||||||
const basicFieldsValid = (
|
|
||||||
formData.buyerName.trim() !== '' &&
|
|
||||||
formData.email.trim() !== '' &&
|
|
||||||
isValidEmail(formData.email) &&
|
|
||||||
formData.phoneNumber.trim() !== '' &&
|
|
||||||
isValidPhoneNumber(formData.phoneNumber)
|
|
||||||
);
|
|
||||||
|
|
||||||
// isIdentity가 true면 identityValue도 필수
|
const basicFieldsValid = formData.sendMethod === 'EMAIL'
|
||||||
|
? formData.email.trim() !== '' && isValidEmail(formData.email)
|
||||||
|
: formData.buyerName.trim() !== '' && formData.phoneNumber.trim() !== '' && isValidPhoneNumber(formData.phoneNumber);
|
||||||
|
|
||||||
if (formData.isIdentity) {
|
if (formData.isIdentity) {
|
||||||
return basicFieldsValid && formData.identityValue.trim() !== '';
|
return basicFieldsValid && formData.identityValue.trim() !== '';
|
||||||
}
|
}
|
||||||
@@ -85,14 +81,14 @@ export const LinkPaymentApplyPage = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onClickToChangeTab = () => {
|
const onClickToChangeTab = () => {
|
||||||
if(processStep === ProcessStep.One) {
|
if (processStep === ProcessStep.One) {
|
||||||
setProcessStep(ProcessStep.Two);
|
setProcessStep(ProcessStep.Two);
|
||||||
}
|
}
|
||||||
else if(processStep === ProcessStep.Two) {
|
else if (processStep === ProcessStep.Two) {
|
||||||
navigate(PATHS.additionalService.linkPayment.requestConfirm, {
|
navigate(PATHS.additionalService.linkPayment.requestConfirm, {
|
||||||
state: { formData }
|
state: { formData }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -103,58 +99,58 @@ export const LinkPaymentApplyPage = () => {
|
|||||||
<div className="option-list">
|
<div className="option-list">
|
||||||
<div className="issue-progress">
|
<div className="issue-progress">
|
||||||
<div className="bar">
|
<div className="bar">
|
||||||
{(processStep === ProcessStep.One) &&
|
{(processStep === ProcessStep.One) &&
|
||||||
<div
|
<div
|
||||||
className="fill"
|
className="fill"
|
||||||
style={{ width: '50%' }}
|
style={{ width: '50%' }}
|
||||||
></div>
|
></div>
|
||||||
}
|
}
|
||||||
{(processStep === ProcessStep.Two) &&
|
{(processStep === ProcessStep.Two) &&
|
||||||
<div
|
<div
|
||||||
className="fill"
|
className="fill"
|
||||||
style={{ width: '100%' }}
|
style={{ width: '100%' }}
|
||||||
></div>
|
></div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{(processStep === ProcessStep.One) &&
|
|
||||||
<LinkPaymentStep1
|
|
||||||
formData={formData}
|
|
||||||
setFormData={setFormData}
|
|
||||||
></LinkPaymentStep1>
|
|
||||||
}
|
|
||||||
{ (processStep === ProcessStep.Two) &&
|
|
||||||
<LinkPaymentStep2
|
|
||||||
setProcessStep={ setProcessStep }
|
|
||||||
formData={formData}
|
|
||||||
setFormData={setFormData}
|
|
||||||
></LinkPaymentStep2>
|
|
||||||
}
|
|
||||||
|
|
||||||
</div>
|
|
||||||
{(processStep === ProcessStep.One) &&
|
{(processStep === ProcessStep.One) &&
|
||||||
<div className="apply-row">
|
<LinkPaymentStep1
|
||||||
<button
|
formData={formData}
|
||||||
className="btn-50 btn-blue flex-1"
|
setFormData={setFormData}
|
||||||
onClick={() => onClickToChangeTab()}
|
></LinkPaymentStep1>
|
||||||
disabled={!isStep1Valid()}
|
|
||||||
>다음</button>
|
|
||||||
</div>
|
|
||||||
}
|
}
|
||||||
{(processStep === ProcessStep.Two) &&
|
{(processStep === ProcessStep.Two) &&
|
||||||
<div className="apply-row two-button">
|
<LinkPaymentStep2
|
||||||
<button
|
setProcessStep={setProcessStep}
|
||||||
className="btn-50 btn-darkgray flex-1"
|
formData={formData}
|
||||||
onClick={() => onClickToBack()}
|
setFormData={setFormData}
|
||||||
>이전</button>
|
></LinkPaymentStep2>
|
||||||
<button
|
|
||||||
className="btn-50 btn-blue flex-3"
|
|
||||||
onClick={() => onClickToChangeTab()}
|
|
||||||
disabled={!isStep2Valid()}
|
|
||||||
>결제 신청</button>
|
|
||||||
</div>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{(processStep === ProcessStep.One) &&
|
||||||
|
<div className="apply-row">
|
||||||
|
<button
|
||||||
|
className="btn-50 btn-blue flex-1"
|
||||||
|
onClick={() => onClickToChangeTab()}
|
||||||
|
disabled={!isStep1Valid()}
|
||||||
|
>다음</button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
{(processStep === ProcessStep.Two) &&
|
||||||
|
<div className="apply-row two-button">
|
||||||
|
<button
|
||||||
|
className="btn-50 btn-darkgray flex-1"
|
||||||
|
onClick={() => onClickToBack()}
|
||||||
|
>이전</button>
|
||||||
|
<button
|
||||||
|
className="btn-50 btn-blue flex-3"
|
||||||
|
onClick={() => onClickToChangeTab()}
|
||||||
|
disabled={!isStep2Valid()}
|
||||||
|
>결제 신청</button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export const LinkPaymentWaitDetailPage = () => {
|
|||||||
const [titleInfo, setTitleInfo] = useState<TitleInfo>();
|
const [titleInfo, setTitleInfo] = useState<TitleInfo>();
|
||||||
const [paymentInfo, setPaymentInfo] = useState<PaymentInfo>();
|
const [paymentInfo, setPaymentInfo] = useState<PaymentInfo>();
|
||||||
|
|
||||||
useSetHeaderTitle('링크결제 상세_발송대기');
|
useSetHeaderTitle('링크결제 상세 발송대기');
|
||||||
useSetHeaderType(HeaderType.RightClose);
|
useSetHeaderType(HeaderType.RightClose);
|
||||||
useSetOnBack(() => {
|
useSetOnBack(() => {
|
||||||
navigate(PATHS.additionalService.linkPayment.pendingSend);
|
navigate(PATHS.additionalService.linkPayment.pendingSend);
|
||||||
@@ -56,23 +56,14 @@ export const LinkPaymentWaitDetailPage = () => {
|
|||||||
}
|
}
|
||||||
linkPayWaitDelete(deleteParam)
|
linkPayWaitDelete(deleteParam)
|
||||||
.then((rs) => {
|
.then((rs) => {
|
||||||
onClickToNavigate(PATHS.additionalService.linkPayment.pendingSend)
|
callDetail();
|
||||||
snackBar("삭제를 성공하였습니다.")
|
snackBar("삭제를 성공하였습니다.")
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error("Resend 실패: ", error);
|
|
||||||
snackBar(`[실패] ${error?.response?.data?.message}`)
|
snackBar(`[실패] ${error?.response?.data?.message}`)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const onClickToNavigate = (path: string) => {
|
|
||||||
let timeout = setTimeout(() => {
|
|
||||||
clearTimeout(timeout);
|
|
||||||
navigate(PATHS.additionalService.linkPayment.pendingSend, {
|
|
||||||
});
|
|
||||||
}, 10)
|
|
||||||
};
|
|
||||||
|
|
||||||
const onClickToCancel = () => {
|
const onClickToCancel = () => {
|
||||||
let msg = '삭제 하시겠습니까?';
|
let msg = '삭제 하시겠습니까?';
|
||||||
|
|
||||||
|
|||||||
@@ -50,8 +50,8 @@ export const PayoutListPage = () => {
|
|||||||
const [fromDate, setFromDate] = useState<string>(moment().format('YYYYMMDD'));
|
const [fromDate, setFromDate] = useState<string>(moment().format('YYYYMMDD'));
|
||||||
const [toDate, setToDate] = useState<string>(moment().format('YYYYMMDD'));
|
const [toDate, setToDate] = useState<string>(moment().format('YYYYMMDD'));
|
||||||
const [status, setStatus] = useState<PayoutDisbursementStatus>(PayoutDisbursementStatus.ALL);
|
const [status, setStatus] = useState<PayoutDisbursementStatus>(PayoutDisbursementStatus.ALL);
|
||||||
const [minAmount, setMinAmount] = useState<number | undefined>(0);
|
const [minAmount, setMinAmount] = useState<number | undefined>();
|
||||||
const [maxAmount, setMaxAmount] = useState<number | undefined>(50000000);
|
const [maxAmount, setMaxAmount] = useState<number | undefined>();
|
||||||
const [emailBottomSheetOn, setEmailBottomSheetOn] = useState<boolean>(false);
|
const [emailBottomSheetOn, setEmailBottomSheetOn] = useState<boolean>(false);
|
||||||
|
|
||||||
const { mutateAsync: extensionPayoutList } = useExtensionPayoutListMutation();
|
const { mutateAsync: extensionPayoutList } = useExtensionPayoutListMutation();
|
||||||
|
|||||||
@@ -44,13 +44,17 @@ export const PayoutRequestPage = () => {
|
|||||||
settlementDate: settlementDate,
|
settlementDate: settlementDate,
|
||||||
};
|
};
|
||||||
extensionPayoutRequest(params)
|
extensionPayoutRequest(params)
|
||||||
.then((rs) => {
|
.then((rs) => {
|
||||||
snackBar("신청을 성공하였습니다.")
|
if (rs.status) {
|
||||||
})
|
snackBar("신청을 성공하였습니다.")
|
||||||
.catch((error) => {
|
} else {
|
||||||
snackBar(`[실패] ${error?.response?.data?.message} `|| '[실패] 신청을 실패하였습니다.')
|
snackBar(`[실패] ${rs.error?.message}`)
|
||||||
})
|
}
|
||||||
;
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
snackBar(`[실패] ${error?.response?.data?.message} ` || '[실패] 신청을 실패하였습니다.')
|
||||||
|
})
|
||||||
|
;
|
||||||
};
|
};
|
||||||
|
|
||||||
const isFormValid = () => {
|
const isFormValid = () => {
|
||||||
@@ -90,9 +94,13 @@ export const PayoutRequestPage = () => {
|
|||||||
<NumericFormat
|
<NumericFormat
|
||||||
value={disbursementAmount}
|
value={disbursementAmount}
|
||||||
allowNegative={false}
|
allowNegative={false}
|
||||||
|
thousandSeparator={true}
|
||||||
displayType="input"
|
displayType="input"
|
||||||
onChange={(e: ChangeEvent<HTMLInputElement>) => setDisbursementAmount(parseInt(e.target.value))}
|
onValueChange={(values) => {
|
||||||
></NumericFormat>
|
const { floatValue } = values;
|
||||||
|
setDisbursementAmount(floatValue ?? 0);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="billing-row">
|
<div className="billing-row">
|
||||||
@@ -102,8 +110,7 @@ export const PayoutRequestPage = () => {
|
|||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="날짜 선택"
|
placeholder="날짜 선택"
|
||||||
value={settlementDate ? moment(settlementDate).format('YYYY.MM.DD') : '' }
|
value={settlementDate ? moment(settlementDate).format('YYYY.MM.DD') : ''}
|
||||||
readOnly={true}
|
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
className="date-btn"
|
className="date-btn"
|
||||||
|
|||||||
Reference in New Issue
Block a user