Afip.Dotnet.DependencyInjection 0.1.0-prerelease

This is a prerelease version of Afip.Dotnet.DependencyInjection.
dotnet add package Afip.Dotnet.DependencyInjection --version 0.1.0-prerelease
                    
NuGet\Install-Package Afip.Dotnet.DependencyInjection -Version 0.1.0-prerelease
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Afip.Dotnet.DependencyInjection" Version="0.1.0-prerelease" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Afip.Dotnet.DependencyInjection" Version="0.1.0-prerelease" />
                    
Directory.Packages.props
<PackageReference Include="Afip.Dotnet.DependencyInjection" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Afip.Dotnet.DependencyInjection --version 0.1.0-prerelease
                    
#r "nuget: Afip.Dotnet.DependencyInjection, 0.1.0-prerelease"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Afip.Dotnet.DependencyInjection@0.1.0-prerelease
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Afip.Dotnet.DependencyInjection&version=0.1.0-prerelease&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Afip.Dotnet.DependencyInjection&version=0.1.0-prerelease&prerelease
                    
Install as a Cake Tool

AFIP .NET SDK

CI/CD Pipeline NuGet Version NuGet Downloads Coverage Status License: MIT

A comprehensive and modern .NET SDK for integrating with AFIP (now ARCA - Agencia de RecaudaciΓ³n y Control Aduanero) web services. This SDK provides a clean, async/await-based API for electronic invoicing, authentication, and parameter management.

πŸš€ Features

  • πŸ” WSAA Authentication: Automatic ticket management with certificate-based authentication
  • 🧾 Electronic Invoicing (WSFEv1): Complete support for invoice authorization and management
  • πŸ’° Foreign Currency Support: Full compliance with RG 5616/2024 (FEv4)
  • πŸ“Š Parameter Tables: Dynamic access to AFIP parameter tables
  • ⚑ Async/Await: Modern asynchronous programming patterns
  • πŸ›‘οΈ Type Safety: Strongly-typed models for all AFIP operations
  • πŸ“ Comprehensive Logging: Built-in logging support with Microsoft.Extensions.Logging
  • πŸ§ͺ Testing Support: Separate testing and production environments
  • πŸ“¦ NuGet Packages: Easy installation via NuGet
  • πŸ”§ Dependency Injection: Separate DI package for clean architecture

Supported AFIP Services

Service Description Status
WSAA Web Service Authentication and Authorization βœ… Complete
WSFEv1 Electronic Invoicing for domestic market βœ… Complete
WSFEX Electronic Invoicing for exports βœ… Complete
WSMTXCA Electronic Invoicing with item details βœ… Complete

Supported Invoice Types

  • Invoice A, B, C - Standard domestic invoices
  • FCE MiPyMEs - Credit invoices for small and medium enterprises (RG 4367/2018)
  • Foreign Currency Invoices - USD and other currencies (RG 5616/2024)
  • Credit and Debit Notes - With invoice associations
  • Export Invoices (E) - For international transactions

Regulatory Compliance

This SDK implements and supports the following AFIP regulations:

  • RG 2485, RG 2904, RG 3067 - Base electronic invoicing regulations
  • RG 3668/2014 - Special regimes for restaurants, hotels, and bars
  • RG 3749/2015 - VAT responsible parties and exempt entities
  • RG 4367/2018 - FCE MiPyMEs credit invoices
  • RG 5616/2024 - Foreign currency operations (FEv4)

πŸ“¦ Installation

Core Package

dotnet add package Afip.Dotnet

Dependency Injection Package (Optional)

dotnet add package Afip.Dotnet.DependencyInjection

PackageReference

<PackageReference Include="Afip.Dotnet" Version="1.0.0" />
<PackageReference Include="Afip.Dotnet.DependencyInjection" Version="1.0.0" />

βš™οΈ Configuration

1. Certificate Setup

First, you need an X.509 certificate from AFIP in PKCS#12 format (.p12 or .pfx):

