189 lines
8.2 KiB
TypeScript
189 lines
8.2 KiB
TypeScript
|
|
/**
|
||
|
|
* @file lib/kis/error-codes.ts
|
||
|
|
* @description KIS FAQ 오류코드(msg_cd) 문구를 공통으로 해석하는 유틸입니다.
|
||
|
|
* @see https://apiportal.koreainvestment.com/faq-error-code 한국투자증권 공식 오류코드 기준
|
||
|
|
*/
|
||
|
|
|
||
|
|
export const KIS_ERROR_CODE_REFERENCE_URL =
|
||
|
|
"https://apiportal.koreainvestment.com/faq-error-code";
|
||
|
|
|
||
|
|
const KIS_ERROR_CODE_MESSAGE_MAP = {
|
||
|
|
EGW00001: "일시적 오류가 발생했습니다.",
|
||
|
|
EGW00002: "서버 에러가 발생했습니다.",
|
||
|
|
EGW00003: "접근이 거부되었습니다.",
|
||
|
|
EGW00004: "권한을 부여받지 않은 고객입니다.",
|
||
|
|
EGW00101: "유효하지 않은 요청입니다.",
|
||
|
|
EGW00102: "AppKey는 필수입니다.",
|
||
|
|
EGW00103: "유효하지 않은 AppKey입니다.",
|
||
|
|
EGW00104: "AppSecret은 필수입니다.",
|
||
|
|
EGW00105: "유효하지 않은 AppSecret입니다.",
|
||
|
|
EGW00106: "redirect_uri는 필수입니다.",
|
||
|
|
EGW00107: "유효하지 않은 redirect_uri입니다.",
|
||
|
|
EGW00108: "유효하지 않은 서비스구분(service)입니다.",
|
||
|
|
EGW00109: "scope는 필수입니다.",
|
||
|
|
EGW00110: "유효하지 않은 scope 입니다.",
|
||
|
|
EGW00111: "유효하지 않은 state 입니다.",
|
||
|
|
EGW00112: "유효하지 않은 grant 입니다.",
|
||
|
|
EGW00113: "응답구분(response_type)은 필수입니다.",
|
||
|
|
EGW00114: "지원하지 않는 응답구분(response_type)입니다.",
|
||
|
|
EGW00115: "권한부여 타입(grant_type)은 필수입니다.",
|
||
|
|
EGW00116: "지원하지 않는 권한부여 타입(grant_type)입니다.",
|
||
|
|
EGW00117: "지원하지 않는 토큰 타입(token_type)입니다.",
|
||
|
|
EGW00118: "유효하지 않은 code 입니다.",
|
||
|
|
EGW00119: "code를 찾을 수 없습니다.",
|
||
|
|
EGW00120: "기간이 만료된 code 입니다.",
|
||
|
|
EGW00121: "유효하지 않은 token 입니다.",
|
||
|
|
EGW00122: "token을 찾을 수 없습니다.",
|
||
|
|
EGW00123: "기간이 만료된 token 입니다.",
|
||
|
|
EGW00124: "유효하지 않은 session_key 입니다.",
|
||
|
|
EGW00125: "session_key를 찾을 수 없습니다.",
|
||
|
|
EGW00126: "기간이 만료된 session_key 입니다.",
|
||
|
|
EGW00127: "제휴사번호(corpno)는 필수입니다.",
|
||
|
|
EGW00128: "계좌번호(acctno)는 필수입니다.",
|
||
|
|
EGW00129: "HTS_ID는 필수입니다.",
|
||
|
|
EGW00130: "유효하지 않은 유저(user)입니다.",
|
||
|
|
EGW00131: "유효하지 않은 hashkey입니다.",
|
||
|
|
EGW00132: "Content-Type이 유효하지 않습니다.",
|
||
|
|
EGW00201: "초당 거래건수를 초과하였습니다.",
|
||
|
|
EGW00202: "GW라우팅 중 오류가 발생했습니다.",
|
||
|
|
EGW00203: "OPS라우팅 중 오류가 발생했습니다.",
|
||
|
|
EGW00204: "Internal Gateway 인스턴스를 잘못 입력했습니다.",
|
||
|
|
EGW00205: "credentials_type이 유효하지 않습니다.(Bearer)",
|
||
|
|
EGW00206: "API 사용 권한이 없습니다.",
|
||
|
|
EGW00207: "IP 주소가 없거나 유효하지 않습니다.",
|
||
|
|
EGW00208: "고객유형(custtype)이 유효하지 않습니다.",
|
||
|
|
EGW00209: "일련번호(seq_no)가 유효하지 않습니다.",
|
||
|
|
EGW00210: "법인고객의 경우 모의투자를 이용할 수 없습니다.",
|
||
|
|
EGW00211: "고객명(personalname)은 필수 입니다.",
|
||
|
|
EGW00212: "휴대전화번호(personalphone)는 필수 입니다.",
|
||
|
|
EGW00213: "제휴사명(corpname)은 필수 입니다. / 모의투자 tr이 아닙니다.",
|
||
|
|
EGW00300: "Gateway 라우팅 오류가 발생했습니다.",
|
||
|
|
EGW00301: "연결 시간이 초과되었습니다. 직전 거래를 반드시 확인하세요.",
|
||
|
|
EGW00302: "거래시간이 초과되었습니다. 직전 거래를 반드시 확인하세요.",
|
||
|
|
EGW00303: "법인고객에게 허용되지 않은 IP접근입니다.",
|
||
|
|
EGW00304:
|
||
|
|
"고객식별키(법인 personalSeckey, 개인 appSecret)가 유효하지 않습니다.",
|
||
|
|
OPSQ0001: "호출 전처리 오류 입니다.",
|
||
|
|
OPSQ0002: "없는 서비스 코드 입니다.",
|
||
|
|
OPSQ0003: "호출 오류 입니다.",
|
||
|
|
OPSQ0004: "호출 후처리 오류 입니다.",
|
||
|
|
OPSQ0005: "호출 후처리 오류 입니다.",
|
||
|
|
OPSQ0006: "호출 후처리 오류 입니다.",
|
||
|
|
OPSQ0007: "호출 후처리(헤더설정) 오류 입니다.",
|
||
|
|
OPSQ0008: "호출 후처리(MCI전송) 오류 입니다.",
|
||
|
|
OPSQ0009: "호출 후처리(MCI수신) 오류 입니다.",
|
||
|
|
OPSQ0010: "호출 결과처리(리소스 부족) 오류 입니다.",
|
||
|
|
OPSQ0011: "호출 결과처리(리소스 부족) 오류 입니다.",
|
||
|
|
OPSQ1002: "세션 연결 오류.",
|
||
|
|
OPSQ2000: "ERROR : INPUT INVALID_CHECK_ACNO",
|
||
|
|
OPSQ2001: "ERROR : INPUT INVALID_CHECK_MRKT_DIV_CODE",
|
||
|
|
OPSQ2002: "ERROR : INPUT INVALID_CHECK_FIELD_LENGTH",
|
||
|
|
OPSQ2003: "ERROR : SET_MCI_SEND_DATA",
|
||
|
|
OPSQ3001: "ERROR : RESPONSE_ADDITEMTOOBJECT",
|
||
|
|
OPSQ3002: "ERROR : GET_CALL_PARAM_MCI_SEND_DATA_LEN",
|
||
|
|
OPSQ3004: "ERROR : OUT_STRING_ARRAY ALLOC FAILED",
|
||
|
|
OPSQ9995: "JSON PARSING ERROR : body not found",
|
||
|
|
OPSQ9996: "JSON PARSING ERROR : header not found",
|
||
|
|
OPSQ9997: "JSON PARSING ERROR : invalid json format",
|
||
|
|
OPSQ9998: "JSON PARSING ERROR : seq_no not found",
|
||
|
|
OPSQ9999: "JSON PARSING ERROR : tr_id not found",
|
||
|
|
OPSP0000: "SUBSCRIBE SUCCESS",
|
||
|
|
OPSP0001: "UNSUBSCRIBE SUCCESS",
|
||
|
|
OPSP0002: "ALREADY IN SUBSCRIBE",
|
||
|
|
OPSP0003: "UNSUBSCRIBE ERROR(not found!)",
|
||
|
|
OPSP0007: "SUBSCRIBE INTERNAL ERROR",
|
||
|
|
OPSP0008: "MAX SUBSCRIBE OVER",
|
||
|
|
OPSP0009: "SUBSCRIBE ERROR : mci send failed",
|
||
|
|
OPSP0010: "SUBSCRIBE WARNNING : invalid appkey",
|
||
|
|
OPSP0011: "invalid approval(appkey) : NOT FOUND",
|
||
|
|
OPSP8991: "SUBSCRIBE ERROR : invalid tr_id",
|
||
|
|
OPSP8992: "SUBSCRIBE ERROR : invalid tr_key",
|
||
|
|
OPSP8993: "JSON PARSING ERROR : invalid tr_key",
|
||
|
|
OPSP8994: "JSON PARSING ERROR : personalseckey not found",
|
||
|
|
OPSP8995: "JSON PARSING ERROR : appsecret not found",
|
||
|
|
OPSP8996: "ALREADY IN USE appkey",
|
||
|
|
OPSP8997: "JSON PARSING ERROR : invalid tr_type",
|
||
|
|
OPSP8998: "JSON PARSING ERROR : invalid custtype",
|
||
|
|
OPSP8999: "resource not available (ALLOC_CALL_PARAM)",
|
||
|
|
OPSP9990: "JSON PARSING ERROR : tr_key not found",
|
||
|
|
OPSP9991: "JSON PARSING ERROR : input not found",
|
||
|
|
OPSP9992: "JSON PARSING ERROR : body not found",
|
||
|
|
OPSP9993: "JSON PARSING ERROR : internal error",
|
||
|
|
OPSP9994: "JSON PARSING ERROR : INVALID appkey",
|
||
|
|
OPSP9995: "JSON PARSING ERROR : resource not available",
|
||
|
|
OPSP9996: "JSON PARSING ERROR : appkey",
|
||
|
|
OPSP9997: "JSON PARSING ERROR : custtype not found",
|
||
|
|
OPSP9998: "JSON PARSING ERROR : header not found",
|
||
|
|
OPSP9999: "JSON PARSING ERROR : invalid json format",
|
||
|
|
} as const;
|
||
|
|
|
||
|
|
export interface KisErrorGuide {
|
||
|
|
code: string;
|
||
|
|
message: string;
|
||
|
|
referenceUrl: string;
|
||
|
|
}
|
||
|
|
|
||
|
|
interface BuildKisErrorDetailParams {
|
||
|
|
message?: string;
|
||
|
|
msgCode?: string;
|
||
|
|
extraMessages?: Array<string | undefined>;
|
||
|
|
}
|
||
|
|
|
||
|
|
function normalizeKisErrorCode(msgCode?: string) {
|
||
|
|
return msgCode?.trim().toUpperCase() ?? "";
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @description KIS msg_cd를 공식 FAQ 문구와 매칭합니다.
|
||
|
|
* @param msgCode KIS 응답 msg_cd
|
||
|
|
* @returns 코드/문구/참고 URL 정보. 없으면 null
|
||
|
|
* @see lib/kis/client.ts kisGet/kisPost 비즈니스 오류 메시지 구성
|
||
|
|
* @see features/kis-realtime/stores/kisWebSocketStore.ts 실시간 제어 오류 안내문 구성
|
||
|
|
*/
|
||
|
|
export function getKisErrorGuide(msgCode?: string): KisErrorGuide | null {
|
||
|
|
const code = normalizeKisErrorCode(msgCode);
|
||
|
|
if (!code) return null;
|
||
|
|
|
||
|
|
const message =
|
||
|
|
KIS_ERROR_CODE_MESSAGE_MAP[
|
||
|
|
code as keyof typeof KIS_ERROR_CODE_MESSAGE_MAP
|
||
|
|
];
|
||
|
|
if (!message) return null;
|
||
|
|
|
||
|
|
return {
|
||
|
|
code,
|
||
|
|
message,
|
||
|
|
referenceUrl: KIS_ERROR_CODE_REFERENCE_URL,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @description KIS 오류 조각(msg1/msg_cd/부가메시지)을 사람이 읽기 쉬운 한 줄로 합칩니다.
|
||
|
|
* @param params 오류 문자열 조합 입력값
|
||
|
|
* @returns 중복 제거된 상세 메시지
|
||
|
|
* @see lib/kis/token.ts buildTokenIssueDetail 토큰 발급/폐기 오류 상세 구성
|
||
|
|
* @see lib/kis/approval.ts issueKisApprovalKey 승인키 발급 오류 상세 구성
|
||
|
|
*/
|
||
|
|
export function buildKisErrorDetail({
|
||
|
|
message,
|
||
|
|
msgCode,
|
||
|
|
extraMessages = [],
|
||
|
|
}: BuildKisErrorDetailParams) {
|
||
|
|
const tokens = new Set<string>();
|
||
|
|
|
||
|
|
for (const raw of [...extraMessages, message]) {
|
||
|
|
const normalized = raw?.trim();
|
||
|
|
if (normalized) tokens.add(normalized);
|
||
|
|
}
|
||
|
|
|
||
|
|
const guide = getKisErrorGuide(msgCode);
|
||
|
|
const normalizedCode = normalizeKisErrorCode(msgCode);
|
||
|
|
if (guide) {
|
||
|
|
tokens.add(`${guide.code} (${guide.message})`);
|
||
|
|
} else if (normalizedCode) {
|
||
|
|
tokens.add(normalizedCode);
|
||
|
|
}
|
||
|
|
|
||
|
|
return [...tokens].join(" / ");
|
||
|
|
}
|