using System.Text; using Microsoft.AspNetCore.Http; using Microsoft.Net.Http.Headers; using Newtonsoft.Json; using Nop.Core; using Nop.Plugin.Misc.Zettle.Domain.Api; using Nop.Plugin.Misc.Zettle.Domain.Api.OAuth; namespace Nop.Plugin.Misc.Zettle.Services; /// /// Represents HTTP client to request third-party services /// public class ZettleHttpClient { #region Fields protected readonly HttpClient _httpClient; protected readonly ZettleSettings _zettleSettings; protected string _accessToken; #endregion #region Ctor public ZettleHttpClient(HttpClient httpClient, ZettleSettings zettleSettings) { httpClient.Timeout = TimeSpan.FromSeconds(zettleSettings.RequestTimeout ?? ZettleDefaults.RequestTimeout); httpClient.DefaultRequestHeaders.Add(HeaderNames.UserAgent, ZettleDefaults.UserAgent); httpClient.DefaultRequestHeaders.Add(HeaderNames.Accept, MimeTypes.ApplicationJson); httpClient.DefaultRequestHeaders.Add(ZettleDefaults.PartnerHeader.Name, ZettleDefaults.PartnerHeader.Value); _httpClient = httpClient; _zettleSettings = zettleSettings; } #endregion #region Utilities /// /// Get access token /// /// The asynchronous task whose result contains access token protected async Task GetAccessTokenAsync() { if (!string.IsNullOrEmpty(_accessToken)) return _accessToken; if (string.IsNullOrEmpty(_zettleSettings.ApiKey)) throw new NopException("API key is not set"); _accessToken = (await RequestAsync(new() { Assertion = _zettleSettings.ApiKey, ClientId = ZettleDefaults.PartnerHeader.Value, GrantType = "urn:ietf:params:oauth:grant-type:jwt-bearer" }))?.AccessToken; return _accessToken; } #endregion #region Methods /// /// Request services /// /// Request type /// Response type /// Request /// The asynchronous task whose result contains response details public async Task RequestAsync(TRequest request) where TRequest : IApiRequest where TResponse : IApiResponse { //prepare request parameters var requestString = JsonConvert.SerializeObject(request); var requestContent = request is not GetAuthenticationRequest authentication ? (ByteArrayContent)new StringContent(requestString, Encoding.UTF8, MimeTypes.ApplicationJson) : new FormUrlEncodedContent(new Dictionary { ["assertion"] = authentication.Assertion, ["client_id"] = authentication.ClientId, ["grant_type"] = authentication.GrantType }); var requestMessage = new HttpRequestMessage(new HttpMethod(request.Method), new Uri(new Uri(request.BaseUrl), request.Path)) { Content = requestContent }; //add authorization if (request is IAuthorizedRequest) { var accessToken = await GetAccessTokenAsync(); requestMessage.Headers.Add(HeaderNames.Authorization, $"Bearer {accessToken}"); } //add ETag if (request is IConditionalRequest conditionalRequest) { var header = request.Method == HttpMethods.Get ? HeaderNames.IfNoneMatch : HeaderNames.IfMatch; requestMessage.Headers.Add(header, conditionalRequest.ETag); } //execute request and get result var httpResponse = await _httpClient.SendAsync(requestMessage); var responseString = await httpResponse.Content.ReadAsStringAsync(); var result = JsonConvert.DeserializeObject(responseString ?? string.Empty); if (!string.IsNullOrEmpty(result?.Error)) { var error = !string.IsNullOrEmpty(result.ErrorDescription) ? result.ErrorDescription : result.Error; throw new NopException($"Request error: {error}"); } if (!string.IsNullOrEmpty(result?.DeveloperMessage)) throw new NopException($"Request error: {result.DeveloperMessage}{Environment.NewLine}Details: {responseString}"); return result; } #endregion }