# Generate private key
openssl genrsa -out private.key 2048

# Generate certificate request
openssl req -new -key private.key -out certificate.csr -subj "/C=AR/O=YourCompany/CN=YourName/serialNumber=CUIT YourCuit"

# After AFIP approves, convert to PKCS#12
openssl pkcs12 -export -out certificate.p12 -inkey private.key -in certificate.crt

2. Basic Configuration

using Afip.Dotnet;
using Afip.Dotnet.Abstractions.Models;

var configuration = new AfipConfiguration
{
    Environment = AfipEnvironment.Testing, // or AfipEnvironment.Production
    Cuit = 20123456789,
    CertificatePath = "path/to/your/certificate.p12",
    CertificatePassword = "your-certificate-password",
    TimeoutSeconds = 30
};

using var client = AfipClient.Create(configuration);

3. Factory Methods

For quick setup:

// Testing environment
using var testingClient = AfipClient.CreateForTesting(
    cuit: 20123456789,
    certificatePath: "cert.p12",
    certificatePassword: "password"
);

// Production environment
using var productionClient = AfipClient.CreateForProduction(
    cuit: 20123456789,
    certificatePath: "cert.p12",
    certificatePassword: "password"
);

πŸ”§ Usage Examples

Basic Service Status Check

// Check if the service is available
var status = await client.ElectronicInvoicing.CheckServiceStatusAsync();
Console.WriteLine($"Service Status: {status.AppServer} - {status.DbServer} - {status.AuthServer}");

Get Next Invoice Number

// Get the next available invoice number
var lastNumber = await client.ElectronicInvoicing.GetLastInvoiceNumberAsync(
    pointOfSale: 1,
    invoiceType: 11 // Invoice C
);
Console.WriteLine($"Next invoice number: {lastNumber + 1}");

Authorize a Simple Invoice

var invoiceRequest = new InvoiceRequest
{
    PointOfSale = 1,
    InvoiceType = 11, // Invoice C
    Concept = 1, // Products
    DocumentType = 96, // DNI
    DocumentNumber = 12345678,
    InvoiceNumberFrom = 1,
    InvoiceNumberTo = 1,
    InvoiceDate = DateTime.Today,
    TotalAmount = 121.00m,
    NetAmount = 100.00m,
    VatAmount = 21.00m,
    VatDetails = new List<VatDetail>
    {
        new VatDetail
        {
            VatRateId = 5, // 21%
            BaseAmount = 100.00m,
            VatAmount = 21.00m
        }
    }
};

var response = await client.ElectronicInvoicing.AuthorizeInvoiceAsync(invoiceRequest);
Console.WriteLine($"CAE: {response.Cae}, Expiration: {response.CaeExpirationDate}");

Foreign Currency Invoice (USD)

var usdInvoice = new InvoiceRequest
{
    PointOfSale = 1,
    InvoiceType = 19, // Invoice E (Export)
    Concept = 1,
    DocumentType = 80, // CUIT
    DocumentNumber = 20987654321,
    InvoiceNumberFrom = 1,
    InvoiceNumberTo = 1,
    InvoiceDate = DateTime.Today,
    CurrencyId = "DOL", // USD
    CurrencyRate = 350.50m, // Exchange rate
    TotalAmount = 1000.00m,
    NetAmount = 1000.00m,
    PayInSameForeignCurrency = true,
    ReceiverVatCondition = 4 // Foreign consumer
};

var response = await client.ElectronicInvoicing.AuthorizeInvoiceAsync(usdInvoice);

Export Invoice (WSFEX)

