package network import io.ktor.client.* import io.ktor.client.call.* import io.ktor.client.engine.cio.CIO import io.ktor.client.plugins.contentnegotiation.* import io.ktor.client.request.* import io.ktor.client.statement.bodyAsText import io.ktor.http.* import io.ktor.serialization.kotlinx.json.* import kotlinx.serialization.json.Json import io.ktor.client.plugins.contentnegotiation.* import io.ktor.client.plugins.logging.* import model.TokenRequest import model.TokenResponse class KisAuthService { private val client = HttpClient(CIO) { install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true encodeDefaults = true // 기본값(grant_type)이 누락되지 않도록 설정 }) } // 디버깅을 위해 로그 추가 (인텔 맥 콘솔에서 전송 데이터 확인 가능) install(Logging) { level = LogLevel.BODY } } private fun getBaseUrl(isSimulation: Boolean): String { return if (isSimulation) { "https://openapivts.koreainvestment.com:29443" // 'openapi' 추가됨 } else { "https://openapi.koreainvestment.com:9443" } } suspend fun fetchAccessToken( appKey: String, secretKey: String, isSimulation: Boolean ): Result { return try { val url = "${getBaseUrl(isSimulation)}/oauth2/tokenP" val response = client.post(url) { // 헤더 설정 (매우 중요) contentType(ContentType.Application.Json) // 요청 바디 (TokenRequest 객체 전달) setBody(TokenRequest( "client_credentials", appKey, secretKey )) } if (response.status == HttpStatusCode.OK) { Result.success(response.body()) } else { val errorBody = response.bodyAsText() println("HTTP ${response.status}: $errorBody") Result.failure(Exception("HTTP ${response.status}: $errorBody")) } } catch (e: Exception) { Result.failure(e) } } }