using System.Text.Json; using System.Net.Http.Headers; using Back.Program.Services.V1.Interfaces; namespace Back.Program.Services.V1; public class KakaoService: IKakaoService { private readonly HttpClient _httpClient; private const string KAKAO_API_BASE_URL = "https://kapi.kakao.com"; private const string KAKAO_AUTH_BASE_URL = "https://kauth.kakao.com"; private readonly string _clientId; private readonly string _clientSecret; private readonly string _redirectUri; public KakaoService(HttpClient httpClient, IConfiguration configuration) { _httpClient = httpClient; _clientId = configuration["Kakao:ClientId"] ?? throw new InvalidOperationException("Kakao:ClientId not configured"); _redirectUri = configuration["Kakao:RedirectUri"] ?? throw new InvalidOperationException("Kakao:RedirectUri not configured"); _clientSecret = configuration["Kakao:ClientSecret"] ?? throw new InvalidOperationException("Kakao:ClientSecret not configured"); } private void SetHeaders(string accessToken) { _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); } private async Task Call(HttpMethod method, string url, HttpContent? content = null) { try { var request = new HttpRequestMessage(method, url); if (content != null) { request.Content = content; } var response = await _httpClient.SendAsync(request); var responseContent = await response.Content.ReadAsStringAsync(); if (!response.IsSuccessStatusCode) { return JsonSerializer.Serialize(new { error = $"HTTP {(int)response.StatusCode}: {responseContent}" }); } return responseContent; } catch (Exception ex) { return JsonSerializer.Serialize(new { error = ex.Message }); } } /// /// 인가 받은 코드로 토큰 받기를 실행하고 이 토큰으로 사용자 정보 가져오기를 할 수 있다. /// /// /// public async Task GetAccessToken(string code) { var content = new FormUrlEncodedContent(new[] { new KeyValuePair("grant_type", "authorization_code"), new KeyValuePair("client_id", _clientId), new KeyValuePair("redirect_uri", _redirectUri), new KeyValuePair("code", code), new KeyValuePair("client_secret", _clientSecret) }); content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded"); var response = await Call(HttpMethod.Post, $"{KAKAO_AUTH_BASE_URL}/oauth/token", content); var responseData = JsonSerializer.Deserialize(response); if (responseData.TryGetProperty("error", out var error)) { return response; } if (!responseData.TryGetProperty("access_token", out var accessToken)) { return JsonSerializer.Serialize(new { error = "Access token is missing from response" }); } return JsonSerializer.Serialize(new { access_token = accessToken.GetString() }); } /// /// 인가코드를 받는다 이 인가코드를 사용해 토큰 받기를 요청 할 수 있다. /// 이게 리다이렉트에 포함되어있음 /// /// /// public Task GetAuthorizationUrl(string scope) { var authUrl = $"{KAKAO_AUTH_BASE_URL}/oauth/authorize?client_id={_clientId}&redirect_uri={_redirectUri}&response_type=code"; if (!string.IsNullOrEmpty(scope)) { authUrl += $"&scope={scope}"; } return Task.FromResult(authUrl); } public async Task<(bool Success, string Response)> Redirect(string code) { if (string.IsNullOrEmpty(code)) return (false, JsonSerializer.Serialize(new { error = "Authorization code not found" })); var response = await GetAccessToken(code); var responseData = JsonSerializer.Deserialize(response); if (responseData.TryGetProperty("error", out var error)) { return (false, response); } var accessToken = responseData.GetProperty("access_token").GetString(); if (string.IsNullOrEmpty(accessToken)) { return (false, response); } return (true, accessToken); } public async Task<(bool Success, string Response)> Logout(string accessToken) { if (string.IsNullOrEmpty(accessToken)) return (false, JsonSerializer.Serialize(new { error = "Not logged in" })); SetHeaders(accessToken); var response = await Call(HttpMethod.Post, $"{KAKAO_API_BASE_URL}/v1/user/logout"); return (true, response); } public async Task<(bool Success, string Response)> Unlink(string accessToken) { if (string.IsNullOrEmpty(accessToken)) return (false, JsonSerializer.Serialize(new { error = "Not logged in" })); SetHeaders(accessToken); var response = await Call(HttpMethod.Post, $"{KAKAO_API_BASE_URL}/v1/user/unlink"); return (true, response); } public async Task<(bool Success, string Response)> UserMe(string accessToken) { if (string.IsNullOrEmpty(accessToken)) return (false, JsonSerializer.Serialize(new { error = "Not logged in" })); SetHeaders(accessToken); var response = await Call(HttpMethod.Get, $"{KAKAO_API_BASE_URL}/v2/user/me"); return (true, response); } }