var exportInvoice = new ExportInvoiceRequest
{
    PointOfSale = 1,
    InvoiceType = 19, // Invoice E (Export)
    InvoiceNumberFrom = 1,
    InvoiceNumberTo = 1,
    InvoiceDate = DateTime.Today,
    DocumentType = 80, // CUIT
    DocumentNumber = 20987654321,
    ReceiverName = "International Buyer Corp",
    ReceiverAddress = "123 Main St",
    ReceiverCity = "New York",
    ReceiverCountryCode = "US",
    CurrencyId = "DOL", // USD
    CurrencyRate = 350.50m,
    TotalAmount = 1000.00m,
    NetAmount = 1000.00m,
    VatAmount = 0.00m,
    ExportDestination = 3, // Other countries
    Incoterm = 3, // FOB
    Language = 2 // English
};

var exportResponse = await client.ExportInvoicing.AuthorizeExportInvoiceAsync(exportInvoice);
Console.WriteLine($"Export CAE: {exportResponse.Cae}");

Detailed Invoice with Items (WSMTXCA)

var detailedInvoice = new DetailedInvoiceRequest
{
    PointOfSale = 1,
    InvoiceType = 11, // Invoice C
    InvoiceNumberFrom = 1,
    InvoiceNumberTo = 1,
    InvoiceDate = DateTime.Today,
    DocumentType = 96, // DNI
    DocumentNumber = 12345678,
    ReceiverName = "Juan PΓ©rez",
    ReceiverAddress = "Av. Corrientes 123",
    ReceiverCity = "Buenos Aires",
    ReceiverPostalCode = "1043",
    ReceiverVatCondition = 5, // Consumidor Final
    TotalAmount = 242.00m,
    NetAmount = 200.00m,
    VatAmount = 42.00m,
    Items = new List<InvoiceItem>
    {
        new InvoiceItem
        {
            Description = "Laptop HP Pavilion",
            Quantity = 1,
            UnitPrice = 200.00m,
            TotalAmount = 242.00m,
            NetAmount = 200.00m,
            VatAmount = 42.00m,
            UnitOfMeasurement = 6, // Unit
            ItemCategory = 1, // Products
            ItemType = 1, // Goods
            VatRate = 5 // 21%
        }
    },
    VatDetails = new List<VatDetail>
    {
        new VatDetail
        {
            VatRateId = 5, // 21%
            BaseAmount = 200.00m,
            VatAmount = 42.00m
        }
    }
};

var detailedResponse = await client.DetailedInvoicing.AuthorizeDetailedInvoiceAsync(detailedInvoice);
Console.WriteLine($"Detailed Invoice CAE: {detailedResponse.Cae}");

Credit Note with Association

var creditNote = new InvoiceRequest
{
    PointOfSale = 1,
    InvoiceType = 213, // Credit Note C
    Concept = 1,
    DocumentType = 96,
    DocumentNumber = 12345678,
    InvoiceNumberFrom = 1,
    InvoiceNumberTo = 1,
    InvoiceDate = DateTime.Today,
    TotalAmount = 50.00m,
    NetAmount = 41.32m,
    VatAmount = 8.68m,
    
    // Associate with original invoice
    AssociatedInvoices = new List<AssociatedInvoice>
    {
        new AssociatedInvoice
        {
            InvoiceType = 11,
            PointOfSale = 1,
            InvoiceNumber = 123L
        }
    },
    
    VatDetails = new List<VatDetail>
    {
        new VatDetail
        {
            VatRateId = 5,
            BaseAmount = 41.32m,
            VatAmount = 8.68m
        }
    }
};

var response = await client.ElectronicInvoicing.AuthorizeInvoiceAsync(creditNote);

Query Existing Invoice

var invoice = await client.ElectronicInvoicing.QueryInvoiceAsync(
    pointOfSale: 1,
    invoiceType: 11,
    invoiceNumber: 123
);

Console.WriteLine($"Invoice Amount: {invoice.TotalAmount}");
Console.WriteLine($"CAE: {invoice.Cae}");
Console.WriteLine($"Authorized: {invoice.ProcessedDate}");

Batch Invoice Processing

var batchRequests = new List<InvoiceRequest>
{
    // Create multiple invoice requests...
    invoice1,
    invoice2,
    invoice3
};

