SDK de Ruby
La gema oficial fiscalrail cubre las 42 operaciones de la referencia de la API. La versión 0.4.0 requiere Ruby 3.3 o posterior y funciona con o sin Rails. El código fuente y las versiones están disponibles en GitHub.
Instala y autentica
Añade la gema a tu Gemfile y ejecuta bundle install:
gem "fiscalrail", "~> 0.4.0"
Crea un cliente con una clave de API explícita:
require "fiscalrail"
client = FiscalRail::Client.new(api_key: ENV.fetch("FISCALRAIL_API_KEY"))
Tu aplicación lee la clave desde su entorno o gestor de secretos; el SDK no la busca automáticamente. Test y Live usan la misma URL base. La clave selecciona el entorno de la cuenta.
Reutiliza el cliente durante la vida del proceso y llama a client.close al cerrarlo. El adaptador HTTP mantiene una conexión y ejecuta las peticiones concurrentes de forma secuencial. Usa clientes separados si necesitas peticiones HTTP en paralelo. FiscalRail::Client.open(api_key: key) { |client| ... } cierra la conexión al terminar el bloque. Tu aplicación conserva la responsabilidad de cerrar los adaptadores que inyecte mediante adapter:.
Emite una factura
Usa BigDecimal o cadenas para los importes y los helpers de España para los impuestos del catálogo:
require "fiscalrail"
client = FiscalRail::Client.new(api_key: ENV.fetch("FISCALRAIL_API_KEY"))
invoice = client.invoices.issue(
idempotency_key: "2f294ef2-9a60-4c7e-a573-5e18fa8348e2",
lines: [{ description: "Consulting", quantity: 2, unit_price: BigDecimal("75.00"),
taxes: [FiscalRail::TaxRegimes::ES::VAT.general] }]
)
Las peticiones aceptan argumentos con nombre y hashes anidados con claves de tipo símbolo o cadena. Los campos omitidos no se envían; nil, false y los arrays vacíos se conservan cuando se indican explícitamente. Las fechas y horas se serializan como cadenas ISO 8601. La API valida las reglas de negocio.
Modelos de respuesta
Las respuestas son objetos generados de solo lectura bajo FiscalRail::Models. Las fechas se convierten en Date, las marcas de tiempo en Time y los decimales en BigDecimal:
invoice.id
invoice.issue_date
invoice.totals.payable
invoice.request_id
invoice.idempotency_key
invoice.idempotent_replayed
invoice.to_h
Los valores anidados están congelados. Los campos desconocidos siguen disponibles en extra_fields y response["field"]; to_h devuelve contenedores nuevos con claves de cadena. Una estructura de respuesta inválida genera FiscalRail::ResponseParseError con la ruta del campo y el identificador de la petición.
Idempotencia y reintentos
issue y amend generan una clave de idempotencia si no proporcionas una. Los reintentos de esa llamada la reutilizan. Para trabajos persistentes, guarda tu propia idempotency_key: antes del primer intento y pásala en cada reintento, incluso tras reiniciar el proceso. Una llamada nueva sin clave genera otra distinta. Nunca reutilices una clave para otra operación o contenido.
Por defecto se realizan como máximo dos reintentos ante fallos de conexión, tiempos de espera agotados y respuestas HTTP 408, 429 o 5xx, solo para operaciones seguras: lecturas, emisiones y rectificaciones con idempotencia, generación de PDF y activación o desactivación de destinos. Las llamadas ordinarias de creación, actualización y eliminación no se reintentan automáticamente. El cliente respeta Retry-After, limitado a 30 segundos, y en su ausencia usa espera exponencial. Configura max_retries: 0 para desactivar los reintentos.
Errores
Todos los errores del SDK heredan de FiscalRail::Error. Los errores de API incluyen status_code, code, request_id, details e idempotency_key:
begin
invoice = client.invoices.retrieve("inv_...")
rescue FiscalRail::APIConnectionError => error
warn error.message
rescue FiscalRail::APIError => error
warn "#{error.code}: HTTP #{error.status_code}, request #{error.request_id}"
end
Los errores de conexión y tiempo de espera también conservan la clave de idempotencia. Guárdala cuando no sepas si una emisión o rectificación llegó a completarse.
Recursos disponibles
El cliente expone account, account_tax_regimes, balances, api_keys, customers, event_destinations, events, invoice_series, payment_instructions, invoices, invoice_pdfs, tax_ids y tax_regimes. Cada operación de la referencia incluye un ejemplo en Ruby. Las facturas usan issue y amend; las facturas emitidas nunca se actualizan.
Las rutas de cuenta singleton requieren una versión del SDK generada a partir del contrato OpenAPI actual. Las versiones publicadas todavía usan IDs de cuenta.
Consulta el estado de una cuenta con client.balances.retrieve() y client.account_tax_regimes.retrieve(). El recurso independiente tax_regimes describe el catálogo público.
Paginación
list obtiene una página. Usa auto_paging_each para recuperar las siguientes a medida que las recorres, conservando los filtros:
page = client.customers.list(country: "ES", limit: 25)
page.each { |customer| puts customer.name }
client.invoices.auto_paging_each(customer: "cus_...", page_size: 100) do |invoice|
puts invoice.code
end
Las páginas exponen data y has_more. Para gestionar los cursores manualmente, pasa starting_after: o ending_before: a list. Las cuentas y los regímenes fiscales devuelven listas únicas y no ofrecen paginación automática.
PDF de facturas
render y retrieve devuelven metadatos del PDF, incluida una URL para el cliente. Para descargar directamente los bytes:
client.invoice_pdfs.render_content(invoice.id, locale: "es").write_to_file("invoice.pdf")
Los métodos _content devuelven FiscalRail::BinaryContent. Las descargas se almacenan en memoria. locale: se convierte en la cabecera Accept-Language.
Verifica webhooks
Verifica el cuerpo sin modificar antes de procesar el evento. En un controlador de Rails:
event = FiscalRail::Webhooks.construct_event(
request.raw_post,
request.headers["FiscalRail-Signature"],
signing_secret
)
El helper verifica el HMAC en tiempo constante, rechaza marcas de tiempo que se alejen más de cinco minutos en cualquier dirección e interpreta el evento como un hash con claves de cadena. Si la verificación falla, genera FiscalRail::WebhookSignatureError. Consulta webhooks para conocer el comportamiento de entrega y confirmación.