Files
amrez-nova-eop-services-api/AMREZ.EOP.Infrastructures/Integrations/SCB/Clients/ScbSlipClient.cs
Thanakarn Klangkasame d266463c9f Fix Redundance Reference
2025-10-03 10:33:55 +07:00

83 lines
3.4 KiB
C#

using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using AMREZ.EOP.Abstractions.Infrastructures.Integrations.SCB.Clients;
using AMREZ.EOP.Contracts.DTOs.Integrations.SCB.OAuth;
using AMREZ.EOP.Contracts.DTOs.Integrations.SCB.SlipVerification;
using AMREZ.EOP.Domain.Shared.Payments;
namespace AMREZ.EOP.Infrastructures.Integrations.SCB.Clients;
public sealed class ScbSlipClient : IScbSlipClient
{
private readonly HttpClient _http;
private readonly SCBOptions _opts;
private readonly JsonSerializerOptions _json = new()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull
};
private string? _token;
private DateTimeOffset _exp;
private readonly SemaphoreSlim _gate = new(1,1);
public ScbSlipClient(HttpClient http, SCBOptions opts)
{
_http = http;
_opts = opts;
}
public async Task<SlipVerificationEnvelope?> VerifyAsync(string transRef, string sendingBank, CancellationToken ct)
{
var bearer = await GetTokenAsync(ct);
var path = $"payment/billpayment/transactions/{Uri.EscapeDataString(transRef)}?sendingBank={Uri.EscapeDataString(sendingBank)}";
using var req = new HttpRequestMessage(HttpMethod.Get, path);
AddHeaders(req.Headers, bearer);
using var res = await _http.SendAsync(req, ct);
var body = await res.Content.ReadAsStringAsync(ct);
if (!res.IsSuccessStatusCode) return null;
return JsonSerializer.Deserialize<SlipVerificationEnvelope>(body, _json);
}
private async Task<string> GetTokenAsync(CancellationToken ct)
{
if (!string.IsNullOrEmpty(_token) && _exp > DateTimeOffset.UtcNow.AddSeconds(30)) return _token!;
await _gate.WaitAsync(ct);
try
{
if (!string.IsNullOrEmpty(_token) && _exp > DateTimeOffset.UtcNow.AddSeconds(30)) return _token!;
var json = JsonSerializer.Serialize(new OAuthRequest(_opts.SCBApplicationKey, _opts.SCBApplicationSecret), _json);
using var req = new HttpRequestMessage(HttpMethod.Post, "oauth/token")
{ Content = new StringContent(json, Encoding.UTF8, "application/json") };
AddHeaders(req.Headers, null);
using var res = await _http.SendAsync(req, ct);
var body = await res.Content.ReadAsStringAsync(ct);
res.EnsureSuccessStatusCode();
var parsed = JsonSerializer.Deserialize<OAuthResponse>(body, _json)
?? throw new UnauthorizedAccessException("OAuth parse failed");
if (parsed.Status?.Code != 1000) throw new UnauthorizedAccessException(parsed.Status?.Description ?? "OAuth not OK");
_token = parsed.Data?.AccessToken ?? throw new UnauthorizedAccessException("No token");
var ttl = parsed.Data?.ExpiresIn ?? 300;
_exp = DateTimeOffset.UtcNow.AddSeconds(ttl);
return _token!;
}
finally { _gate.Release(); }
}
private void AddHeaders(HttpRequestHeaders h, string? bearer)
{
h.TryAddWithoutValidation("accept-language", _opts.DefaultLanguage);
h.TryAddWithoutValidation("requestUId", Guid.NewGuid().ToString());
h.TryAddWithoutValidation("resourceOwnerId", _opts.SCBResourceOwnerId);
if (!string.IsNullOrEmpty(bearer))
h.Authorization = new AuthenticationHeaderValue("Bearer", bearer);
}
}