var responses = new List<InvoiceResponse>();

foreach (var request in batchRequests)
{
    try
    {
        var response = await client.ElectronicInvoicing.AuthorizeInvoiceAsync(request);
        responses.Add(response);
        Console.WriteLine($"Invoice {request.InvoiceNumberFrom} authorized with CAE: {response.Cae}");
    }
    catch (AfipException ex)
    {
        Console.WriteLine($"Error authorizing invoice {request.InvoiceNumberFrom}: {ex.Message}");
    }
}

Working with Parameter Tables

// Get invoice types
var invoiceTypes = await client.Parameters.GetInvoiceTypesAsync();
foreach (var type in invoiceTypes)
{
    Console.WriteLine($"{type.Id}: {type.Description}");
}

// Get currencies
var currencies = await client.Parameters.GetCurrenciesAsync();
var usd = currencies.FirstOrDefault(c => c.Id == "DOL");
Console.WriteLine($"USD Rate: {usd?.Rate}");

// Get VAT rates
var vatRates = await client.Parameters.GetVatRatesAsync();
foreach (var rate in vatRates)
{
    Console.WriteLine($"{rate.Id}: {rate.Description} - {rate.Percentage}%");
}

// Get document types
var documentTypes = await client.Parameters.GetDocumentTypesAsync();
var cuit = documentTypes.FirstOrDefault(d => d.Id == "80");
Console.WriteLine($"CUIT: {cuit?.Description}");

πŸ”’ Authentication Management

The SDK automatically handles WSAA authentication:

// Authentication is handled automatically, but you can access it directly
var ticket = await client.Authentication.GetValidTicketAsync("wsfe");
Console.WriteLine($"Token expires at: {ticket.ExpiresAt}");

// Clear ticket cache
client.Authentication.ClearTicketCache();

// Check ticket status
var isValid = ticket.IsValid;
var willExpireSoon = ticket.WillExpireSoon(10); // Check if expires in 10 minutes

🚨 Error Handling

try
{
    var response = await client.ElectronicInvoicing.AuthorizeInvoiceAsync(request);
}
catch (AfipException ex)
{
    Console.WriteLine($"AFIP Error: {ex.Message}");
    Console.WriteLine($"Error Code: {ex.ErrorCode}");
    
    if (ex.InnerException != null)
    {
        Console.WriteLine($"Inner Exception: {ex.InnerException.Message}");
    }
}
catch (HttpRequestException ex)
{
    Console.WriteLine($"Network Error: {ex.Message}");
}
catch (TaskCanceledException ex)
{
    Console.WriteLine($"Request Timeout: {ex.Message}");
}

πŸ“ Logging

The SDK supports Microsoft.Extensions.Logging:

using Microsoft.Extensions.Logging;

// Configure logging
var loggerFactory = LoggerFactory.Create(builder =>
    builder.AddConsole().SetMinimumLevel(LogLevel.Debug));

var logger = loggerFactory.CreateLogger<AfipClient>();

// Create client with logging
var config = new AfipConfiguration
{
    // ... configuration
    EnableLogging = true
};

using var client = new AfipClient(config, logger);

πŸ§ͺ Testing

The SDK includes comprehensive unit tests and supports both testing and production environments:

# Run all tests
dotnet test

# Run with coverage
dotnet test --collect:"XPlat Code Coverage"

# Run specific test category
dotnet test --filter Category=Unit

Test Configuration

// Use AFIP's testing environment
var testConfig = new AfipConfiguration
{
    Environment = AfipEnvironment.Testing,
    Cuit = 20123456789, // Use AFIP's test CUIT
    CertificatePath = "test-certificate.p12",
    CertificatePassword = "test-password"
};

πŸ“Š Performance Considerations

  • Connection Pooling: The SDK reuses HTTP connections
  • Ticket Caching: Authentication tickets are cached and automatically renewed
  • Async Operations: All operations are asynchronous for better scalability
  • Memory Efficiency: Minimal memory footprint with proper disposal patterns

