Fix Redundance Reference
This commit is contained in:
@@ -12,9 +12,11 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.3.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.9" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.9" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.9" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.9" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
|
||||
<PackageReference Include="StackExchange.Redis" Version="2.9.17" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.14.0" />
|
||||
|
||||
@@ -1,16 +1,24 @@
|
||||
using AMREZ.EOP.Abstractions.Applications.Tenancy;
|
||||
using AMREZ.EOP.Abstractions.Applications.UseCases.Authentications;
|
||||
using AMREZ.EOP.Abstractions.Applications.UseCases.HumanResources;
|
||||
using AMREZ.EOP.Abstractions.Applications.UseCases.Payments.SlipVerification;
|
||||
using AMREZ.EOP.Abstractions.Applications.UseCases.Payments.SlipVerification.QrDecode;
|
||||
using AMREZ.EOP.Abstractions.Applications.UseCases.Tenancy;
|
||||
using AMREZ.EOP.Abstractions.Infrastructures.Common;
|
||||
using AMREZ.EOP.Abstractions.Infrastructures.Integrations.SCB.Clients;
|
||||
using AMREZ.EOP.Abstractions.Infrastructures.Repositories;
|
||||
using AMREZ.EOP.Abstractions.Security;
|
||||
using AMREZ.EOP.Abstractions.Storage;
|
||||
using AMREZ.EOP.Application.UseCases.Authentications;
|
||||
using AMREZ.EOP.Application.UseCases.HumanResources;
|
||||
using AMREZ.EOP.Application.UseCases.Payments.SlipVerification;
|
||||
using AMREZ.EOP.Application.UseCases.Payments.SlipVerification.BankDetect;
|
||||
using AMREZ.EOP.Application.UseCases.Payments.SlipVerification.QrDecode;
|
||||
using AMREZ.EOP.Application.UseCases.Tenancy;
|
||||
using AMREZ.EOP.Domain.Shared.Payments;
|
||||
using AMREZ.EOP.Domain.Shared.Tenancy;
|
||||
using AMREZ.EOP.Infrastructures.Data;
|
||||
using AMREZ.EOP.Infrastructures.Integrations.SCB.Clients;
|
||||
using AMREZ.EOP.Infrastructures.Options;
|
||||
using AMREZ.EOP.Infrastructures.Repositories;
|
||||
using AMREZ.EOP.Infrastructures.Security;
|
||||
@@ -30,7 +38,17 @@ public static class ServiceCollectionExtensions
|
||||
{
|
||||
services.AddHttpContextAccessor();
|
||||
services.AddScoped<IJwtFactory, JwtFactory>();
|
||||
|
||||
|
||||
services.AddSingleton(new SCBOptions());
|
||||
|
||||
services.AddHttpClient<IScbSlipClient, ScbSlipClient>((sp, http) =>
|
||||
{
|
||||
var o = sp.GetRequiredService<SCBOptions>();
|
||||
var baseUrl = o.BaseUrl.EndsWith('/') ? o.BaseUrl[..^1] : o.BaseUrl;
|
||||
http.BaseAddress = new Uri(baseUrl + "/v1/");
|
||||
http.Timeout = TimeSpan.FromSeconds(20);
|
||||
});
|
||||
|
||||
// Options
|
||||
services.AddOptions<AuthOptions>()
|
||||
.BindConfiguration("Connections")
|
||||
@@ -116,6 +134,11 @@ public static class ServiceCollectionExtensions
|
||||
services.AddScoped<IListTenantsUseCase, ListTenantsUseCase>();
|
||||
services.AddScoped<IListDomainsUseCase, ListDomainsUseCase>();
|
||||
|
||||
|
||||
// UseCase & helpers (no OCR)
|
||||
services.AddScoped<IQrDecoder, ZxingQrDecoder>();
|
||||
services.AddScoped<IVerifyFromImageUseCase, VerifyFromImageUseCase>();
|
||||
|
||||
// Redis (optional)
|
||||
services.AddSingleton<IConnectionMultiplexer?>(sp =>
|
||||
{
|
||||
|
||||
@@ -1,6 +1,83 @@
|
||||
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 class ScbSlipClient
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user