IQeSign.TicketBai.MultiTenant
1.0.0
dotnet add package IQeSign.TicketBai.MultiTenant --version 1.0.0
NuGet\Install-Package IQeSign.TicketBai.MultiTenant -Version 1.0.0
<PackageReference Include="IQeSign.TicketBai.MultiTenant" Version="1.0.0" />
<PackageVersion Include="IQeSign.TicketBai.MultiTenant" Version="1.0.0" />
<PackageReference Include="IQeSign.TicketBai.MultiTenant" />
paket add IQeSign.TicketBai.MultiTenant --version 1.0.0
#r "nuget: IQeSign.TicketBai.MultiTenant, 1.0.0"
#:package IQeSign.TicketBai.MultiTenant@1.0.0
#addin nuget:?package=IQeSign.TicketBai.MultiTenant&version=1.0.0
#tool nuget:?package=IQeSign.TicketBai.MultiTenant&version=1.0.0
IQeSign.TicketBai.MultiTenant
Cliente .NET multi-tenant para la API IQ eSign TicketBAI de InnoQubit Software. Permite gestionar facturas TicketBAI de múltiples clientes desde una sola instancia, con caché de tokens JWT independiente por credencial y refresco automático.
Sobre InnoQubit
InnoQubit Business Software es una empresa tecnológica con sede en Castellón (España), especializada en la digitalización y automatización de procesos empresariales para sistemas ERP.
Su producto insignia IQ eSign agrupa soluciones de facturación electrónica y firma digital que se integran con cualquier ERP (Microsoft Dynamics 365 Business Central, Navision y software a medida):
| Solución | Descripción |
|---|---|
| IQ eSign VeriFactu | Presentación de facturas al sistema VeriFactu de la AEAT |
| IQ eSign TicketBAI | Facturación electrónica para el País Vasco |
| IQ eSign Facturae | Generación y envío de facturas en formato Facturae |
| IQ eSign ePDF | Generación de PDFs firmados digitalmente |
¿Qué es TicketBAI?
TicketBAI es el sistema de facturación electrónica obligatorio en el País Vasco, regulado por las haciendas forales de Álava, Gipuzkoa y Bizkaia. Garantiza la integridad de cada factura mediante encadenamiento de firmas digitales y su presentación en tiempo real a la hacienda foral correspondiente.
¿Qué paquete utilizar?
| Escenario | Paquete recomendado |
|---|---|
| Una sola empresa / un solo credencial | IQeSign.TicketBai |
| Múltiples clientes / credenciales dinámicos | IQeSign.TicketBai.MultiTenant ← este paquete |
Este paquete está pensado para integradores, ERPs en la nube y plataformas SaaS que gestionan las facturas TicketBAI de varios clientes desde una misma instancia de aplicación. Ambos paquetes son independientes entre sí: instala únicamente el que necesites.
Instalación
dotnet add package IQeSign.TicketBai.MultiTenant
Para obtener una cuenta y los CredentialGuid de tus clientes, contacta con el equipo comercial de InnoQubit en comercial@innoqubit.com.
Inicio rápido
1. Registro en el contenedor DI
// Program.cs
builder.Services.AddIQeSignTicketBaiMultiTenant(options =>
{
options.Environment = IQeSignEnvironment.Production; // o Staging para pruebas
options.TimeoutSeconds = 30;
});
O bien usando una sección de appsettings.json:
{
"IQeSignTicketBaiMultiTenant": {
"Environment": "Production",
"TimeoutSeconds": 30
}
}
builder.Services.AddIQeSignTicketBaiMultiTenant(
builder.Configuration.GetSection(IQeSignMultiTenantOptions.SectionName));
Nota: No se especifica
CredentialGuiden la configuración. Cada cliente aporta su propiocredentialGuiden tiempo de ejecución.
2. Inyectar y usar el cliente
public class FacturacionMultiTenantService(IQeSignMultiTenantClient multiTenant)
{
public async Task<string> EnviarFacturaAsync(
string credentialGuid, AddDocumentRequest request, CancellationToken ct = default)
{
var tenant = multiTenant.ForTenant(credentialGuid);
var response = await tenant.TicketBai.AddDocumentAsync(request, ct);
if (!response.IsSuccess)
throw new Exception($"Error TicketBAI [{response.ErrorCode}]: {response.ErrorMessage}");
return response.Result!.Id!;
}
}
3. Ejemplo completo: enviar una factura emitida
var tenant = multiTenantClient.ForTenant("credentialGuid-del-cliente");
var response = await tenant.TicketBai.AddDocumentAsync(new AddDocumentRequest
{
CertificateId = "id-del-certificado-en-iqportal",
CertificatePass = "contraseña-del-pfx",
File = new TicketBaiDocumentFile
{
Issuer = new TicketBaiIssuerInfo
{
Name = "Mi Empresa S.L.",
CifNif = "B12345678",
TaxCategory = "662"
},
Serial = "FAC",
Number = "2024-001",
Date = "2024-01-15",
Name = "Cliente S.A.",
Nif = "A98765432",
Address = "Calle Mayor 1",
ZipCode = "28001",
Country = "ES",
Simplified = false,
OperationType = OperationTypeEmitted.SinInversion,
Rectified = false,
TotalInvoice = 1210.00m,
Administration = Administration.Gipuzkoa,
Lines =
[
new InvoiceLine
{
Description = "Servicios de consultoría",
Quantity = 1,
UnitAmount = 1000.00m,
DiscountAmount = 0m,
Vat = 21m,
VatEc = 0m,
VatSubject = true,
VatCause = VatCause.E1,
TaxKey = TaxKey.RegimenGeneral
}
]
},
Metadata = new DocumentMetadata
{
Platform = "MiApp",
Version = "1.0.0",
User = "usuario@empresa.com",
Email = "facturacion@empresa.com",
Company = "Mi Empresa S.L.",
Tenant = "tenant-001",
Description = "Factura generada desde MiApp"
}
}, ct);
4. Facturas recibidas por tenant
var tenant = multiTenantClient.ForTenant("credentialGuid-del-cliente");
var response = await tenant.Received.AddReceivedAsync(new AddReceivedRequest
{
CertificateId = "id-certificado",
CertificatePass = "contraseña",
File = new ReceivedDocumentFile
{
Issuer = new ReceivedIssuerInfo
{
Name = "Proveedor S.L.",
CifNif = "B87654321",
Country = "ES",
IdentifierType = IdentifierType.NifIva
},
OperationType = ReceivedOperationType.AdquisicionBienesServicios,
InvoiceType = ReceivedInvoiceType.FacturaConDestinatario,
Serial = "PRV",
Number = "2024-100",
Exercise = 2024,
ReceivedDate = "2024-01-20",
InvoiceDate = "2024-01-18",
SalesVatQuote = 0m,
Name = "Mi Empresa S.L.",
Nif = "B12345678",
Description = "Servicios de consultoría recibidos",
Rectified = false,
TaxBase = 500.00m,
TotalInvoice = 605.00m
}
}, ct);
5. Certificados por tenant
var tenant = multiTenantClient.ForTenant("credentialGuid-del-cliente");
var certs = await tenant.Certificate.ListAsync(ct);
Cómo funciona internamente
Consumer code
→ IQeSignMultiTenantClient.ForTenant(credentialGuid)
→ TenantClient (Certificate + TicketBai + Received vinculados al tenant)
→ MultiTenantHttpClient (singleton, caché de tokens JWT por tenant)
→ IHttpClientFactory (named client: Production o Staging)
→ https://iqesignapi.azurewebsites.net
Caché de tokens JWT
- Un token por tenant:
ConcurrentDictionary<credentialGuid, TokenEntry>garantiza aislamiento total entre clientes. - Refresco automático: El token se renueva cuando caduca (margen de 1 h sobre los 24 h reales de la API).
- Sin bloqueos globales: Cada tenant tiene su propio
SemaphoreSlim(1,1), evitando que el refresco de un cliente bloquee a los demás. - Double-check pattern: Tras adquirir el semáforo se vuelve a comprobar la caché para evitar refreshes redundantes.
Servicios disponibles en TenantClient
El TenantClient devuelto por ForTenant(credentialGuid) expone tres servicios:
tenant.TicketBai → ITicketBaiService
| Método | Endpoint | Descripción |
|---|---|---|
GetUsageAsync() |
GET /api/v2/Ticketbai/Usage |
Consumo del plan contratado |
AddDocumentAsync(request) |
POST /api/v2/TicketBai/Document |
Envía una factura emitida |
GetDocumentByIdAsync(id) |
GET /api/v2/TicketBai/Document/{id} |
Consulta un documento |
DownloadDocumentAsync(id) |
GET /api/v2/TicketBai/Document/{id}/Download |
Descarga el XML firmado (ZIP Base64) |
CancelDocumentAsync(id) |
PUT /api/v2/TicketBai/Document/{id}/Cancel |
Cancela un documento |
RetryDocumentAsync(id) |
PUT /api/v2/TicketBai/Document/{id}/Retry |
Reintenta un envío fallido |
ListDocumentsAsync(filtros?) |
GET /api/v2/TicketBai/Document/List |
Lista documentos |
AddZuzenduAsync(id, request) |
POST /api/v2/TicketBai/Document/{id}/Zuzendu |
Corrección Zuzendu (Álava/Gipuzkoa) |
CancelZuzenduAsync(id) |
PUT /api/v2/TicketBai/Document/{id}/Zuzendu/Cancel |
Cancela un Zuzendu |
tenant.Received → ITicketBaiReceivedService
| Método | Endpoint | Descripción |
|---|---|---|
AddReceivedAsync(request) |
POST /api/v2/TicketBai/Received |
Registra una factura recibida |
CancelReceivedAsync(id, request) |
PUT /api/v2/TicketBai/Received/{id}/Cancel |
Cancela una factura recibida |
CheckReceivedAsync(id, request) |
PUT /api/v2/TicketBai/Received/{id}/Check |
Consulta el estado en hacienda |
UpdateReceivedAsync(id, request) |
PUT /api/v2/TicketBai/Received/{id} |
Actualiza una factura recibida |
ListReceivedAsync(filtros?) |
GET /api/v2/TicketBai/Received/List |
Lista facturas recibidas |
tenant.Certificate → ICertificateService
| Método | Endpoint | Descripción |
|---|---|---|
AddAsync(request) |
POST /api/v2/Certificate |
Sube un certificado .pfx en Base64 |
GetByIdAsync(id) |
GET /api/v2/Certificate/{id} |
Consulta un certificado |
DownloadAsync(id) |
GET /api/v2/Certificate/{id}/Download |
Descarga el .pfx |
ListAsync() |
GET /api/v2/Certificate/List |
Lista todos los certificados |
DeleteAsync(id) |
DELETE /api/v2/Certificate/{id} |
Elimina un certificado |
Gestión avanzada de tokens
// Forzar un nuevo login para un tenant
multiTenantClient.InvalidateToken("credentialGuid-del-cliente");
// Limpiar tokens caducados (liberar memoria en aplicaciones con muchos tenants)
multiTenantClient.PurgeExpiredTokens();
// Número de tenants con token activo en caché
int activos = multiTenantClient.ActiveTenantCount;
Control de errores
var tenant = multiTenantClient.ForTenant(credentialGuid);
var response = await tenant.TicketBai.AddDocumentAsync(request, ct);
if (!response.IsSuccess)
{
// ErrorCode "0" → éxito
// ErrorCode "8" → error de validación (datos incorrectos, ID no encontrado)
// ErrorCode "9" → error no controlado (ver ErrorMessage)
// Otros → error de negocio (ver ErrorMessage)
Console.WriteLine($"[{response.ErrorCode}] {response.ErrorMessage}");
}
| Excepción | Cuándo se lanza |
|---|---|
IQeSignAuthException |
El credentialGuid es inválido o la cuenta está inactiva |
IQeSignApiException |
La API devuelve un código HTTP 4xx/5xx inesperado |
Entornos
| Entorno | URL | Uso |
|---|---|---|
IQeSignEnvironment.Production |
https://iqesignapi.azurewebsites.net |
Producción real |
IQeSignEnvironment.Staging |
https://iqesignapistaging.azurewebsites.net |
Pruebas e integración |
Requisitos
- .NET 8.0 o .NET Standard 2.1 (compatible con .NET 6+, .NET 7+)
- Cuenta activa en IQ Portal con la solución IQ eSign TicketBAI contratada para cada tenant
- Certificados digitales .pfx válidos para firma de facturas
Documentación adicional
- Documentación técnica de la API IQ eSign TicketBAI (PDF)
- IQ Portal — gestión de credenciales y certificados
- Swagger de la API (producción)
Licencia
MIT © InnoQubit Software
| Product | Versions 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 is compatible. 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 | netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.1 is compatible. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.1
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.2)
- Microsoft.Extensions.Http (>= 8.0.1)
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.3)
- Microsoft.Extensions.Options (>= 8.0.2)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 8.0.0)
- System.Net.Http.Json (>= 9.0.0)
- System.Text.Json (>= 9.0.0)
-
net8.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.2)
- Microsoft.Extensions.Http (>= 8.0.1)
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.3)
- Microsoft.Extensions.Options (>= 8.0.2)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 8.0.0)
- System.Net.Http.Json (>= 9.0.0)
- System.Text.Json (>= 9.0.0)
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 |
|---|---|---|
| 1.0.0 | 99 | 5/13/2026 |