πŸ”„ Migration from Other Libraries

If you're migrating from other AFIP libraries:

// Old library pattern
var oldClient = new SomeAfipLibrary();
var result = oldClient.AutorizarComprobante(data);

// New SDK pattern
using var newClient = AfipClient.CreateForProduction(cuit, certPath, certPassword);
var response = await newClient.ElectronicInvoicing.AuthorizeInvoiceAsync(request);

πŸ› οΈ Advanced Configuration

Custom HTTP Client

var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Add("User-Agent", "MyApp/1.0");

var config = new AfipConfiguration
{
    // ... other settings
    CustomHttpClient = httpClient
};

Custom URLs (for testing)

var config = new AfipConfiguration
{
    Environment = AfipEnvironment.Testing,
    CustomWsaaUrl = "https://custom-wsaa-url",
    CustomWsfev1Url = "https://custom-wsfev1-url",
    // ... other settings
};

πŸ“‹ Requirements

  • .NET Standard 2.0 or higher
  • .NET Framework 4.6.1 or higher
  • .NET Core 2.0 or higher
  • .NET 5.0 or higher

Dependencies

  • System.ServiceModel.Http (>= 4.10.0)
  • System.Security.Cryptography.Pkcs (>= 6.0.0)
  • Microsoft.Extensions.Logging.Abstractions (>= 6.0.0)

πŸ”§ Dependency Injection

The AFIP SDK provides seamless integration with Microsoft.Extensions.DependencyInjection through a separate package, making it easy to use in ASP.NET Core, Worker Services, and other .NET applications.

Installation

First, install the AFIP SDK DI package:

dotnet add package Afip.Dotnet.DependencyInjection

Basic Registration

using Afip.Dotnet.DependencyInjection.Extensions;
using Afip.Dotnet.Abstractions.Models;

// Register AFIP services with configuration
services.AddAfipServices(new AfipConfiguration
{
    Environment = AfipEnvironment.Testing,
    Cuit = 20123456789,
    CertificatePath = "path/to/certificate.p12",
    CertificatePassword = "certificate-password",
    EnableLogging = true
});

Configuration-based Registration

// Register with action-based configuration
services.AddAfipServices(config =>
{
    config.Environment = AfipEnvironment.Production;
    config.Cuit = 20123456789;
    config.CertificatePath = "certificates/production.p12";
    config.CertificatePassword = Environment.GetEnvironmentVariable("AFIP_CERT_PASSWORD");
    config.TimeoutSeconds = 60;
    config.EnableLogging = true;
});

Quick Setup Methods

// For testing environment
services.AddAfipServicesForTesting(
    cuit: 20123456789,
    certificatePath: "test-cert.p12", 
    certificatePassword: "test-password"
);

// For production environment
services.AddAfipServicesForProduction(
    cuit: 20123456789,
    certificatePath: "prod-cert.p12",
    certificatePassword: Environment.GetEnvironmentVariable("CERT_PASSWORD")!
);

Optimized Registration

// Register with all optimizations enabled
services.AddAfipServicesOptimized(new AfipConfiguration
{
    Environment = AfipEnvironment.Production,
    Cuit = 20123456789,
    CertificatePath = "cert.p12",
    CertificatePassword = "password"
});

// Register with minimal dependencies
services.AddAfipServicesMinimal(new AfipConfiguration
{
    Environment = AfipEnvironment.Testing,
    Cuit = 20123456789,
    CertificatePath = "test-cert.p12",
    CertificatePassword = "test-password"
});

πŸ“± Usage Examples with Dependency Injection

ASP.NET Core Web API

// Program.cs (.NET 6+)
using Afip.Dotnet.DependencyInjection.Extensions;

var builder = WebApplication.CreateBuilder(args);

