<p align="center">
  <a href="https://www.npmjs.com/package/@cfdi/xml">
    <img src="https://raw.githubusercontent.com/MisaelMa/cards/main/packages/cfdi-xml.png" alt="@cfdi/xml" width="400" />
  </a>
</p>

<h3 align="center">Librerias modernas para trabajar con CFDI 4.0 en Node.js</h3>

<p align="center">
  <a href="https://www.npmjs.com/package/@cfdi/xml">
    <img src="https://img.shields.io/npm/v/@cfdi/xml?style=flat-square&color=cb3837&label=npm" alt="npm version" />
  </a>
  <a href="https://www.npmjs.com/package/@cfdi/xml">
    <img src="https://img.shields.io/npm/dm/@cfdi/xml?style=flat-square&color=cb3837&label=downloads" alt="npm downloads" />
  </a>
  <a href="https://github.com/MisaelMa/node-cfdi/blob/main/LICENSE">
    <img src="https://img.shields.io/badge/license-MIT-blue?style=flat-square" alt="license" />
  </a>
  <img src="https://img.shields.io/badge/node-%3E%3D22-brightgreen?style=flat-square&logo=node.js&logoColor=white" alt="node" />
  <img src="https://img.shields.io/badge/TypeScript-strict-3178c6?style=flat-square&logo=typescript&logoColor=white" alt="typescript" />
</p>

<p align="center">
  <a href="https://cfdi.recreando.dev">
    <img src="https://raw.githubusercontent.com/MisaelMa/cards/main/cfdi-documentacion.png" alt="Documentacion" width="300" />
  </a>
</p>

---

## Ecosistema CFDI

<p align="center">
  <img src="https://raw.githubusercontent.com/MisaelMa/cards/main/cfdi-ecosystem.png" alt="CFDI Ecosystem" width="600" />
</p>

<table>
  <tr>
    <td align="center" width="33%">
      <a href="https://www.npmjs.com/package/@cfdi/complementos">
        <img src="https://raw.githubusercontent.com/MisaelMa/cards/main/packages/cfdi-complementos.png" alt="@cfdi/complementos" width="100%" />
      </a>
    </td>
    <td align="center" width="33%">
      <a href="https://www.npmjs.com/package/@cfdi/xsd">
        <img src="https://raw.githubusercontent.com/MisaelMa/cards/main/packages/cfdi-xsd.png" alt="@cfdi/xsd" width="100%" />
      </a>
    </td>
    <td align="center" width="33%">
      <a href="https://www.npmjs.com/package/@cfdi/csd">
        <img src="https://raw.githubusercontent.com/MisaelMa/cards/main/packages/cfdi-csd.png" alt="@cfdi/csd" width="100%" />
      </a>
    </td>
  </tr>
  <tr>
    <td align="center" width="33%">
      <a href="https://www.npmjs.com/package/@cfdi/csf">
        <img src="https://raw.githubusercontent.com/MisaelMa/cards/main/packages/cfdi-csf.png" alt="@cfdi/csf" width="100%" />
      </a>
    </td>
    <td align="center" width="33%">
      <a href="https://www.npmjs.com/package/@cfdi/catalogos">
        <img src="https://raw.githubusercontent.com/MisaelMa/cards/main/packages/cfdi-catalogos.png" alt="@cfdi/catalogos" width="100%" />
      </a>
    </td>
    <td align="center" width="33%">
      <a href="https://www.npmjs.com/package/@cfdi/transform">
        <img src="https://raw.githubusercontent.com/MisaelMa/cards/main/packages/cfdi-transform.png" alt="@cfdi/transform" width="100%" />
      </a>
    </td>
  </tr>
  <tr>
    <td align="center" width="33%">
      <a href="https://www.npmjs.com/package/@cfdi/elements">
        <img src="https://raw.githubusercontent.com/MisaelMa/cards/main/packages/cfdi-elements.png" alt="@cfdi/elements" width="100%" />
      </a>
    </td>
    <td align="center" width="33%">
      <a href="https://www.npmjs.com/package/@cfdi/types">
        <img src="https://raw.githubusercontent.com/MisaelMa/cards/main/packages/cfdi-types.png" alt="@cfdi/types" width="100%" />
      </a>
    </td>
    <td align="center" width="33%">
      <a href="https://www.npmjs.com/package/@cfdi/expresiones">
        <img src="https://raw.githubusercontent.com/MisaelMa/cards/main/packages/cfdi-expresiones.png" alt="@cfdi/expresiones" width="100%" />
      </a>
    </td>
  </tr>
  <tr>
    <td align="center" width="33%">
      <a href="https://www.npmjs.com/package/@cfdi/xml2json">
        <img src="https://raw.githubusercontent.com/MisaelMa/cards/main/packages/cfdi-2json.png" alt="@cfdi/xml2json" width="100%" />
      </a>
    </td>
    <td align="center" width="33%">
      <a href="https://www.npmjs.com/package/@cfdi/rfc">
        <img src="https://raw.githubusercontent.com/MisaelMa/cards/main/packages/cfdi-rfc.png" alt="@cfdi/rfc" width="100%" />
      </a>
    </td>
    <td align="center" width="33%">
      <a href="https://www.npmjs.com/package/@cfdi/utils">
        <img src="https://raw.githubusercontent.com/MisaelMa/cards/main/packages/cfdi-utils.png" alt="@cfdi/utils" width="100%" />
      </a>
    </td>
  </tr>
  <tr>
    <td align="center" width="33%">
      <a href="https://www.npmjs.com/package/@clir/openssl">
        <img src="https://raw.githubusercontent.com/MisaelMa/cards/main/packages/clir-openssl.png" alt="@clir/openssl" width="100%" />
      </a>
    </td>
    <td align="center" width="33%">
      <a href="https://www.npmjs.com/package/@saxon-he/cli">
        <img src="https://raw.githubusercontent.com/MisaelMa/cards/main/packages/saxon-he-cli.png" alt="@saxon-he/cli" width="100%" />
      </a>
    </td>
    <td align="center" width="33%">
    </td>
  </tr>
