using System.Text.Json; namespace Kbx.Shared.Providers; public sealed class KisMarketDataAdapter(HttpClient http, IKbxProviderSecretStore secrets, IKisAccessTokenProvider tokens, KisRequestPacer pacer) : IKbxExternalProviderAdapter { public string ProviderId => "provider.kis.market-data"; public async Task> GetDomesticStockCurrentPriceAsync(KisCurrentPriceRequest input, CancellationToken ct) { await pacer.WaitAsync(input.Sandbox, ct); var appKey = await secrets.GetRequiredAsync("ExternalProviders:Kis:AppKey", ct); var appSecret = await secrets.GetRequiredAsync("ExternalProviders:Kis:AppSecret", ct); var token = await tokens.GetAsync(input.Sandbox, ct); var baseUri = input.Sandbox ? "https://openapivts.koreainvestment.com:29443" : "https://openapi.koreainvestment.com:9443"; var uri = baseUri + "/uapi/domestic-stock/v1/quotations/inquire-price" + $"?FID_COND_MRKT_DIV_CODE={Uri.EscapeDataString(input.MarketDivisionCode)}&FID_INPUT_ISCD={Uri.EscapeDataString(input.StockCode)}"; using var req = new HttpRequestMessage(HttpMethod.Get, uri); req.Headers.TryAddWithoutValidation("authorization", "Bearer " + token); req.Headers.TryAddWithoutValidation("appkey", appKey); req.Headers.TryAddWithoutValidation("appsecret", appSecret); req.Headers.TryAddWithoutValidation("tr_id", "FHKST01010100"); req.Headers.TryAddWithoutValidation("custtype", "P"); req.Headers.TryAddWithoutValidation("tr_cont", ""); using var res = await http.SendAsync(req, ct); var text = await res.Content.ReadAsStringAsync(ct); if (!res.IsSuccessStatusCode) return new((int)res.StatusCode >= 500 || (int)res.StatusCode is 408 or 429 ? KbxProviderResultKind.TransientFailure : KbxProviderResultKind.PermanentFailure, Code:$"HTTP_{(int)res.StatusCode}", Message:"KIS 시세 HTTP 호출 실패", HttpStatus:(int)res.StatusCode); JsonDocument doc; try { doc = JsonDocument.Parse(text); } catch { return new(KbxProviderResultKind.PermanentFailure, Code:"KIS_INVALID_JSON", Message:"KIS 응답 JSON을 해석할 수 없습니다."); } var root=doc.RootElement; var rt=root.TryGetProperty("rt_cd",out var r)?r.GetString():null; if(rt=="0") return new(KbxProviderResultKind.Success,doc); var code=root.TryGetProperty("msg_cd",out var c)?c.GetString():"KIS_PROVIDER_ERROR"; var msg=root.TryGetProperty("msg1",out var m)?m.GetString():"KIS 시세 조회 실패"; return new(KbxProviderResultKind.PermanentFailure,doc,code,msg); } }