105 lines
2.7 KiB
TypeScript
105 lines
2.7 KiB
TypeScript
|
|
import { NextRequest, NextResponse } from "next/server";
|
||
|
|
import { executeOrderCash } from "@/lib/kis/trade";
|
||
|
|
import {
|
||
|
|
DashboardStockCashOrderRequest,
|
||
|
|
DashboardStockCashOrderResponse,
|
||
|
|
} from "@/features/dashboard/types/dashboard.types";
|
||
|
|
import {
|
||
|
|
KisCredentialInput,
|
||
|
|
hasKisConfig,
|
||
|
|
normalizeTradingEnv,
|
||
|
|
} from "@/lib/kis/config";
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @file app/api/kis/domestic/order-cash/route.ts
|
||
|
|
* @description 국내주식 현금 주문 API
|
||
|
|
*/
|
||
|
|
|
||
|
|
export async function POST(request: NextRequest) {
|
||
|
|
const credentials = readKisCredentialsFromHeaders(request.headers);
|
||
|
|
|
||
|
|
if (!hasKisConfig(credentials)) {
|
||
|
|
return NextResponse.json(
|
||
|
|
{
|
||
|
|
ok: false,
|
||
|
|
tradingEnv: normalizeTradingEnv(credentials.tradingEnv),
|
||
|
|
message: "KIS API 키 설정이 필요합니다.",
|
||
|
|
},
|
||
|
|
{ status: 400 },
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
try {
|
||
|
|
const body = (await request.json()) as DashboardStockCashOrderRequest;
|
||
|
|
|
||
|
|
// TODO: Validate body fields (symbol, quantity, price, etc.)
|
||
|
|
if (
|
||
|
|
!body.symbol ||
|
||
|
|
!body.accountNo ||
|
||
|
|
!body.accountProductCode ||
|
||
|
|
body.quantity <= 0
|
||
|
|
) {
|
||
|
|
return NextResponse.json(
|
||
|
|
{
|
||
|
|
ok: false,
|
||
|
|
tradingEnv: normalizeTradingEnv(credentials.tradingEnv),
|
||
|
|
message:
|
||
|
|
"주문 정보가 올바르지 않습니다. (종목코드, 계좌번호, 수량 확인)",
|
||
|
|
},
|
||
|
|
{ status: 400 },
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
const output = await executeOrderCash(
|
||
|
|
{
|
||
|
|
symbol: body.symbol,
|
||
|
|
side: body.side,
|
||
|
|
orderType: body.orderType,
|
||
|
|
quantity: body.quantity,
|
||
|
|
price: body.price,
|
||
|
|
accountNo: body.accountNo,
|
||
|
|
accountProductCode: body.accountProductCode,
|
||
|
|
},
|
||
|
|
credentials,
|
||
|
|
);
|
||
|
|
|
||
|
|
const response: DashboardStockCashOrderResponse = {
|
||
|
|
ok: true,
|
||
|
|
tradingEnv: normalizeTradingEnv(credentials.tradingEnv),
|
||
|
|
message: "주문이 전송되었습니다.",
|
||
|
|
orderNo: output.ODNO,
|
||
|
|
orderTime: output.ORD_TMD,
|
||
|
|
orderOrgNo: output.KRX_FWDG_ORD_ORGNO,
|
||
|
|
};
|
||
|
|
|
||
|
|
return NextResponse.json(response);
|
||
|
|
} catch (error) {
|
||
|
|
const message =
|
||
|
|
error instanceof Error
|
||
|
|
? error.message
|
||
|
|
: "주문 전송 중 오류가 발생했습니다.";
|
||
|
|
return NextResponse.json(
|
||
|
|
{
|
||
|
|
ok: false,
|
||
|
|
tradingEnv: normalizeTradingEnv(credentials.tradingEnv),
|
||
|
|
message,
|
||
|
|
},
|
||
|
|
{ status: 500 },
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function readKisCredentialsFromHeaders(headers: Headers): KisCredentialInput {
|
||
|
|
const appKey = headers.get("x-kis-app-key")?.trim();
|
||
|
|
const appSecret = headers.get("x-kis-app-secret")?.trim();
|
||
|
|
const tradingEnv = normalizeTradingEnv(
|
||
|
|
headers.get("x-kis-trading-env") ?? undefined,
|
||
|
|
);
|
||
|
|
|
||
|
|
return {
|
||
|
|
appKey,
|
||
|
|
appSecret,
|
||
|
|
tradingEnv,
|
||
|
|
};
|
||
|
|
}
|