EcfDgii.Client is an enterprise-grade solution that wraps and exposes the Dominican Republic Tax Authority's (DGII) Comprobante Fiscal Electrónico (e-CF) SOAP/REST integration services. Refactored under Clean Architecture and Domain-Driven Design (DDD) principles, this solution provides a robust REST API wrapper, secure JWT-based authentication, PostgreSQL persistence with automated auditing and soft-delete, FluentValidation rules, correlation logging, and full Docker orchestration support.
- What's New in Refactored v2.0.0
- Overview
- Key Features
- Solution Structure
- Installation & Setup
- Dependencies
- Basic Configuration
- Security & JWT Authentication
- XML Digital Signature (XMLDSig)
- API Endpoints Reference
- JSON Request & Response Examples
- Database Persistence & Migrations
- Complete Core API Interfaces
- Performance Considerations
- Best Practices
- Complete Workflows
- Docker Orchestration
- Diagnostics & Testing
- License
- Contact
- Support
This release marks a complete architectural migration from a legacy single Class Library SDK into a production-ready enterprise solution built with ASP.NET Core 10, Entity Framework Core, PostgreSQL, and CQRS via MediatR.
| Refactoring Area | Legacy SDK Limitation | v2.0.0 Enterprise Solution |
|---|---|---|
| API Authentication | No API-level protection; endpoints could be called anonymously. | Secure JWT Bearer Token validation using Microsoft.AspNetCore.Authentication.JwtBearer. |
| Password Hashing | No secure mechanism to manage credentials or register users. | BCrypt-based hashing via BCrypt.Net-Next for user login validation. |
| Credential Storage | Certificates, credentials, and endpoints hardcoded in code or plain settings. | Typed configuration options bound automatically via IOptions<EcfClientOptions>. |
| Refactoring Area | Legacy SDK Limitation | v2.0.0 Enterprise Solution |
|---|---|---|
| Layered Structure | Monolithic codebase where controllers, business logic, and SDK services coexisted. | Clean Architecture split into 5 core projects (Domain, Application, Infrastructure, Shared, Api). |
| Dependency Inversion | Application layer and API controllers depended directly on concrete implementations. | Interface-driven architecture using domain abstractions like IEcfClient and IEcfXmlSerializer. |
| Business Flow | Direct client wrapping inside fat controllers and request processors. | CQRS pattern implemented using MediatR handlers for register, login, customer management, and e-CF submittals. |
| Refactoring Area | Legacy SDK Limitation | v2.0.0 Enterprise Solution |
|---|---|---|
| Local Database | e-CF transactions were processed only in-memory, leading to data loss on restarts. | Local PostgreSQL repository tracking every submitted e-CF and customer model. |
| Audit Logs & Soft Delete | Auditable timestamps updated manually; hard deletion of customer data. | Base entity AuditableEntity auto-updates auditing properties in SaveChangesAsync(), implementing soft-delete global filters. |
| Error Handling | Raw custom exceptions thrown directly to clients, leaking implementation details. | Standardized ProblemDetails global middleware matching the RFC 9457 specification. |
Auto-Migrations at Startup: In development, database migrations are automatically executed against the configured PostgreSQL instance.
Mandatory Authentication: All endpoints (except
/api/auth/registerand/api/auth/login) require a valid JWT bearer token.Default Admin Credentials: A default admin user (
admin) is seeded during migrations with a placeholder password. It is not printed here — a plaintext admin password in a public README is exactly how the previous one (AdminPassword123!) ended up effectively public. The current seed password was communicated out of band when it was last rotated (seeUserConfiguration.csand migrationRotateSeededAdminPasswordHash). Change it immediately after first login in any real deployment — this seed exists to bootstrap local development, not as a standing production credential.
The EcfDgii.Client solution acts as a middleware between internal billing platforms and the Dominican Republic Tax Authority (DGII) server systems. It automates XML serialization, digital signing (XMLDSig / XAdES), authentication token acquisition, document transmission, and status querying.
graph TD
Api[src/EcfDgii.Client.Api] --> Application[src/EcfDgii.Client.Application]
Api --> Infrastructure[src/EcfDgii.Client.Infrastructure]
Api --> Shared[src/EcfDgii.Client.Shared]
Infrastructure --> Application
Infrastructure --> Domain[src/EcfDgii.Client.Domain]
Infrastructure --> Shared
Application --> Domain
Application --> Shared
UnitTests[src/EcfDgii.Client.Tests/UnitTests] --> Application
UnitTests --> Domain
UnitTests --> Infrastructure
IntegrationTests[src/EcfDgii.Client.Tests/IntegrationTests] --> Api
- Single e-CF Sending: Prepares, validates, signs, and posts signed XML tax receipts directly to DGII REST services.
- RFCE Summaries: Automatic validation, serialization, signing, and transmission of Consumption Invoice Summaries (RFCE).
- DGII Status Syncing: Polls local and external services to sync transaction statuses (TrackId results) directly to the PostgreSQL database.
- Sequence Collision Recovery: Automatically retries transmitting with a newly acquired sequence number if the DGII responds with a sequence-in-use error.
- JWT Authorization: Protects REST API endpoints with JWT token verification and role policies.
- XMLDSig (RSA-SHA256): Digitally signs invoices using enveloped signature transformations and validates certificate RNC ownership.
- Auditing & Tracking: Automatically registers creation, update, and soft deletion dates/users for all tables.
- Serilog Logging: Complete request/response correlation logging, stack trace capture, and rolling file writes.
- OpenTelemetry Instrumentation: Distributed tracing and metrics for ASP.NET Core API and EF Core PostgreSQL database operations.
- Scalar OpenAPI Interface: Next-gen documentation and sandbox console exposed natively in development environments.
src/
├── EcfDgii.Client.Domain/ # Enterprise core, entities, value objects, exceptions, and abstractions
│ ├── Common/ # AuditableEntity base model
│ ├── Entities/ # User, Customer, EcfDocument, Rfce schemas
│ ├── Interfaces/ # Abstractions (IEcfClient, IEcfXmlSerializer, Repositories)
│ └── Exceptions/ # Domain specific exceptions (EcfSigningException, EcfValidationException)
├── EcfDgii.Client.Application/ # Application use cases, MediatR handlers, validation rules
│ ├── Common/ # Logging and Validation pipeline behaviors
│ ├── Customers/ # Customer CRUD handlers
│ ├── Ecf/ # SendEcf, SendRfce, and GetStatus handlers
│ └── Auth/ # Authentication use cases
├── EcfDgii.Client.Infrastructure/ # Concrete implementations, database contexts, soap clients
│ ├── Persistence/ # ApplicationDbContext, repository configurations, migrations
│ ├── Security/ # PasswordHasher, TokenService, XML Signer
│ ├── Serialization/ # XML serializer helpers
│ └── Dgii/ # Direct transport REST client and token managers
├── EcfDgii.Client.Shared/ # Shared libraries (Result wrapper pattern)
├── EcfDgii.Client.Api/ # ASP.NET Core host, controllers, middleware, and services
└── EcfDgii.Client.Tests/ # Solution tests directory
├── UnitTests/ # Unit tests project
└── IntegrationTests/# Integration tests project
- Clone the repository:
git clone https://github.com/JorgeGBeltre/EcfDgi.Client.git cd EcfDgi.Client - Build the solution:
dotnet build EcfDgii.Client.slnx
- Start the API project:
dotnet run --project src/EcfDgii.Client.Api/EcfDgii.Client.Api.csproj
- Run the entire database and API stack:
docker compose up --build -d
- Verify execution using the docker logs:
docker logs ecf_dgii_api -f
<!-- Core Database & EF Core -->
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.9" />
<!-- Core Request Handling & CQRS -->
<PackageReference Include="MediatR" Version="12.4.1" />
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="11.11.0" />
<!-- Security, Auth & Cryptography -->
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.9" />
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
<PackageReference Include="System.Security.Cryptography.Xml" Version="10.0.0-preview.2.25163.2" />
<!-- Logging & API Documentation -->
<PackageReference Include="Serilog.AspNetCore" Version="10.0.0" />
<PackageReference Include="Scalar.AspNetCore" Version="2.16.6" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.16.0" />The DI lifecycle is configured in two clean extension methods: AddApplicationServices() and AddInfrastructureServices(IConfiguration).
// Program.cs
builder.Services.AddApplicationServices();
builder.Services.AddInfrastructureServices(builder.Configuration);Configure your databases, credentials, and signing certificates in src/Api/appsettings.json:
{
"ConnectionStrings": {
"DefaultConnection": "Host=localhost;Port=5432;Database=ecf_dgii;Username=postgres;Password=postgres"
},
"JwtSettings": {
"Secret": "e_CF_Dominican_Tax_Authority_Secure_JWT_Secret_Token_2026_Key_Length_Minimum_32_Bytes!",
"ExpirationMinutes": 60,
"Issuer": "EcfDgiiClientIssuer",
"Audience": "EcfDgiiClientAudience"
},
"EcfClientOptions": {
"ApiKey": "",
"BaseUrl": "https://ecf.dgii.gov.do",
"Environment": "Test",
"Mode": "DgiiDirect",
"RncEmisor": "101672919",
"CertificatePath": "C:/config/credentials/dgii_certificate.p12",
"CertificatePassword": "SecurePassword123",
"AutoRetryOnReuseableSequence": true
}
}Authentication options are read directly into JwtSettings and configured with symmetric security validation keys:
var jwtSettingsSection = builder.Configuration.GetSection("JwtSettings");
var jwtSettings = jwtSettingsSection.Get<JwtSettings>();
var key = Encoding.ASCII.GetBytes(jwtSettings.Secret);
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.RequireHttpsMetadata = false;
options.SaveToken = true;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = true,
ValidIssuer = jwtSettings.Issuer,
ValidateAudience = true,
ValidAudience = jwtSettings.Audience,
ClockSkew = TimeSpan.Zero
};
});The cryptographic signature of XML receipts is handled by the EcfXmlSigner service. It extracts the private key from the client certificate, validates that the certificate matches the sender's RNC, computes an RSA-SHA256 digest, and appends the <Signature> block.
// EcfXmlSigner.cs
public string SignXml(string xmlContent, string rncEmisor)
{
if (!ValidateCertificateSn(rncEmisor))
throw new EcfSigningException($"El RNC del certificado no coincide con el emisor: {rncEmisor}");
var doc = new XmlDocument { PreserveWhitespace = false };
doc.LoadXml(xmlContent);
var signedXml = new SignedXml(doc);
signedXml.SigningKey = _certificate.GetRSAPrivateKey();
signedXml.SignedInfo.SignatureMethod = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";
// Inclusive Canonicalization Method required by DGII
signedXml.SignedInfo.CanonicalizationMethod = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315";
var reference = new Reference { Uri = "" };
reference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
reference.DigestMethod = "http://www.w3.org/2001/04/xmlenc#sha256";
signedXml.AddReference(reference);
var keyInfo = new KeyInfo();
keyInfo.AddClause(new KeyInfoX509Data(_certificate));
signedXml.KeyInfo = keyInfo;
signedXml.ComputeSignature();
var xmlDigitalSignature = signedXml.GetXml();
doc.DocumentElement?.AppendChild(doc.ImportNode(xmlDigitalSignature, true));
return doc.OuterXml;
}The 6-character security code is computed by taking the SHA-256 hash of the SignatureValue inside the signed XML document:
// EcfSecurityUtils.cs
public static string CalcularCodigoSeguridad(string signedXml)
{
var doc = new XmlDocument();
doc.LoadXml(signedXml);
var ns = new XmlNamespaceManager(doc.NameTable);
ns.AddNamespace("ds", "http://www.w3.org/2000/09/xmldsig#");
var node = doc.SelectSingleNode("//ds:SignatureValue", ns);
if (node == null)
throw new InvalidOperationException("XML does not contain a signature value.");
var signatureText = node.InnerText.Trim();
var hashBytes = SHA256.HashData(Encoding.UTF8.GetBytes(signatureText));
return Convert.ToBase64String(hashBytes).Substring(0, 6).ToUpperInvariant();
}All endpoints except Auth require a valid JWT Bearer header: Authorization: Bearer <your-token>.
| Route | Method | Authentication | Request Body | Description |
|---|---|---|---|---|
/api/auth/register |
POST |
Anonymous | RegisterUserCommand |
Creates a new user |
/api/auth/login |
POST |
Anonymous | LoginUserCommand |
Verifies user password and yields JWT token |
/api/customers |
GET |
Bearer Token | None | Returns a list of active customers |
/api/customers/{id} |
GET |
Bearer Token | None | Retrieves a customer by ID |
/api/customers |
POST |
Bearer Token | CreateCustomerCommand |
Creates a new customer record |
/api/customers/{id} |
PUT |
Bearer Token | UpdateCustomerCommand |
Updates an existing customer record |
/api/customers/{id} |
DELETE |
Admin Role | None | Soft-deletes a customer |
/api/ecf/send |
POST |
Bearer Token | SendEcfCommand |
Signs and sends an XML e-CF document |
/api/ecf/send-rfce |
POST |
Bearer Token | SendRfceCommand |
Signs and sends a Consumption Summary |
/api/ecf/status |
GET |
Bearer Token | Query Parameters | Queries current processing status |
/api/documents |
POST |
Bearer Token | CanonicalDocumentDto |
ERP integration: compiles raw JSON into compliant XML, signs it, and sends it to DGII |
/api/documents/by-source/{txnId} |
GET |
Bearer Token | None | ERP integration: queries document status by original TxnId |
/fe/recepcion/api/ecf |
POST |
Anonymous | Multipart Form (xml file) |
Electronic Receiver: receives e-CF from peers and yields signed Acuse de Recibo (ARECF) |
/fe/aprobacioncomercial/api/ecf |
POST |
Anonymous | Multipart Form (xml file) |
Electronic Receiver: receives commercial approval (ACECF) and returns HTTP 200 |
/fe/autenticacion/api/semilla |
GET |
Anonymous | None | Peer Auth: yields a new seed XML (SemillaModel) |
/fe/autenticacion/api/validacioncertificado |
POST |
Anonymous | Multipart Form (xml seed signature) |
Peer Auth: validates seed signature and returns a session token |
Request Payload:
{
"username": "jorge_admin",
"email": "jorge@domain.com",
"password": "SecurePassword123!",
"role": "Admin"
}Response Payload (200 OK):
{
"username": "jorge_admin",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"role": "Admin"
}Request Payload:
{
"username": "jorge_admin",
"password": "SecurePassword123!"
}Response Payload (200 OK):
{
"username": "jorge_admin",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"role": "Admin"
}Request Payload:
{
"xmlContent": "<eCF xmlns=\"http://dgii.gov.do/eCF\">...</eCF>",
"fileName": "101672919E3100000001.xml",
"rncEmisor": "101672919",
"eNcf": "E310000000001",
"rncComprador": "22400013743",
"totalAmount": 1180.00,
"itbisAmount": 180.00
}Response Payload (200 OK):
{
"trackId": "d748f219-c0ad-4d43-9878-837cc21087ab",
"error": null,
"mensaje": "e-CF recibido exitosamente"
}Database configurations map columns to database snake_case properties. The ApplicationDbContext intercepts entities derived from AuditableEntity to execute auditing writes and soft deletion.
// CustomerConfiguration.cs
public void Configure(EntityTypeBuilder<Customer> builder)
{
builder.ToTable("customers");
builder.HasKey(c => c.Id).HasName("pk_customers");
builder.Property(c => c.Id).HasColumnName("id");
builder.Property(c => c.Name).HasColumnName("name").HasMaxLength(150).IsRequired();
builder.Property(c => c.Email).HasColumnName("email").HasMaxLength(150).IsRequired();
builder.Property(c => c.Rnc).HasColumnName("rnc").HasMaxLength(20).IsRequired();
// Auditable columns mapping
builder.Property(c => c.IsDeleted).HasColumnName("is_deleted").IsRequired();
builder.Property(c => c.CreatedAt).HasColumnName("created_at").IsRequired();
builder.Property(c => c.CreatedBy).HasColumnName("created_by").HasMaxLength(100);
}Run these commands from the root directory:
# Add new database migration
dotnet ef migrations add InitialCreate --project src/EcfDgii.Client.Infrastructure --startup-project src/EcfDgii.Client.Api --output-dir Persistence/Migrations
# Apply migration changes directly to PostgreSQL
dotnet ef database update --project src/EcfDgii.Client.Infrastructure --startup-project src/EcfDgii.Client.Api
# Generate idempotent SQL script for Production pipelines
dotnet ef migrations script --idempotent --output script.sql --project src/EcfDgii.Client.Infrastructure --startup-project src/EcfDgii.Client.ApiThese abstractions separate use cases in the Application layer from concrete implementations in the Infrastructure layer.
// IEcfClient.cs
namespace EcfDgii.Client.Domain.Interfaces
{
public interface IEcfClient
{
Task<EcfRecepcionResponse> SendEcfAsync(string xmlContent, string fileName, CancellationToken ct = default);
Task<RfceRecepcionResponse> SendRfceAsync(Rfce rfce, CancellationToken ct = default);
Task<ConsultaResultadoResponse> ConsultarResultadoAsync(string trackId, CancellationToken ct = default);
Task<ConsultaEstadoResponse> ConsultarEstadoAsync(string rncEmisor, string eNcf, string? rncComprador = null, string? codigoSeguridad = null, CancellationToken ct = default);
Task<List<TrackIdDetalle>> ConsultarTrackIdsAsync(string rncEmisor, string eNcf, CancellationToken ct = default);
Task<RfceConsultaResponse> ConsultarRfceAsync(string rncEmisor, string eNcf, string codigoSeguridad, CancellationToken ct = default);
Task<TimbreResponse> ValidarTimbreEcfAsync(TimbreEcfRequest request, CancellationToken ct = default);
Task<TimbreFcResponse> ValidarTimbreFcAsync(TimbreFcRequest request, CancellationToken ct = default);
Task<List<DirectorioContribuyente>> ConsultarDirectorioAsync(CancellationToken ct = default);
Task<List<EstatusServicio>> ConsultarEstatusServiciosAsync(CancellationToken ct = default);
Task<List<VentanaMantenimiento>> ConsultarVentanasMantenimientoAsync(CancellationToken ct = default);
Task<string> VerificarEstadoAmbienteAsync(AmbienteEnum ambiente, CancellationToken ct = default);
Task<AnulacionResponse> AnularRangosAsync(string xmlContent, CancellationToken ct = default);
}
}
// IEcfXmlSerializer.cs
namespace EcfDgii.Client.Domain.Interfaces
{
public interface IEcfXmlSerializer
{
string Serialize<T>(T model) where T : class;
T Deserialize<T>(string xml) where T : class;
string GetFileName(string rncEmisor, string eNcf);
string EscapeAlfanum(string value);
}
}
// IEcfSequenceProvider.cs
namespace EcfDgii.Client.Domain.Interfaces
{
public interface IEcfSequenceProvider
{
Task<string> GetNextAsync(string rncEmisor, CancellationToken ct = default);
Task ReleaseAsync(string rncEmisor, string eNcf, CancellationToken ct = default);
}
}- HTTP Client Connection Pooling:
HttpClientis registered once and shared using dependency injection to prevent socket exhaustion. - EF Core AsNoTracking: Queries that only return read data use
AsNoTrackingto skip memory allocation for changes. - XML Serializer Reuse: Standardizes XML parser instantiations to avoid generating assembly files dynamically on each serializing request.
- Use HTTPS and TLS 1.3: Ensure that connections to the API and to DGII endpoints are strictly encrypted using TLS 1.3/1.2 protocols.
- Store P12/PFX Certificates Safely: Avoid placing your digital certificate file inside any public folders. Rely on secure environment configurations or cloud storage (AWS Secrets Manager/Azure KeyVault).
- Always Register the Pipeline Middleware: The
GlobalExceptionMiddlewareensures validation errors are formatted cleanly into RFC-compliant responses, preventing detailed exceptions from exposing system layers.
Client App EcfDgii.Client API DGII Gateway
│ │ │
│── POST /api/ecf/send ──────────►│ │
│ (JWT authentication check) │── 1. Sign XML (XMLDSig) │
│ │── 2. Authenticate token │
│ │── 3. Post payload ─────────►│
│ │◄── 4. Return TrackId ───────│
│ │ │
│ │── 5. Save to local Database │
│◄── Return TrackId ──────────────│ │
Application Handler EcfClient Service DGII Gateway
│ │ │
│── SendRfceAsync ───────────────►│ │
│ │── Send to DGII ────────────►│
│ │◄── Rejected (Sequence Used)─│
│ │ │
│ │── 1. Get next sequence │
│ │── 2. Re-sign XML payload │
│ │── 3. Resend payload ───────►│
│ │◄── Accepted (Success) ──────│
│◄── Return Success ──────────────│ │
graph TD
Client[Client / API Request] --> API[EcfDgii.Client.Api]
API --> CacheService[ICacheService / DistributedCache]
CacheService --> Redis[(Redis Cache)]
subgraph Use Cases
UC1[DGII Token Cache per RNC]
UC2[Taxpayer Directory & DGII Status]
UC3[Distributed Locking / e-CF Idempotency]
UC4[Query Response Cache]
end
CacheService --> UC1
CacheService --> UC2
CacheService --> UC3
CacheService --> UC4
UC1 -. Miss .-> DGII[DGII Web Services]
UC2 -. Miss .-> DGII
Important
Fallback Strategy (High Availability):
A resilience strategy is implemented where, if Redis is unavailable or temporarily fails, the system will gracefully degrade using IMemoryCache as a local in-memory fallback without interrupting the operation of the DGII client.
Note
Orchestration with Docker Compose:
The official redis:7-alpine image is included with optional persistence (RDB/AOF) and memory limit configuration (maxmemory 256mb, policy allkeys-lru).
The API stack uses Docker Compose, linking the REST wrapper API container, a PostgreSQL database, and a Redis cache server.
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS base
WORKDIR /app
EXPOSE 8080
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
COPY . .
RUN dotnet restore "./src/EcfDgii.Client.Api/EcfDgii.Client.Api.csproj"
WORKDIR "/src/src/EcfDgii.Client.Api"
RUN dotnet publish "./EcfDgii.Client.Api.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "Api.dll"]version: '3.8'
services:
postgres:
image: postgres:15-alpine
container_name: ecf_dgii_postgres
environment:
POSTGRES_DB: ecf_dgii
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres -d ecf_dgii"]
interval: 5s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
container_name: ecf_dgii_redis
ports:
- "6379:6379"
volumes:
- redis_data:/data
command: redis-server --save 60 1 --loglevel notice
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 5s
retries: 5
api:
image: ecf_dgii_api
build:
context: .
dockerfile: Dockerfile
container_name: ecf_dgii_api
ports:
- "8080:8080"
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ConnectionStrings__DefaultConnection=Host=postgres;Port=5432;Database=ecf_dgii;Username=postgres;Password=postgres
- ConnectionStrings__Redis=redis:6379
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
volumes:
postgres_data:
redis_data:Execute the xUnit test runner from the root folder:
# Run unit & integration tests
dotnet test EcfDgii.Client.slnxCheck API status by requesting the /health endpoint:
Example Request:
curl http://localhost:8080/healthExample Response:
{
"status": "Healthy"
}OpenApi documentation is compiled and rendered on Development runs under:
http://localhost:8080/scalar/v1
Licensed under the MIT License. See LICENSE for details.
Author: Jorge Gaspar Beltre Rivera
Project: EcfDgii.Client API & SDK
This project is developed independently. Even a small contribution helps me dedicate more time to development, testing, and releasing new features.