// Add AFIP services
builder.Services.AddAfipServicesForTesting(
    cuit: 20123456789,
    certificatePath: "certificates/testing.p12",
    certificatePassword: builder.Configuration["Afip:CertificatePassword"]!
);

builder.Services.AddControllers();

var app = builder.Build();

app.MapControllers();
app.Run();

// Controllers/InvoiceController.cs
using Microsoft.AspNetCore.Mvc;
using Afip.Dotnet.Abstractions.Services;
using Afip.Dotnet.Abstractions.Models.Invoice;

[ApiController]
[Route("api/[controller]")]
public class InvoiceController : ControllerBase
{
    private readonly IAfipClient _afipClient;
    private readonly ILogger<InvoiceController> _logger;

    public InvoiceController(IAfipClient afipClient, ILogger<InvoiceController> logger)
    {
        _afipClient = afipClient;
        _logger = logger;
    }

    [HttpPost("authorize")]
    public async Task<IActionResult> AuthorizeInvoice([FromBody] InvoiceRequest request)
    {
        try
        {
            var response = await _afipClient.ElectronicInvoicing.AuthorizeInvoiceAsync(request);
            
            _logger.LogInformation("Invoice authorized with CAE: {Cae}", response.Cae);
            
            return Ok(new
            {
                Success = true,
                Cae = response.Cae,
                ExpirationDate = response.CaeExpirationDate,
                InvoiceNumber = request.InvoiceNumberFrom
            });
        }
        catch (AfipException ex)
        {
            _logger.LogError(ex, "AFIP error while authorizing invoice");
            return BadRequest(new { Error = ex.Message, Code = ex.ErrorCode });
        }
    }

    [HttpGet("next-number/{pointOfSale}/{invoiceType}")]
    public async Task<IActionResult> GetNextInvoiceNumber(int pointOfSale, int invoiceType)
    {
        try
        {
            var lastNumber = await _afipClient.ElectronicInvoicing.GetLastInvoiceNumberAsync(
                pointOfSale, invoiceType);
            
            return Ok(new { NextNumber = lastNumber + 1 });
        }
        catch (AfipException ex)
        {
            _logger.LogError(ex, "Error getting next invoice number");
            return BadRequest(new { Error = ex.Message });
        }
    }

    [HttpGet("status")]
    public async Task<IActionResult> GetServiceStatus()
    {
        try
        {
            var status = await _afipClient.ElectronicInvoicing.CheckServiceStatusAsync();
            return Ok(status);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error checking service status");
            return StatusCode(500, new { Error = "Service unavailable" });
        }
    }
}

Worker Service Background Processing

// Program.cs
using Afip.Dotnet.DependencyInjection.Extensions;

var builder = Host.CreateApplicationBuilder(args);

// Add AFIP services
builder.Services.AddAfipServicesForProduction(
    cuit: long.Parse(builder.Configuration["Afip:Cuit"]!),
    certificatePath: builder.Configuration["Afip:CertificatePath"]!,
    certificatePassword: builder.Configuration["Afip:CertificatePassword"]!
);

builder.Services.AddHostedService<InvoiceProcessorWorker>();

var host = builder.Build();
host.Run();

// InvoiceProcessorWorker.cs
using Afip.Dotnet.Abstractions.Services;
using Afip.Dotnet.Abstractions.Models.Invoice;

public class InvoiceProcessorWorker : BackgroundService
{
    private readonly IAfipClient _afipClient;
    private readonly ILogger<InvoiceProcessorWorker> _logger;

