facturalo-pe / facturalo-php
SDK oficial de Facturalo para PHP: emite facturas, boletas, notas y guías de remisión ante SUNAT, consulta RUC/DNI y verifica webhooks.
Requires
- php: ^8.1
- guzzlehttp/guzzle: ^7.8
Requires (Dev)
- phpunit/phpunit: ^10.5
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
SDK oficial de Facturalo para PHP. Emite facturas, boletas, notas de crédito y débito y guías de remisión ante SUNAT, consulta RUC y DNI, trabaja el SIRE y verifica los webhooks. Funciona en cualquier proyecto PHP 8.1+, incluido Laravel.
Documentación completa de la API: docs.facturalo.pe.
composer require facturalo-pe/facturalo-php
Primera factura
Crea tu empresa en console.facturalo.pe y copia su token Demo (sk_test_...) desde el menú de la empresa, opción Tokens. El token decide el entorno: sk_test_... va a SUNAT beta y sk_live_... a producción.
use Facturalo\FacturaloClient; $facturalo = new FacturaloClient(getenv('FACTURALO_TOKEN')); $envio = $facturalo->documentos->enviar([ 'sunatTransaccion' => '0101', 'tipoComprobante' => '01', 'serie' => 'F001', 'correlativo' => '1', 'fechaEmision' => '2026-10-30', 'moneda' => 'PEN', 'formaPago' => 'CONTADO', 'cliente' => [ 'tipoDocumento' => '6', 'numeroDocumento' => '20000000001', 'denominacion' => 'EMPRESA S.A.C.', 'direccion' => 'AV. LIMA 123 - LIMA', ], 'items' => [ // precioUnitario incluye IGV; Facturalo calcula el IGV y los totales. ['unidadMedida' => 'NIU', 'descripcion' => 'SERVICIO DE CONSULTORÍA', 'cantidad' => 1, 'precioUnitario' => 118, 'tipoIgv' => '10'], ], ]); if ($envio['estado'] === 'ACEPTADA') { echo $envio['enlaces']['pdf']; } else { echo $envio['estado'] . ': ' . ($envio['sunat']['mensaje'] ?? ''); }
Un rechazo de SUNAT no lanza excepción: llega con estado RECHAZADA y el motivo en sunat. Las excepciones son para errores de la petición (validación, token, red).
La respuesta solo identifica el comprobante. El detalle completo (cliente, totales, ítems) se obtiene con $facturalo->documentos->consultar($id).
Sin esperar a SUNAT
$envio = $facturalo->documentos->enviar($documento, webhook: true); // $envio['estado'] === 'EN_PROCESO': el resultado llega a tu webhook como documento.sunat.respuesta
Recursos
// Documentos (factura, boleta, notas) $facturalo->documentos->enviar($documento, webhook: false, establecimiento: null); $facturalo->documentos->generarXml($documento); // firma el XML sin enviarlo $facturalo->documentos->consultar($id); // detalle completo + estado $facturalo->documentos->buscar('01', 'F001', '1'); $facturalo->documentos->listar(['fechaInicio' => '2026-10-01', 'estado' => 'ACEPTADA', 'pagina' => 1]); $facturalo->documentos->anular('01', 'F001', '1', 'Error en el monto'); $facturalo->documentos->reenviar($id); $facturalo->documentos->eventos($id); $facturalo->documentos->eliminar($id); // solo NO_ENVIADA file_put_contents('F001-1.pdf', $facturalo->documentos->descargar($id, 'pdf')); // pdf | ticket | xml | cdr // Guías de remisión (serie T... remitente, V... transportista) $facturalo->guias->enviar($guia, webhook: false); $facturalo->guias->consultar($id); $facturalo->guias->consultarTicket($ticket); $facturalo->guias->listar(['pagina' => 1]); // Consultas $facturalo->consultas->ruc('20601476551'); $facturalo->consultas->dni('12345678'); $facturalo->consultas->tipoCambio('2026-10-30'); // Webhooks enviados $facturalo->webhooks->listar(['estado' => 'error']); $facturalo->webhooks->reenviar($eventoId); // SIRE (periodo YYYYMM) $facturalo->sire->ventas->resumen('202610'); $facturalo->sire->compras->comprobantes('202610', ['page' => 1, 'perPage' => 100]);
Recibir webhooks
Facturalo firma cada webhook con hash_hmac('sha256', $cuerpo, $secret) en el header X-Facturalo-Firma. Verifica siempre con el cuerpo crudo:
use Facturalo\Exception\FacturaloSignatureVerificationException; use Facturalo\Webhook; // Laravel Route::post('/webhooks/facturalo', function (Request $request) { try { $evento = Webhook::construirEvento( $request->getContent(), $request->header('X-Facturalo-Firma'), config('services.facturalo.webhook_secret'), ); } catch (FacturaloSignatureVerificationException) { abort(401); } if ($evento['evento'] === 'documento.sunat.respuesta') { // $evento['documento']['id'], $evento['sunat']['estado'], $evento['enlaces']['pdf']... } return response()->noContent(); });
En PHP sin framework usa file_get_contents('php://input') y $_SERVER['HTTP_X_FACTURALO_FIRMA']. El secreto (whsec_...) está en la consola, el menú de la empresa, opción Webhooks, y es distinto en Demo y Live.
Cuenta: empresas y establecimientos
Con el token de usuario (user_sk_...) puedes dar de alta empresas desde tu propio software:
use Facturalo\FacturaloCuentaClient; $cuenta = new FacturaloCuentaClient(getenv('FACTURALO_USER_TOKEN')); $empresa = $cuenta->empresas->crear([ 'ruc' => $ruc, 'nombre' => $nombre, 'direccion' => $direccion, 'departamento' => $departamento, 'provincia' => $provincia, 'distrito' => $distrito, 'codigoPostal' => $ubigeo, 'sunat' => ['solUsuario' => $usuarioSol, 'solClave' => $claveSol], ]); $cuenta->empresas->actualizarWebhook($empresa['id'], ['demo' => 'https://mi-sistema.pe/webhooks/facturalo']);
Errores
| Excepción | Cuándo |
|---|---|
FacturaloValidationException |
400 / 422. getErrors() trae los errores por campo |
FacturaloAuthenticationException |
401: token inválido o regenerado |
FacturaloNotFoundException |
404: no existe en este entorno |
FacturaloRateLimitException |
429 |
FacturaloConnectionException |
Red o timeout (default 90 s) |
FacturaloApiException |
Clase base de las HTTP, con getStatus() y getBody() |
Las consultas (GET) se reintentan solas ante 429/5xx y fallos de red. Los envíos nunca se reintentan automáticamente, para no emitir dos veces el mismo comprobante.
Licencia
MIT © Consultia Digital S.A.C. (Facturalo)