Refresh KIS token on API calls
CI / test (push) Successful in 35s

This commit is contained in:
2026-06-09 15:51:19 +09:00
parent bf63581b6a
commit 5110df995f
4 changed files with 26 additions and 16 deletions
+3 -6
View File
@@ -3,12 +3,9 @@ use crate::core::dotenv;
const KIS_BASE_URL: &str = "https://openapi.koreainvestment.com:9443";
pub fn call(api: &KisApi, endpoint: &str, params: &[(&str, &str)]) -> Result<String, String> {
pub fn call(_api: &KisApi, endpoint: &str, params: &[(&str, &str)]) -> Result<String, String> {
let env = dotenv::read_env(&crate::core::paths::env_file());
let token = api
.token()
.or_else(|| env.get("KIS_ACCESS_TOKEN").map(String::as_str))
.ok_or("로그인이 필요합니다. `openstock api login`을 먼저 실행하세요.")?;
let token = crate::apis::kis::login::access_token_or_login()?;
let appkey = env
.get("KIS_APPKEY")
.ok_or("KIS_APPKEY가 설정 파일에 없습니다.")?;
@@ -29,7 +26,7 @@ pub fn call(api: &KisApi, endpoint: &str, params: &[(&str, &str)]) -> Result<Str
let mut request = crate::core::http::agent()
.get(&url)
.header("Content-Type", "application/json; charset=UTF-8")
.header("authorization", &format!("Bearer {}", token))
.header("authorization", &format!("Bearer {}", token.as_str()))
.header("appkey", appkey)
.header("appsecret", appsecret)
.header("custtype", "P");
+20 -1
View File
@@ -116,6 +116,25 @@ pub fn login(args: &KisLoginArguments) -> Result<String, String> {
Ok(access_token)
}
pub(crate) fn access_token_or_login() -> Result<String, String> {
let env_path = crate::core::paths::env_file();
let env = dotenv::read_env(&env_path);
if let Some(access_token) = valid_access_token(&env) {
return Ok(access_token.to_string());
}
let appkey = env
.get("KIS_APPKEY")
.ok_or("KIS_APPKEY가 설정 파일에 없습니다.")?
.to_string();
let appsecret = env
.get("KIS_APPSECRET")
.ok_or("KIS_APPSECRET가 설정 파일에 없습니다.")?
.to_string();
login(&KisLoginArguments::new(appkey, appsecret, true))
}
fn validate_credentials(args: &KisLoginArguments) -> Result<(), String> {
if args.appkey.trim().is_empty() {
return Err("[KIS] KIS_APPKEY가 비어 있습니다".to_string());
@@ -126,7 +145,7 @@ fn validate_credentials(args: &KisLoginArguments) -> Result<(), String> {
Ok(())
}
fn valid_access_token(env: &HashMap<String, String>) -> Option<&str> {
pub(crate) fn valid_access_token(env: &HashMap<String, String>) -> Option<&str> {
let access_token = env.get("KIS_ACCESS_TOKEN")?;
if access_token.trim().is_empty() {
return None;
-3
View File
@@ -27,9 +27,6 @@ impl KisApi {
}
}
pub(crate) fn token(&self) -> Option<&str> {
self.token.as_deref()
}
}
impl TraderApi for KisApi {
+3 -6
View File
@@ -70,12 +70,9 @@ pub(crate) fn order_cash(
.to_string())
}
fn post_order(api: &KisApi, tr_id: &str, body: &serde_json::Value) -> Result<String, String> {
fn post_order(_api: &KisApi, tr_id: &str, body: &serde_json::Value) -> Result<String, String> {
let env = dotenv::read_env(&crate::core::paths::env_file());
let token = api
.token()
.or_else(|| env.get("KIS_ACCESS_TOKEN").map(String::as_str))
.ok_or("로그인이 필요합니다. `openstock api login`을 먼저 실행하세요.")?;
let token = crate::apis::kis::login::access_token_or_login()?;
let appkey = env
.get("KIS_APPKEY")
.ok_or("KIS_APPKEY가 설정 파일에 없습니다.")?;
@@ -87,7 +84,7 @@ fn post_order(api: &KisApi, tr_id: &str, body: &serde_json::Value) -> Result<Str
let response = crate::core::http::agent()
.post(&url)
.header("Content-Type", "application/json; charset=UTF-8")
.header("authorization", &format!("Bearer {}", token))
.header("authorization", &format!("Bearer {}", token.as_str()))
.header("appkey", appkey)
.header("appsecret", appsecret)
.header("tr_id", tr_id)