    public InvoiceProcessorWorker(IAfipClient afipClient, ILogger<InvoiceProcessorWorker> logger)
    {
        _afipClient = afipClient;
        _logger = logger;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            try
            {
                // Process pending invoices from database/queue
                var pendingInvoices = await GetPendingInvoicesFromDatabase();

                foreach (var invoiceData in pendingInvoices)
                {
                    var request = MapToInvoiceRequest(invoiceData);
                    
                    var response = await _afipClient.ElectronicInvoicing
                        .AuthorizeInvoiceAsync(request, stoppingToken);
                    
                    await UpdateInvoiceInDatabase(invoiceData.Id, response);
                    
                    _logger.LogInformation("Processed invoice {InvoiceId} with CAE {Cae}", 
                        invoiceData.Id, response.Cae);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error processing invoices");
            }

            await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken);
        }
    }

    private async Task<List<InvoiceData>> GetPendingInvoicesFromDatabase()
    {
        // Implementation to get pending invoices
        return new List<InvoiceData>();
    }

    private InvoiceRequest MapToInvoiceRequest(InvoiceData data)
    {
        // Implementation to map from your data model to InvoiceRequest
        return new InvoiceRequest();
    }

    private async Task UpdateInvoiceInDatabase(int invoiceId, InvoiceResponse response)
    {
        // Implementation to update invoice with AFIP response
    }
}

Console Application

// Program.cs
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Afip.Dotnet.DependencyInjection.Extensions;
using Afip.Dotnet.Abstractions.Services;
using Afip.Dotnet.Abstractions.Models.Invoice;

var host = Host.CreateDefaultBuilder(args)
    .ConfigureServices((context, services) =>
    {
        // Add AFIP services
        services.AddAfipServicesForTesting(
            cuit: 20123456789,
            certificatePath: "certificates/testing.p12",
            certificatePassword: "test-password"
        );
        
        services.AddScoped<InvoiceProcessor>();
    })
    .Build();

// Run the application
using var scope = host.Services.CreateScope();
var processor = scope.ServiceProvider.GetRequiredService<InvoiceProcessor>();
await processor.ProcessInvoiceAsync();

public class InvoiceProcessor
{
    private readonly IAfipClient _afipClient;
    private readonly ILogger<InvoiceProcessor> _logger;

    public InvoiceProcessor(IAfipClient afipClient, ILogger<InvoiceProcessor> logger)
    {
        _afipClient = afipClient;
        _logger = logger;
    }

    public async Task ProcessInvoiceAsync()
    {
        try
        {
            // Check service status
            var status = await _afipClient.ElectronicInvoicing.CheckServiceStatusAsync();
            _logger.LogInformation("AFIP Service Status: {Status}", status.AppServer);

            // Get next invoice number
            var lastNumber = await _afipClient.ElectronicInvoicing
                .GetLastInvoiceNumberAsync(pointOfSale: 1, invoiceType: 11);
            var nextNumber = lastNumber + 1;

            // Create and authorize invoice
            var request = new InvoiceRequest
            {
                PointOfSale = 1,
                InvoiceType = 11, // Invoice C
                Concept = 1, // Products
                DocumentType = 96, // DNI
                DocumentNumber = 12345678,
                InvoiceNumberFrom = nextNumber,
                InvoiceNumberTo = nextNumber,
                InvoiceDate = DateTime.Today,
                TotalAmount = 121.00m,
                NetAmount = 100.00m,
                VatAmount = 21.00m,
                VatDetails = new List<VatDetail>
                {
                    new VatDetail
                    {
                        VatRateId = 5, // 21%
                        BaseAmount = 100.00m,
                        VatAmount = 21.00m
                    }
                }
            };

            var response = await _afipClient.ElectronicInvoicing.AuthorizeInvoiceAsync(request);
            
            _logger.LogInformation("Invoice authorized successfully!");
            _logger.LogInformation("CAE: {Cae}", response.Cae);
            _logger.LogInformation("Expiration: {ExpirationDate}", response.CaeExpirationDate);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error processing invoice");
        }
    }
}

Configuration with appsettings.json

