Metadata-Version: 2.4
Name: nodda-client
Version: 1.2.1
Summary: Cliente oficial de la API de Nodda para emitir y consultar facturas VERI*FACTU (AEAT).
Author-email: Nodda <soporte@nodda.es>
License: MIT
Project-URL: Documentacion, https://app.nodda.es/developers/introduccion
Project-URL: Repositorio, https://gitlab.com/Bitfx/nodda-facturacion/nodda-cliente
Keywords: verifactu,aeat,facturacion,factura,nodda
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: OS Independent
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# nodda-client (Python)

Cliente oficial de la API de [Nodda](https://nodda.es) para emitir y consultar facturas **VERI\*FACTU** de la Agencia Tributaria (AEAT) desde tu propio programa (ERP, tienda online, TPV…).

Nodda se encarga de la firma con tu certificado, del XML, de la cadena de huellas y del envío a la AEAT: tú solo mandas los datos de la factura en JSON.

## Instalación

```bash
pip install nodda-client
```

Requisitos: Python 3.8 o superior. Sin dependencias (solo la librería estándar).

## Antes de empezar

1. Crea tu cuenta en el portal de Nodda y da de alta tu empresa (NIF y certificado digital).
2. Genera tu **clave API** en el portal → *API keys*.
3. Usa la dirección del entorno con su clave:

| Entorno | Dirección | Uso |
|---|---|---|
| Pruebas | `https://sandbox.nodda.es` | Envía al entorno de pruebas de la AEAT, sin validez fiscal |
| Producción | `https://api.nodda.es` | Facturas reales |

La clave de pruebas no sirve en producción, ni al revés.

## Uso

```python
import os
from nodda_client import NoddaClient

nodda = NoddaClient("https://sandbox.nodda.es", os.environ["NODDA_API_KEY"])

r = nodda.crear_factura({
    "series": "2026", "number": "0001", "issueDate": "2026-11-03", "invoiceType": "F1",
    "description": "Servicios de consultoría", "taxRegimeKey": "01", "operationQualification": "S1",
    "taxBase": "100.00", "taxRate": "21", "taxAmount": "21.00", "totalAmount": "121.00",
    "recipients": [{"nif": "B12345674", "name": "Cliente Demo SL"}],
})
# r["status"] == "queued": la API responde al momento; la AEAT, unos segundos después.

estado = nodda.consultar_estado(r["id"])  # queued, sent, accepted_with_errors o permanently_failed
```

`number` es el número de la factura dentro de su serie (sin huecos ni repeticiones). Puedes omitirlo si la serie tiene numeración automática configurada (en el portal, Empresas → series de numeración, o con `PUT /v1/emisores/{id}/series`); si no, la API responde `series_not_configured`.

Si tu clave da acceso a varias empresas (NIF), indica cuál en el parámetro `nif` de cada método. Puedes asociar la factura a un centro de coste enviando `costCenterId` en los datos.

## Métodos

| Método | Endpoint |
|---|---|
| `crear_factura(datos, nif=None)` | `POST /v1/invoices` |
| `consultar_estado(id, nif=None)` | `GET /v1/invoices/{id}` |
| `listar_facturas(filtros=None, nif=None)` | `GET /v1/invoices` |
| `cancelar_factura(id, datos=None, nif=None)` | `POST /v1/invoices/{id}/cancel` |
| `reintentar_factura(id, tipo=None, nif=None)` | `POST /v1/invoices/{id}/retry` |
| `obtener_url_qr(id)` / `obtener_imagen_qr(id)` | `GET /v1/invoices/{id}/qr` |
| `estado_sistema(nif=None)` | `GET /v1/status` |
| `obtener_perfil()` | `GET /v1/client` |
| `configurar_webhook(url, nif=None)` / `quitar_webhook(nif=None)` | `PUT` / `DELETE /v1/webhooks` |
| `NoddaClient.verificar_firma_webhook(cuerpo, firma, secreto)` | Comprueba la firma de un aviso |

## Avisos (webhooks)

En lugar de consultar el estado de cada factura, Nodda puede avisar a tu servidor cuando la AEAT responde. Configura la URL con `configurar_webhook(url)` (la respuesta trae el `secreto` **una sola vez**: guárdalo) y, en tu endpoint, comprueba la firma antes de confiar en el aviso:

```python
# cuerpo: bytes tal cual llegan; firma: cabecera X-Nodda-Signature
if not NoddaClient.verificar_firma_webhook(cuerpo, firma, os.environ["NODDA_WEBHOOK_SECRET"]):
    return 401
```

El cuerpo trae `id` (número de factura), `status` (`sent`, `accepted_with_errors` o `permanently_failed`), `razon`, `eventId` y los datos del acuse de la AEAT. Responde con un código 2xx; si no, Nodda lo reintenta.

## Errores

Si la API responde con un error, se lanza `NoddaApiException` con el código HTTP y un código estable (por ejemplo `invalid_request`, `business_rule_violation`, `certificate_not_configured`). Si no hubo respuesta (fallo de red), se lanza `NoddaException`: puedes reintentar la misma petición.

## Documentación

Guía completa, campos de cada petición y códigos de error: <https://app.nodda.es/developers/introduccion>
Referencia OpenAPI: <https://api.nodda.es/openapi.json>

Soporte: soporte@nodda.es

## Licencia

MIT