</table>

---

## Por que @cfdi/xml?

<table>
  <tr>
    <td width="25%" align="center"><b>Sin Java</b><br/><sub>Motor XSLT nativo en Node.js con <code>@cfdi/transform</code>. Saxon-HE disponible como alternativa.</sub></td>
    <td width="25%" align="center"><b>TypeScript nativo</b><br/><sub>Tipos estrictos, autocompletado y seguridad de tipos en todo el flujo de facturacion.</sub></td>
    <td width="25%" align="center"><b>SAT 4.0 compliant</b><br/><sub>Cumple con Anexo 20 del SAT. Genera XML valido contra esquemas XSD oficiales.</sub></td>
    <td width="25%" align="center"><b>Modular</b><br/><sub>Usa solo lo que necesitas. Cada paquete del ecosistema es independiente.</sub></td>
  </tr>
</table>

---

## Quick Start

### 1. Instalar

```bash
npm install @cfdi/xml
```

### 2. Crear y firmar un CFDI

```ts
import { CFDI, Emisor, Receptor, Concepto, Impuestos } from '@cfdi/xml';

const cfdi = new CFDI({
  xslt: { path: './resources/4.0/cadenaoriginal.xslt' },
});

cfdi.comprobante({
  Serie: 'A',
  Folio: '1',
  Fecha: '2024-01-15T10:30:00',
  FormaPago: '01',
  MetodoPago: 'PUE',
  TipoDeComprobante: 'I',
  LugarExpedicion: '64000',
  Moneda: 'MXN',
  SubTotal: '1000.00',
  Total: '1160.00',
});

cfdi.emisor(
  new Emisor({
    Rfc: 'AAA010101AAA',
    Nombre: 'Empresa SA de CV',
    RegimenFiscal: '601',
  })
);

cfdi.receptor(
  new Receptor({
    Rfc: 'BBB020202BBB',
    Nombre: 'Cliente SA de CV',
    UsoCFDI: 'G03',
    DomicilioFiscalReceptor: '64000',
    RegimenFiscalReceptor: '601',
  })
);

const concepto = new Concepto({
  ClaveProdServ: '01010101',
  Cantidad: '1',
  ClaveUnidad: 'E48',
  Descripcion: 'Servicio de consultoria',
  ValorUnitario: '1000.00',
  Importe: '1000.00',
  ObjetoImp: '02',
});
concepto.setTraslado({
  Base: '1000.00',
  Impuesto: '002',
  TipoFactor: 'Tasa',
  TasaOCuota: '0.160000',
  Importe: '160.00',
});
cfdi.concepto(concepto);

const impuestos = new Impuestos({
  TotalImpuestosTrasladados: '160.00',
});
impuestos.traslados({
  Impuesto: '002',
  TipoFactor: 'Tasa',
  TasaOCuota: '0.160000',
  Importe: '160.00',
  Base: '1000.00',
});
cfdi.impuesto(impuestos);
```

### 3. Certificar y sellar

```ts
cfdi.certificar('./certs/certificado.cer');
await cfdi.sellar('./certs/llave.key', '12345678a');
```

### 4. Obtener el XML

```ts
const xml = cfdi.getXmlCdfi();
```

---

## Flujo de firmado

```mermaid
graph LR
    A[XML CFDI] --> B[Cadena Original]
    B --> C[Sello Digital]
    C --> D[CFDI Firmado]

    B -- "@cfdi/transform (default)" --> B
    B -. "@saxon-he/cli (opcional)" .-> B
    C -- ".key + SHA-256" --> C
    A -- ".cer" --> D
```

---

## Output de ejemplo