// appsettings.json
{
  "Afip": {
    "Environment": "Testing",
    "Cuit": "20123456789",
    "CertificatePath": "certificates/testing.p12",
    "CertificatePassword": "your-certificate-password",
    "TimeoutSeconds": 30,
    "EnableLogging": true
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Afip.Dotnet": "Debug"
    }
  }
}
// Configuration binding
services.AddAfipServices(config =>
{
    var afipSettings = builder.Configuration.GetSection("Afip");
    config.Environment = Enum.Parse<AfipEnvironment>(afipSettings["Environment"]!);
    config.Cuit = long.Parse(afipSettings["Cuit"]!);
    config.CertificatePath = afipSettings["CertificatePath"]!;
    config.CertificatePassword = afipSettings["CertificatePassword"]!;
    config.TimeoutSeconds = afipSettings.GetValue<int>("TimeoutSeconds");
    config.EnableLogging = afipSettings.GetValue<bool>("EnableLogging");
});

Advanced Scenarios

Custom Service Registration
// Register with custom lifetimes
services.AddAfipServices(config => { /* configuration */ });

// Override specific services if needed
services.AddSingleton<IAfipClient, CustomAfipClient>();
Multiple AFIP Configurations
// For applications handling multiple companies
services.AddKeyedScoped<IAfipClient>("Company1", (provider, key) =>
{
    var config = new AfipConfiguration
    {
        Cuit = 20111111111,
        CertificatePath = "certs/company1.p12",
        // ... other settings
    };
    return new AfipClient(config, provider.GetService<ILoggerFactory>());
});

services.AddKeyedScoped<IAfipClient>("Company2", (provider, key) =>
{
    var config = new AfipConfiguration
    {
        Cuit = 20222222222,
        CertificatePath = "certs/company2.p12",
        // ... other settings
    };
    return new AfipClient(config, provider.GetService<ILoggerFactory>());
});

// Usage in controllers/services
public class MultiCompanyInvoiceController : ControllerBase
{
    public async Task<IActionResult> AuthorizeForCompany1(
        [FromKeyedServices("Company1")] IAfipClient afipClient,
        [FromBody] InvoiceRequest request)
    {
        var response = await afipClient.ElectronicInvoicing.AuthorizeInvoiceAsync(request);
        return Ok(response);
    }
}

🀝 Contributing

We welcome contributions! Please see our Contributing Guidelines for details.

Development Setup

  1. Clone the repository
  2. Install .NET 6.0 SDK or later
  3. Run dotnet restore
  4. Run dotnet build
  5. Run dotnet test

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

Official AFIP Documentation

Technical Documentation

AFIP Regulations

Service URLs

  • Testing WSAA: https://wsaahomo.afip.gov.ar/ws/services/LoginCms
  • Production WSAA: https://wsaa.afip.gov.ar/ws/services/LoginCms
  • Testing WSFEv1: https://wswhomo.afip.gov.ar/wsfev1/service.asmx
  • Production WSFEv1: https://servicios1.afip.gov.ar/wsfev1/service.asmx

Certificate Management

❓ FAQ

Q: Do I need to register with AFIP to use this SDK? A: Yes, you need to be registered with AFIP and have a valid certificate for electronic invoicing.

Q: Can I use this in production immediately? A: Yes, but make sure to test thoroughly in AFIP's testing environment first.

Q: What's the difference between Invoice A, B, and C? A:

  • Invoice A: For VAT registered entities
  • Invoice B: For final consumers (individuals)
  • Invoice C: For exempt entities

Q: How do I handle certificate expiration? A: The SDK will throw an AfipException when the certificate expires. You'll need to renew it through AFIP.

Q: Is this SDK thread-safe? A: Yes, the SDK is designed to be thread-safe and can be used in multi-threaded applications.

Q: What's the difference between the core package and the DI package? A: The core package (Afip.Dotnet) contains the main SDK functionality. The DI package (Afip.Dotnet.DependencyInjection) provides dependency injection extensions for easier integration with ASP.NET Core and other DI containers.

πŸ“ž Support


Made with ❀️ for the Argentine .NET community

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.0-prerelease 50 7/4/2025