SDK PHP no oficial para el API de Defontana (ERP). Cubre Sales, Purchase, Inventory, Accounting, Order, PurchaseOrder y más.

Maintainers

Package info

github.com/LordNeoZ/defontana-sdk-php

pkg:composer/defontana/sdk

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v0.1.0 2026-06-04 13:09 UTC

This package is auto-updated.

Last update: 2026-06-04 13:16:35 UTC


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-curl y ext-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.