```xml
<?xml version="1.0" encoding="utf-8"?>
<cfdi:Comprobante
  xmlns:cfdi="http://www.sat.gob.mx/cfd/4"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.sat.gob.mx/cfd/4 http://www.sat.gob.mx/sitio_internet/cfd/4/cfdv40.xsd"
  Version="4.0"
  Serie="A"
  Folio="1"
  Fecha="2024-01-15T10:30:00"
  FormaPago="01"
  MetodoPago="PUE"
  TipoDeComprobante="I"
  Moneda="MXN"
  SubTotal="1000.00"
  Total="1160.00"
  LugarExpedicion="64000"
  NoCertificado="20001000000300022815"
  Certificado="MIIFxTCCA62g..."
  Sello="GshKsM2IjAR0+7...">
  <cfdi:Emisor Rfc="AAA010101AAA" Nombre="Empresa SA de CV" RegimenFiscal="601"/>
  <cfdi:Receptor Rfc="BBB020202BBB" Nombre="Cliente SA de CV" UsoCFDI="G03"
    DomicilioFiscalReceptor="64000" RegimenFiscalReceptor="601"/>
  <cfdi:Conceptos>
    <cfdi:Concepto ClaveProdServ="01010101" Cantidad="1" ClaveUnidad="E48"
      Descripcion="Servicio de consultoria" ValorUnitario="1000.00"
      Importe="1000.00" ObjetoImp="02">
      <cfdi:Impuestos>
        <cfdi:Traslados>
          <cfdi:Traslado Base="1000.00" Impuesto="002" TipoFactor="Tasa"
            TasaOCuota="0.160000" Importe="160.00"/>
        </cfdi:Traslados>
      </cfdi:Impuestos>
    </cfdi:Concepto>
  </cfdi:Conceptos>
</cfdi:Comprobante>
```

---

## Motor XSLT

La cadena original se genera mediante transformacion XSLT. Se soportan dos motores:

| Motor               | Paquete           | Requiere Java | Default |
| ------------------- | ----------------- | ------------- | ------- |
| **@cfdi/transform** | `@cfdi/transform` | No            | Si      |
| **Saxon-HE**        | `@saxon-he/cli`   | Si            | No      |

```ts
// Default: @cfdi/transform (pure Node.js)
const cfdi = new CFDI({
  xslt: { path: './resources/4.0/cadenaoriginal.xslt' },
});

// Saxon-HE: requiere Java + Saxon instalado
const cfdiSaxon = new CFDI({
  xslt: { path: './resources/4.0/cadenaoriginal.xslt' },
  saxon: { binary: 'transform' },
});
```

---

## API

| Metodo                       | Descripcion                                           |
| ---------------------------- | ----------------------------------------------------- |
| `comprobante(attr)`          | Atributos del comprobante (Serie, Folio, Fecha, etc.) |
| `emisor(emisor)`             | Datos del emisor                                      |
| `receptor(receptor)`         | Datos del receptor                                    |
| `concepto(concepto)`         | Agrega un concepto (puede llamarse multiples veces)   |
| `impuesto(impuesto)`         | Resumen de impuestos del documento                    |
| `certificar(cerpath)`        | Carga certificado `.cer`                              |
| `sellar(keyfile, password)`  | Genera cadena original y sello digital                |
| `getXmlCdfi()`               | Retorna el XML firmado                                |
| `getJsonCdfi()`              | Retorna representacion JSON                           |
| `saveFile(file, path, name)` | Guarda archivo XML                                    |
| `generarCadenaOriginal()`    | Genera cadena original via XSLT                       |
| `setDebug(debug)`            | Activa/desactiva modo debug                           |

---

## Configuracion

```ts
interface Config {
  debug?: boolean;
  compact?: boolean;
  xslt?: { path: string }; // Ruta al archivo XSLT (requerido para sellar)
  saxon?: { binary: string }; // Si se omite, usa @cfdi/transform
  schema?: { path: string };
}
```

---

## Soporte

<p>
  <a href="https://github.com/MisaelMa/node-cfdi/issues">
    <img src="https://img.shields.io/badge/GitHub-Issues-181717?style=for-the-badge&logo=github" alt="issues" />
  </a>
  <a href="https://github.com/MisaelMa/node-cfdi/discussions">
    <img src="https://img.shields.io/badge/GitHub-Discussions-181717?style=for-the-badge&logo=github" alt="discussions" />
  </a>
  <a href="https://www.npmjs.com/package/@cfdi/xml">
    <img src="https://img.shields.io/badge/npm-@cfdi/xml-cb3837?style=for-the-badge&logo=npm" alt="npm" />
  </a>
</p>

---

## Autor

<p align="center">
  <a href="https://github.com/MisaelMa">
    <img src="https://raw.githubusercontent.com/MisaelMa/cards/main/author.png" alt="Amir Misael Marin Coh" width="100%" />
  </a>
</p>

## Licencia

[MIT](../../LICENSE)
