Nada de esto está inventado: son llamadas reales entre FIRE y un proveedor fiscal en
funcionamiento, con los identificadores cambiados. Sirven para contrastar una
implementación contra algo que ya funciona, en vez de contra una descripción.
Numerar una venta
El request es el mismo para los dos países —mismos campos, mismo orden—; lo que cambia es
qué usa cada proveedor y qué devuelve en document. Acá van los cuerpos recortados a lo que
hace falta para leer el ejemplo; el request completo, campo por campo, está en el
contrato.
Ecuador (EC) — SRI
Colombia (CO) — DIAN
Lo que enviamos
storeFiscalConfig.metadata va vacío: en Ecuador el establecimiento y el punto de emisión
los resolvés vos contra tu catálogo, a partir de store.code y device.uid.Lo que devolvió el proveedor
document habla ecuatoriano. claveAcceso, secuencial, ambiente — los nombres
del SRI, no una traducción. Y no hay campos de otros países: el cufe colombiano
sencillamente no existe acá.
- El número visible viene armado en
numeroComprobante (005-004-000000052). Lo
componés vos, que conocés la regla —art. 18 del Reglamento— y FIRE lo imprime tal cual,
sin reformatearlo. Las tres piezas siguen viajando aparte, como las define el SRI, pero
son para conciliar: nadie las vuelve a unir.
graphic.qr coincide con document.claveAcceso. En Ecuador es así, y la redundancia
es deliberada: la alternativa es que el punto de venta sepa qué se codifica en cada país.
ambiente: "2" es pruebas — en el SRI. En la DIAN el 2 es al revés; ver la pestaña
de Colombia.
Lo que enviamos
Acá storeFiscalConfig.metadata sí trae datos —la clave técnica y el rango que la DIAN
entrega con la resolución—, y los importes de totals no son informativos: entran al hash
del CUFE.Lo que devolvió el proveedor
document habla colombiano, y no se parece al de Ecuador: no hay claveAcceso ni
secuencial, hay cufe, prefijo y numeroDian.
graphic viene en null. En Colombia el QR es la URL del catálogo de la DIAN y viaja
dentro de document.qrCode, así que no se duplica afuera.
numeroComprobante y numeroDian no son lo mismo: el primero es el número visible ya
armado (prefijo + consecutivo), el segundo es el consecutivo solo. Los dos se devuelven
resueltos; FIRE no concatena nada.
ambiente: "2" es pruebas en la DIAN — el código es el del ente, sin normalizar, y
por eso significa lo contrario que en Ecuador.
Fijate en lo que no viaja en ninguno de los dos: ni accountId, ni vendorId —el tenant
sale de la API key—, ni códigos del ente, ni referencia al catálogo del proveedor.
Y dos cosas comunes a los dos países:
authorizationMode e issuedAt están en la raíz, fuera de document: son comunes a
todos los países, así que no pertenecen al bloque del país.
status: "INVOICED", no "PENDING". Recién numerado el documento siempre está
pendiente de autorización — es la condición normal, no un estado que informar.
Cuando falla
La misma petición, con la tienda fuera del catálogo del proveedor
Por qué este error está bien construido:
retryable es lo que decide qué pasa después. Con false cortamos y la venta queda
sin comprobante fiscal, con el motivo registrado. Con true la solicitud queda abierta y
se puede retomar.Sin ese campo hay que adivinar por el código HTTP — y adivinar mal significa reintentar
mientras el cliente espera, o abandonar una venta que se podía numerar.
Qué hacemos con cada respuesta
El estado que expone FIRE a sus canales se deriva de lo que devuelve el proveedor. El
proveedor no conoce estos estados ni tiene que emitirlos:
PENDING no puede venir del proveedor por definición: decirlo implicaría haber
contestado. Es el estado de “no hubo respuesta”, y es el más delicado — el proveedor pudo
haber numerado y consumido un secuencial sin que nos enteremos.Por eso tu deduplicación tiene que ser por orderCode: el reintento llega con el mismo
orderCode y debe devolver el mismo documento con reused: true, en vez de numerar otro.No la bases en un header de idempotencia: no te mandamos ninguno. La llamada al
proveedor lleva solo x-api-key y Content-Type. La llave natural
—país + orderCode + operación— es lo único que liga un reintento con el intento
original, en los dos extremos.