Download the PHP package azur/sdk without Composer
On this page you can find all versions of the php package azur/sdk. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Package sdk
Short Description SDK oficial de PHP para la API de AZUR: facturación electrónica del SRI de Ecuador (comprobantes, catálogos, inventario, cartera, webhooks y emisión directa).
License MIT
Homepage https://azur.com.ec/api
Informations about the package sdk
SDK oficial de PHP para la API de AZUR
Facturación electrónica del SRI de Ecuador desde PHP: comprobantes (factura, nota de crédito y débito, guía, retención, liquidación, compra, proforma, nota de venta), clientes, productos, inventario, cartera, contabilidad, reportes (ATS), comprobantes recibidos y webhooks. También incluye la emisión directa de siempre (API clásica con api_key).
- PHP 8.1+, una sola dependencia: Guzzle 7.
- Reintentos con backoff exponencial y jitter,
Idempotency-Keyautomática en las escrituras. - Excepciones tipadas en español, iteradores para paginar y para sincronizar por cursor.
- Verificación de la firma de los webhooks en tiempo constante.
Índice
- Instalación
- La credencial
- Primeros pasos
- Comprobantes
- Emisión directa (API clásica)
- Catálogos, inventario, cartera y demás
- Paginación y sincronización
- Webhooks
- Errores
- Reintentos e idempotencia
- Opciones del cliente
- Pruebas
Instalación
La credencial
La API v2 usa una credencial que crea el dueño de la cuenta en el portal: menú de usuario → Credenciales API. Al crearla elige el punto de emisión, los permisos (por ejemplo solo «Consultar comprobantes»), las IP autorizadas y un vencimiento opcional. El secreto se muestra una sola vez.
| Prefijo | Ambiente |
|---|---|
azur_live_… |
Producción (efecto tributario real) |
azur_test_… |
Pruebas del SRI |
El ambiente lo fija la credencial, no la llamada: es imposible emitir en producción con una credencial de pruebas. Guárdela en una variable de entorno, nunca en el código:
La emisión directa usa otra clave: el
api_keydel punto de emisión (API_…). Vea Emisión directa.
Primeros pasos
Cada método devuelve una Azur\Sdk\Respuesta, que se usa como un array de solo lectura sobre datos, y además trae:
| Propiedad / método | Qué es |
|---|---|
$r['campo'], $r->get('a.b') |
Lectura de datos (con notación de puntos en get) |
$r->datos |
El array completo |
$r->avisos |
Lo que conviene saber aunque saliera bien (algo se ignoró, la lista se limitó a 60 días…) |
$r->lista() |
La lista principal de un listado (facturas, clientes…) |
$r->limiteRestante() |
Llamadas que quedan en este minuto |
Comprobantes
Recursos por tipo: facturas, creditos, debitos, guias, retenciones, liquidaciones, compras, proformas, notasVenta. Todos comparten estos métodos (el servidor decide cuáles aplican a cada tipo: la nota de débito y la retención no se editan; compra, proforma y nota de venta no van al SRI):
| Método | Endpoint |
|---|---|
guardar($datos) / crear($datos) |
POST /{tipo}/guardar como borrador |
crearYEnviar($datos) |
POST /{tipo}/guardar con accion: enviar (sale al SRI) |
obtener($id) |
GET /{tipo}/{id} |
editar($id, $datos) |
PUT /{tipo}/{id} (solo borradores) |
eliminar($id) |
DELETE /{tipo}/{id} |
listar($filtros) / todos($filtros) |
GET /{plural} |
sincronizar($filtros) |
GET /{plural}?modificado_desde=… con cursor |
enviar($id) |
POST /{tipo}/{id}/enviar |
estado($id, $consultarSri = false) |
GET /{tipo}/{id}/estado |
anular($id) |
POST /{tipo}/{id}/anular |
reprocesar($id) |
POST /{tipo}/{id}/reprocesar |
correo($id, $correo = null) |
POST /{tipo}/{id}/correo |
pdf($id) / xml($id, 'autorizado') |
GET /{tipo}/{id}/pdf|xml → Archivo |
Extras: facturas->saldoAcreditable($id), saldoAcreditablePorNumero($numero), lineasAcreditables($id), cobros($id), cobrar($id, $datos); compras->retencionSugerida($id), pagos($id), pagar($id, $datos); liquidaciones->pagos/pagar; notasVenta->cobros/cobrar.
Crear una factura borrador
Cuando esté revisado: $azur->facturas->enviar($f['id']) y luego $azur->facturas->estado($f['id']) (o espere el webhook).
Listado unificado
Sin
desde,hastanibuscar, los listados de comprobantes se limitan a los últimos 60 días (lo dicedatos.ventanay un aviso).
Emisión directa (API clásica)
La API de siempre para emitir en una sola llamada (genera el XML, firma y envía al SRI). Se autentica con el api_key del punto de emisión (API_…, en Configuración → Puntos de emisión); el ambiente lo define ese punto.
Si no los manda, el SDK pone codigoDoc, emisor.fecha_emision (hoy, hora de Ecuador) y emisor.manejo_interno_secuencia = "SI" (AZUR asigna el secuencial sin duplicados).
| Método | Endpoint |
|---|---|
factura, notaCredito, notaDebito, retencion20 (retención 2.0, recomendada), retencion (1.0, obsoleta), liquidacion, guia (o emitir($tipo, $datos)) |
POST /{tipo}/emision (retencion20 → /retencionats/emision) |
consultar($claveAcceso) |
POST /consulta/comprobante |
cupo($usuarioPrincipal) |
POST /consultar/comprobantes (usuario del dueño de la cuenta) |
reenviarCorreo($claveAcceso, $motivo) |
POST /enviar/correo |
marcarPagada($claveAcceso, true) |
POST /factura/pagada |
guardarCliente($comprador) |
POST /clientes/nuevo |
crearProductos($items) / actualizarProductos($items) |
POST /crear/productos, /actualizar/productos |
stock($establecimiento, $codigo, $idBodega) |
POST /productos/obtenerstock |
La API clásica responde HTTP 200 también cuando rechaza ({"creado": false, "errors": …}). El SDK lo normaliza: lanza Validacion, CredencialInvalida, CupoAgotado o Conflicto (secuencial ocupado). Con 'lanzar_excepciones' => false devuelve la RespuestaClasica con exito, errores y datos.
Reintentos en la emisión clásica: no admite
Idempotency-Key, así que una emisión solo se reintenta ante un 429 (el limitador la rechazó antes de procesarla). Ante un corte de red o un 5xx no se reintenta: pudo haberse emitido. Consulte antes de volver a emitir.
Catálogos, inventario, cartera y demás
¿Un endpoint que el SDK aún no envuelve? $azur->solicitar('GET', 'ruta/nueva', ['param' => 1]).
Paginación y sincronización
sincronizar() (en comprobantes, cada tipo de documento y recibidos) filtra por fecha de modificación y sigue siguiente_cursor solo. Es lo correcto para sincronizar: una factura antigua que se anula hoy aparece; paginar por número sobre una tabla que crece se descoloca. proximo_desde es la hora de Ecuador en que empezó la sincronización menos 60 s de margen.
Webhooks
Eventos: comprobante.autorizado, comprobante.rechazado, comprobante.anulado, plan.cupo_bajo.
Cada aviso trae X-Azur-Firma = hex(HMAC-SHA256(secreto, X-Azur-Momento + "." + cuerpo)), X-Azur-Momento (Unix), X-Azur-Evento y X-Azur-Intento. Webhook::verificar() comprueba la firma en tiempo constante y que el momento esté dentro de la tolerancia (300 s por defecto, contra reenvíos). Use el cuerpo crudo: si lo decodifica y lo vuelve a codificar, la firma no coincide.
Laravel
La ruta debe quedar fuera de la verificación CSRF (las de routes/api.php ya lo están).
PHP puro
AZUR reintenta a los 1, 5 y 15 minutos si no responde 2xx, así que un aviso puede llegar más de una vez: procéselo de forma idempotente (por claveacceso + evento). Para probar su receptor: Webhook::cabecerasDePrueba($cuerpo, $secreto, 'comprobante.autorizado').
Errores
Todas las excepciones heredan de Azur\Sdk\Excepciones\AzurException y traen getCodigo() (estable, para el programa), getMessage() (para la persona), getDetalle(), esReintentable(), getEstadoHttp(), getRequestId(), getCuerpo() y getIdempotencyKey().
| Excepción | HTTP | Códigos de AZUR |
|---|---|---|
CredencialInvalida |
401 | credencial_invalida |
SinPermiso |
403 | sin_permiso (permiso de la credencial o IP no autorizada) |
NoEncontrado |
404 | no_encontrado |
Validacion |
422 | datos_invalidos — getMotivos() con la lista de motivos |
CupoAgotado |
402 | cupo_agotado |
Conflicto |
409, 300 | secuencial_ocupado, documento_no_editable, establecimiento_cerrado, firma_caducada, no_disponible, anulacion_no_solicitada, anulacion_fuera_de_plazo, ambiguo |
LimiteExcedido |
429 | demasiadas_peticiones — getReintentarEn() |
ErrorServidor |
5xx | error_interno, sri_no_disponible, o respuesta no interpretable |
ErrorRed |
— | error_red: DNS, conexión, TLS, tiempo agotado |
FirmaWebhookInvalida |
— | firma_ausente, firma_invalida, momento_fuera_de_tolerancia, momento_invalido, cuerpo_invalido |
Reintentos e idempotencia
- Cada escritura (POST, PUT, DELETE) lleva una
Idempotency-Keynueva (UUID v4), la misma en los reintentos automáticos de esa llamada. Si se corta la conexión y se reintenta, AZUR devuelve el resultado original en vez de crear otro documento (la recuerda 24 h). - Pase la suya para poder reintentar usted mismo más tarde (por ejemplo desde una cola):
['idempotency_key' => 'pedido-1234']. Confalseno se manda ninguna. - Se reintenta, con backoff exponencial + jitter (
espera_base · 2^n, topeespera_maxima):- 429 siempre, respetando
Retry-After(lo da el limitador antes de procesar nada). Si pide esperar más queespera_maxima, se lanzaLimiteExcedido. - 502, 503, 504 y errores de red solo en lecturas y en escrituras con
Idempotency-Key. Nunca una escritura sin llave.
- 429 siempre, respetando
- Límites de la API v2: 300 lecturas y 60 escrituras por minuto por credencial.
Opciones del cliente
Emision acepta las mismas más clave_en (cuerpo|cabecera) y lanzar_excepciones; su timeout por defecto es 60 s.
Pruebas
tests/Unit/CoberturaOpenApiTest.php comprueba que cada operación pública del OpenAPI (tests/fixtures/operaciones-openapi.json, sacado de GET /plataforma/api/v2/openapi) tiene su método y llama al método HTTP y la ruta correctos.
Documentación de la API: https://azur.com.ec/api · OpenAPI: https://azur.com.ec/plataforma/api/v2/openapi
Licencia
MIT. Vea LICENSE.