defontana / sdk
SDK PHP no oficial para el API de Defontana (ERP). Cubre Sales, Purchase, Inventory, Accounting, Order, PurchaseOrder y más.
Requires
- php: >=8.1
- ext-curl: *
- ext-json: *
Requires (Dev)
- phpunit/phpunit: ^10.0
- vlucas/phpdotenv: ^5.6
README
SDK no oficial para el API público de Defontana (ERP utilizado por miles de empresas chilenas para facturación electrónica, ventas, compras, inventario y contabilidad).
- 241 endpoints organizados en 14 servicios tipados.
- 222 DTOs tipados para todos los request bodies (con sub-DTOs y arrays de DTOs).
- Autenticación JWT automática (login + refresh + retry en 401).
- Cero dependencias en runtime (sólo
ext-curlyext-json).
Cobertura por servicio:
| Servicio | Endpoints |
|---|---|
auth() |
4 (+ utilidades de sesión) |
accounting() |
38 |
company() |
1 |
dispatch() |
3 |
environment() |
1 |
inventory() |
13 |
order() |
11 |
payment() |
3 |
paySheet() |
5 |
pricing() |
4 |
purchase() |
16 |
purchaseOrder() |
23 |
sale() |
113 |
subscription() |
4 |
user() |
2 |
Requisitos
- PHP 8.1+
- Extensiones:
curl,json - Credenciales del API de Defontana (cliente, empresa, usuario, password)
Instalación
composer require defontana/sdk
Si todavía no está publicado en Packagist, puedes apuntar al repositorio local en tu composer.json:
{
"repositories": [
{ "type": "path", "url": "../defontana-sdk" }
],
"require": {
"defontana/sdk": "*"
}
}
Quickstart
<?php require __DIR__ . '/vendor/autoload.php'; use Defontana\Sdk\Client; use Defontana\Sdk\Config; use Defontana\Sdk\Exception\ApiException; use Defontana\Sdk\Exception\AuthException; $config = new Config( client: getenv('DEFONTANA_CLIENT'), company: getenv('DEFONTANA_COMPANY'), user: getenv('DEFONTANA_USER'), password: getenv('DEFONTANA_PASSWORD'), ); $defontana = new Client($config); try { // Login implícito en la primera llamada autenticada $empresa = $defontana->company()->list(); echo "Empresa: {$empresa['name']}\n"; // Listar clientes (paginado) $clientes = $defontana->sale()->getClients( status: 1, itemsPerPage: 50, pageNumber: 1, ); foreach ($clientes['clients'] ?? [] as $c) { echo "{$c['legalCode']} — {$c['name']}\n"; } } catch (AuthException $e) { fwrite(STDERR, "Error de autenticación: {$e->getMessage()}\n"); } catch (ApiException $e) { fwrite(STDERR, "Error de API ({$e->statusCode}): {$e->getMessage()}\n"); }
Autenticación
El SDK obtiene el token JWT automáticamente la primera vez que llamas un endpoint protegido, y se re-autentica solo cuando el token expira (o si el API devuelve 401).
Login manual
$defontana->auth()->login(); // refresca el token de inmediato $token = $defontana->auth()->getAccessToken(); $exp = $defontana->auth()->getExpiresAt(); // timestamp UNIX
Reutilizar un token entre requests (ej. en un job)
// Primera ejecución $defontana->auth()->login(); file_put_contents('token.json', json_encode([ 'access_token' => $defontana->auth()->getAccessToken(), 'expires_at' => $defontana->auth()->getExpiresAt(), ])); // Siguiente ejecución (otro proceso) $cached = json_decode(file_get_contents('token.json'), true); $ttl = max(0, $cached['expires_at'] - time()); $defontana->auth()->setToken($cached['access_token'], $ttl);
Importante: no persistas tokens en lugares accesibles públicamente. Trátalos como credenciales.
Login por email/password (alternativo)
$resp = $defontana->auth()->emailLogin('lord.neo.gerson@gmail.com', '***'); // devuelve la lista de empresas asociadas + authResult
DTOs tipados (opcional, recomendado)
Todos los endpoints que aceptan un body tienen un DTO tipado en Defontana\Sdk\Model\<Dominio>\<Clase>. Puedes pasar un DTO o un array — el SDK lo serializa solo:
use Defontana\Sdk\Model\Sale\SaleInputData; use Defontana\Sdk\Model\Sale\SaleInputData_DateInput; use Defontana\Sdk\Model\Sale\SaleInputData_DetailInput; $venta = new SaleInputData( documentType: '33', clientFile: '76123456-7', sellerFileId: 'V001', paymentCondition: 'CO', shopId: '001', emissionDate: new SaleInputData_DateInput(day: 16, month: 5, year: 2026), details: [ new SaleInputData_DetailInput(code: 'PROD001', count: 2, price: 50000), new SaleInputData_DetailInput(code: 'PROD002', count: 1, price: 12500), ], ); $resp = $defontana->sale()->saveSale($venta); echo "Folio asignado: {$resp['firstFolio']}\n";
Ventajas frente al array crudo:
- Autocompletado y type-hints en tu IDE.
- Detección temprana de typos en nombres de propiedades.
toArray()omite automáticamente campos null (no contamina el JSON enviado).fromArray()reconstruye DTOs anidados desde respuestas del API.
Los DTOs son mutables: puedes construirlos por partes y modificarlos antes de enviar.
$venta = new SaleInputData(documentType: '33', clientFile: '76123456-7'); $venta->emissionDate = new SaleInputData_DateInput(day: date('j'), month: date('n'), year: date('Y')); foreach ($carrito as $item) { $venta->details[] = new SaleInputData_DetailInput( code: $item->sku, count: $item->qty, price: $item->price, ); } $resp = $defontana->sale()->saveSale($venta);
Ejemplos por caso de uso
Crear una factura electrónica (con DTO)
use Defontana\Sdk\Model\Sale\SaleInputData; use Defontana\Sdk\Model\Sale\SaleInputData_DateInput; use Defontana\Sdk\Model\Sale\SaleInputData_DetailInput; $resp = $defontana->sale()->saveSale(new SaleInputData( documentType: '33', clientFile: '76123456-7', sellerFileId: 'V001', shopId: '001', paymentCondition: 'CO', emissionDate: new SaleInputData_DateInput(day: 16, month: 5, year: 2026), details: [ new SaleInputData_DetailInput(code: 'PROD001', count: 1, price: 50000), ], )); // $resp['firstFolio'] tiene el folio asignado
Equivalente con array crudo (sigue funcionando)
$resp = $defontana->sale()->saveSale([ 'documentType' => '33', 'clientFile' => '76123456-7', 'sellerFileId' => 'V001', 'shopId' => '001', 'paymentCondition' => 'CO', 'emissionDate' => ['day' => 16, 'month' => 5, 'year' => 2026], 'details' => [ ['code' => 'PROD001', 'count' => 1, 'price' => 50000], ], ]);
Listar ventas en un rango de fechas
$ventas = $defontana->sale()->getSalebyDate( initialDate: '2026-05-01', endingDate: '2026-05-16', itemsPerPage: 100, pageNumber: 1, );
Descargar PDF de un documento
$pdf = $defontana->sale()->getStandardPDFDocumentBase64( documentType: '33', folio: 1042, siiUnit: 'XX', ); file_put_contents('factura-1042.pdf', base64_decode($pdf['pdfBase64']));
Crear/actualizar un cliente
use Defontana\Sdk\Model\Sale\ClientInputData; $defontana->sale()->saveClient(new ClientInputData( legalCode: '76123456-7', name: 'Cliente Ejemplo SpA', giro: 'Servicios informáticos', address: 'Av. Providencia 1234', district: 'PROVIDENCIA', state: 'METROPOLITANA', ));
Inventario y stock
$stock = $defontana->sale()->getStorageStock( storageID: 'BOD01', itemsPerPage: 200, pageNumber: 1, );
Comprobante contable
$voucher = $defontana->accounting()->getVoucher( VoucherType: 'TRA', Number: 100, FiscalYear: 2026, );
Manejo de errores
Toda excepción del SDK extiende de Defontana\Sdk\Exception\DefontanaException. Casos específicos:
| Excepción | Cuándo se lanza |
|---|---|
AuthException |
Login fallido (credenciales inválidas, etc.) |
ApiException |
HTTP no-2xx, o success=false en el cuerpo de respuesta |
NetworkException |
Error de cURL (timeout, DNS, TLS, etc.) |
try { $defontana->sale()->saveSale(body: $payload); } catch (ApiException $e) { echo "Status: {$e->statusCode}\n"; echo "Mensaje: {$e->getMessage()}\n"; echo "Respuesta cruda: {$e->rawBody}\n"; print_r($e->response); }
Configuración avanzada
$config = new Config( client: 'CLIENTE', company: 'EMPRESA', user: 'USUARIO', password: '***', baseUrl: 'https://api.defontana.com', // por defecto timeout: 60, // segundos connectTimeout: 15, verifyTls: true, userAgent: 'mi-app/1.0', );
Estructura del proyecto
src/
├── Client.php ← punto de entrada (facade)
├── Config.php
├── Auth/
│ └── AuthManager.php ← maneja JWT y refresh
├── Http/
│ ├── HttpClient.php ← wrapper sobre cURL
│ └── HttpResponse.php
├── Exception/
│ ├── DefontanaException.php, ApiException.php, AuthException.php, NetworkException.php
├── Service/ ← 14 servicios generados desde Swagger + AuthService + AbstractService
│ └── ...
└── Model/ ← 222 DTOs tipados (uno por schema de request body)
├── AbstractModel.php ← toArray / fromArray
├── Accounting/ (13)
├── Dispatch/ (27)
├── Inventory/ (11)
├── Order/ (26)
├── Payment/ (1)
├── Pricing/ (7)
├── Purchase/ (12)
├── PurchaseOrder/ (16)
└── Sale/ (109)
Los servicios y los DTOs se generan a partir del Swagger oficial. Para regenerarlos cuando Defontana publica nuevos endpoints:
# 1. Actualizar el snapshot del swagger curl -sS https://api.defontana.com/swagger/v1/swagger.json -o tools/swagger.json # 2. Regenerar (en orden) node tools/parse_swagger.js # produce tools/endpoints.json node tools/analyze_schemas.js # produce tools/schemas_to_generate.json node tools/generate_dtos.js # reescribe src/Model/** node tools/generate_services.js # reescribe src/Service/*Service.php
Esto sobreescribe los archivos autogenerados (todo src/Model/** y los *Service.php excepto AuthService y AbstractService). Si añadiste métodos personalizados a alguno, revísalo antes de regenerar.
Licencia
MIT.
Disclaimer
Este SDK no es oficial ni está afiliado a Defontana. Se desarrolló a partir de la documentación pública de su Swagger.