PHP code example of schoolaid / fel

1. Go to this page and download the library: Download schoolaid/fel library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

schoolaid / fel example snippets


return [
    'provider' => env('FEL_PROVIDER', 'infile'),
    'username' => env('FEL_USERNAME'),
    'api_key' => env('FEL_LLAVE_FIRMA', env('FEL_KEY')),          // llave del firmador (viaja como llaveFirma)
    'signature_key' => env('FEL_LLAVE_API', env('FEL_PASSWORD')), // llave del API REST (viaja como llaveApi)

    'provider_config' => [
        'base_url' => env('FEL_BASE_URL', 'https://certificador.feel.com.gt/fel/procesounificado/transaccion/v2/xml'),
        'certify_url' => env('FEL_CERTIFY_URL'),
        'status_url' => env('FEL_STATUS_URL'),
        'cancel_url' => env('FEL_CANCEL_URL'),
        'timeout' => env('FEL_TIMEOUT', 30),
        'verify_ssl' => env('FEL_VERIFY_SSL', true),
        'identifier' => env('FEL_IDENTIFIER'), // id único por transacción — mejor setIdentifier() por documento
    ],
];

use Schoolaid\Fel\Config\FelConfig;

// Opción 3A (recomendada): constructor con nombres veraces
$config = FelConfig::forInfile(
    username: 'tu_usuario_infile',
    llaveFirma: 'tu_llave_de_firma',  // llave del firmador
    llaveApi: 'tu_llave_api',         // llave del API REST
    providerConfig: [
        'base_url' => 'https://certificador.feel.com.gt/fel/procesounificado/transaccion/v2/xml',
        'certify_url' => 'https://certificador.feel.com.gt/fel/procesounificado/transaccion/v2/xml',
        'cancel_url' => 'https://certificador.feel.com.gt/fel/procesounificado/transaccion/v2/xml',
        'identifier' => 'orden-0001'  // id único por transacción (opcional)
    ]
);

// Constructor histórico (nombres invertidos, ver nota de credenciales)
$config = new FelConfig(
    provider: 'infile',
    username: 'tu_usuario_infile',
    apiKey: 'tu_llave_de_firma',   // viaja como llaveFirma
    signatureKey: 'tu_llave_api',  // viaja como llaveApi
    providerConfig: [
        'base_url' => 'https://certificador.feel.com.gt/fel/procesounificado/transaccion/v2/xml',
        'certify_url' => 'https://certificador.feel.com.gt/fel/procesounificado/transaccion/v2/xml',
        'cancel_url' => 'https://certificador.feel.com.gt/fel/procesounificado/transaccion/v2/xml',
        'timeout' => 30,
        'verify_ssl' => true,
        'identifier' => 'orden-0001'  // id único por transacción (opcional)
    ]
);

// Opción 3B: Desde un array
$credenciales = [
    'provider' => 'infile',
    'username' => 'tu_usuario_infile',
    'api_key' => 'tu_llave_de_firma',   // viaja como llaveFirma
    'signature_key' => 'tu_llave_api',  // viaja como llaveApi
    'provider_config' => [
        'base_url' => 'https://certificador.feel.com.gt/fel/procesounificado/transaccion/v2/xml',
        'certify_url' => 'https://certificador.feel.com.gt/fel/procesounificado/transaccion/v2/xml',
        'cancel_url' => 'https://certificador.feel.com.gt/fel/procesounificado/transaccion/v2/xml',
        'timeout' => 30,
        'verify_ssl' => true,
        'identifier' => 'orden-0001' // id único por transacción
    ]
];

$config = FelConfig::fromArray($credenciales);

// Opción 3C: Con setters (útil para modificar configuración existente)
$config = new FelConfig();
$config->setProvider('infile')
    ->setUsername('tu_usuario_infile')
    ->setLlaveFirma('tu_llave_de_firma')  // llave del firmador
    ->setLlaveApi('tu_llave_api')         // llave del API REST
    ->setIdentifier('orden-0001')  // id único por transacción (control de duplicidad)
    ->setProviderConfig([
        'base_url' => 'https://certificador.feel.com.gt/fel/procesounificado/transaccion/v2/xml',
        'certify_url' => 'https://certificador.feel.com.gt/fel/procesounificado/transaccion/v2/xml',
        'cancel_url' => 'https://certificador.feel.com.gt/fel/procesounificado/transaccion/v2/xml',
        'timeout' => 30,
        'verify_ssl' => true,
    ]);

use Schoolaid\Fel\Config\FelConfig;

// Obtener credenciales de la base de datos
$empresa = Empresa::find($empresaId);

$config = new FelConfig(
    provider: 'infile',
    username: $empresa->fel_username,
    apiKey: $empresa->fel_api_key,
    signatureKey: $empresa->fel_signature_key,
    providerConfig: [
        'base_url' => 'https://certificador.feel.com.gt/fel/procesounificado/transaccion/v2/xml',
        'certify_url' => 'https://certificador.feel.com.gt/fel/procesounificado/transaccion/v2/xml',
        'cancel_url' => 'https://certificador.feel.com.gt/fel/procesounificado/transaccion/v2/xml',
        'timeout' => 30,
        'verify_ssl' => true,
        'identifier' => 'orden-' . $ordenId // id único por transacción, uno distinto por documento
    ]
);

// Usar la configuración para certificar
$certify = new FelCertify($invoice, $config);
$response = $certify->execute();

use Schoolaid\Fel\Config\FelConfig;

class FelService
{
    public function certifyForCompany(Invoice $invoice, Company $company)
    {
        // Crear configuración específica para esta empresa
        $config = new FelConfig(
            provider: 'infile',
            username: $company->fel_credentials['username'],
            apiKey: $company->fel_credentials['api_key'],
            signatureKey: $company->fel_credentials['signature_key'],
            providerConfig: [
                'base_url' => 'https://certificador.feel.com.gt/fel/procesounificado/transaccion/v2/xml',
                'certify_url' => 'https://certificador.feel.com.gt/fel/procesounificado/transaccion/v2/xml',
                'cancel_url' => 'https://certificador.feel.com.gt/fel/procesounificado/transaccion/v2/xml',
                'timeout' => 30,
                'verify_ssl' => true,
                'identifier' => 'orden-' . $orderId // id único por transacción, uno distinto por documento
            ]
        );

        $certify = new FelCertify($invoice, $config);
        return $certify->execute();
    }
}

// Uso
$felService = new FelService();
$response = $felService->certifyForCompany($invoice, $empresa);

use Schoolaid\Fel\Config\FelConfig;
use Schoolaid\Fel\Certification\FelCertificationService;

try {
    // Desde .env
    $config = FelConfig::fromConfig();

    // O directamente
    $config = new FelConfig(
        provider: 'infile',
        username: 'tu_usuario',
        apiKey: 'tu_api_key',
        signatureKey: 'tu_signature_key',
        providerConfig: [
            'base_url' => 'https://certificador.feel.com.gt/fel/procesounificado/transaccion/v2/xml',
            'timeout' => 30,
            'verify_ssl' => true
        ]
    );

    $service = new FelCertificationService($config);

    // Si no hay excepciones, la configuración es válida
    echo "Configuración correcta";
} catch (\Exception $e) {
    echo "Error de configuración: " . $e->getMessage();
}

use Schoolaid\Fel\Config\FelConfig;
use Schoolaid\Fel\Actions\FelCertify;
use Schoolaid\Fel\Models\Invoice;
use Schoolaid\Fel\Models\FelIssuer;
use Schoolaid\Fel\Models\FelReceiver;
use Schoolaid\Fel\Models\FelAddress;
use Schoolaid\Fel\Models\FelItems;
use Schoolaid\Fel\Models\FelItem;
use Schoolaid\Fel\Models\FelPhrases;
use Schoolaid\Fel\Models\FelPhrase;
use Schoolaid\Fel\Models\FelTotals;
use Schoolaid\Fel\Enums\DocumentTypeEnum;
use Schoolaid\Fel\Enums\CurrencyEnum;
use Schoolaid\Fel\Enums\IVAAffiliationTypeEnum;

// 1. Configurar credenciales directamente (sin .env)
$config = new FelConfig(
    provider: 'infile',
    username: 'mi_usuario_infile',
    apiKey: 'mi_api_key',
    signatureKey: 'mi_signature_key',
    providerConfig: [
        'base_url' => 'https://certificador.feel.com.gt/fel/procesounificado/transaccion/v2/xml',
        'certify_url' => 'https://certificador.feel.com.gt/fel/procesounificado/transaccion/v2/xml',
        'cancel_url' => 'https://certificador.feel.com.gt/fel/procesounificado/transaccion/v2/xml',
        'timeout' => 30,
        'verify_ssl' => true,
        'identifier' => 'orden-0001' // id único por transacción
    ]
);

// 2. Crear la factura
$issuer = new FelIssuer(
    '[email protected]',
    '1',
    '12345678',
    'Mi Empresa S.A.',
    IVAAffiliationTypeEnum::General,
    'Mi Empresa',
    new FelAddress('Avenida Reforma 1-1 Zona 10', '01010', 'Guatemala', 'Guatemala', 'GT')
);

$receiver = new FelReceiver(
    'CF',
    null,
    'Consumidor Final',
    new FelAddress('Ciudad', '01001', 'Guatemala', 'Guatemala', 'GT')
);

$items = new FelItems([
    new FelItem(1, 'B', 100.0, 'UND', 'Producto de prueba', 100.0, 1, 0.0, [], 100.0)
]);

$invoice = new Invoice(
    DocumentTypeEnum::LOCAL_INVOICE,
    now()->format('Y-m-d\TH:i:s'),
    CurrencyEnum::QUETZAL,
    $issuer,
    $receiver,
    new FelPhrases([new FelPhrase(1, 1)]),
    $items,
    new FelTotals(grandTotal: 100.0)
);

// 3. Certificar usando la configuración directa
$certify = new FelCertify($invoice, $config);
$response = $certify->execute();

// 4. Procesar respuesta
if ($response->isSuccessful()) {
    echo "Factura certificada: " . $response->getUuid();
} else {
    echo "Error: " . implode(', ', $response->getErrors());
}

use Schoolaid\Fel\Enums\CurrencyEnum;
use Schoolaid\Fel\Enums\DocumentTypeEnum;
use Schoolaid\Fel\Enums\IVAAffiliationTypeEnum;
use Schoolaid\Fel\Models\FelAddenda;
use Schoolaid\Fel\Models\FelAddress;
use Schoolaid\Fel\Models\FelIssuer;
use Schoolaid\Fel\Models\FelItem;
use Schoolaid\Fel\Models\FelItems;
use Schoolaid\Fel\Models\FelPhrase;
use Schoolaid\Fel\Models\FelPhrases;
use Schoolaid\Fel\Models\FelReceiver;
use Schoolaid\Fel\Models\FelTotals;
use Schoolaid\Fel\Models\Invoice;
use Schoolaid\Fel\Actions\FelGenerate;
use Schoolaid\Fel\Actions\FelCertify;
use Schoolaid\Fel\Config\FelConfig;

// Configurar datos básicos
$issuerAddress = new FelAddress(
    'Avenida Reforma 15-85 Zona 10, Edificio Torre Internacional Nivel 11',
    '01001',
    'Guatemala',
    'Guatemala',
    'GT'
);

$issuer = new FelIssuer(
    '[email protected]',
    '1',
    '12345678',  // NIT del emisor
    'Empresa, S.A.',
    IVAAffiliationTypeEnum::General,
    'Mi Empresa',
    $issuerAddress
);

$receiverAddress = new FelAddress(
    'Avenida Las Américas 7-62 Zona 13',
    '01013',
    'Guatemala',
    'Guatemala',
    'GT'
);

$receiver = new FelReceiver(
    '87654321',  // NIT del receptor (o CF para Consumidor Final)
    '[email protected]',
    'Cliente Frecuente, S.A.',
    $receiverAddress
);

// Frases requeridas para FACT
$phrases = new FelPhrases([
    new FelPhrase(1, 1)  // Frase 1 y Escenario 1 - Afecta IVA
]);

// Productos/servicios
$items = new FelItems([
    new FelItem(
        1,              // Número de línea
        'B',            // Bien (B) o Servicio (S)
        500.0,          // Precio unitario sin IVA
        'UND',          // Unidad de medida
        'Computadora portátil HP Probook 450 G8', // Descripción
        500.0,          // Precio (sin impuestos)
        1,              // Cantidad
        0.0,            // Descuento
        [],             // Los impuestos se calcularán automáticamente
        500.0           // Total de línea
    ),
    new FelItem(
        2,              // Número de línea
        'S',            // Bien (B) o Servicio (S)
        200.0,          // Precio unitario sin IVA
        'UND',          // Unidad de medida
        'Servicio de instalación y configuración', // Descripción
        200.0,          // Precio (sin impuestos)
        1,              // Cantidad
        0.0,            // Descuento
        [],             // Los impuestos se calcularán automáticamente
        200.0           // Total de línea
    )
]);

// Totales con IVA
$totals = new FelTotals(
    grandTotal: 700.0   // Total incluyendo impuestos (los demás valores se calcularán automáticamente)
);

// Addendas (información adicional)
$addendas = [
    new FelAddenda(
        'http://www.sat.gob.gt/face2/ComplementoFacturaEspecial/0.1.0',
        'ReferenciaInterna',
        'Orden #FT-2023-1234'
    ),
    new FelAddenda(
        'http://www.sat.gob.gt/face2/ComplementoFacturaEspecial/0.1.0',
        'DatosCliente',
        'Proyecto: Implementación ERP'
    )
];

// Crear la factura (FACT)
$invoice = new Invoice(
    DocumentTypeEnum::LOCAL_INVOICE,  // FACT - Factura local
    now()->format('Y-m-d\TH:i:s'),
    CurrencyEnum::QUETZAL,
    $issuer,
    $receiver,
    $phrases,
    $items,
    $totals,
    $addendas
);

// Certificar
$config = FelConfig::fromConfig();
$certify = new FelCertify($invoice, $config);
$response = $certify->execute();

// Procesar respuesta
if ($response->isSuccessful()) {
    $uuid = $response->getUuid();
    $serie = $response->getSerial();
    $numero = $response->getNumber();
    $fecha = $response->getCertificationDate();
    $xmlCertificado = $response->getCertifiedXml();

    // Guardar los datos en tu base de datos
    echo "Factura certificada exitosamente: {$serie}-{$numero}";
} else {
    $errores = $response->getErrors();
    echo "Error al certificar: " . implode(', ', $errores);
}

// El receptor debe tener 'CF' como ID
$receiver = new FelReceiver(
    'CF',  // Consumidor Final
    null,  // Email opcional
    'Consumidor Final',
    $receiverAddress
);

// El resto es igual a una factura normal
$invoice = new Invoice(
    DocumentTypeEnum::LOCAL_INVOICE,
    now()->format('Y-m-d\TH:i:s'),
    CurrencyEnum::QUETZAL,
    $issuer,
    $receiver,
    $phrases,
    $items,
    $totals
);

use Schoolaid\Fel\Enums\DocumentTypeEnum;

// Receptor en el extranjero
$receiver = new FelReceiver(
    'EXPORTACION',  // ID para exportaciones
    '[email protected]',
    'Cliente Internacional Inc.',
    $receiverAddress
);

// Frases para exportación (sin IVA)
$phrases = new FelPhrases([
    new FelPhrase(2, 1)  // Frase 2 - Exento de IVA
]);

// Items (el precio es el total, no se calcula IVA)
$items = new FelItems([
    new FelItem(
        1,
        'B',
        1000.0,  // Precio = Total (sin IVA)
        'UND',
        'Producto para exportación',
        1000.0,
        1,
        0.0,
        [],
        1000.0
    )
]);

$totals = new FelTotals(grandTotal: 1000.0);

// Crear factura de exportación
$invoice = new Invoice(
    DocumentTypeEnum::EXPORT_INVOICE,  // FEXP
    now()->format('Y-m-d\TH:i:s'),
    CurrencyEnum::DOLLAR,  // Puede ser USD para exportaciones
    $issuer,
    $receiver,
    $phrases,
    $items,
    $totals
);

use Schoolaid\Fel\Enums\DocumentTypeEnum;
use Schoolaid\Fel\Models\FelReferenceNote;

// Referencia a la factura original (obligatoria). Los cinco datos vienen del
// CertificationResponse con que se certificó el DTE origen: getUuid(),
// getSeries() y getNumber() (serie y número NO se derivan del UUID; el
// sandbox de INFILE, por ejemplo, emite la serie "**PRUEBAS**").
$reference = new FelReferenceNote(
    '12345678-1234-1234-1234-123456789012', // UUID (autorización) del DTE origen
    '2025-03-15',                           // Fecha de emisión del DTE origen
    'Devolución de mercadería',             // Motivo del ajuste
    'A1B2C3D4',                             // Serie del DTE origen (getSeries)
    '1234567890'                            // Número del DTE origen (getNumber)
);

$invoice = new Invoice(
    DocumentTypeEnum::CREDIT_NOTE,  // NCRE (para NDEB usa DEBIT_NOTE)
    now()->format('Y-m-d\TH:i:s'),
    CurrencyEnum::QUETZAL,          // Debe ser la misma moneda del DTE origen
    $issuer,                        // Mismo NIT emisor que el DTE origen
    $receiver,                      // Mismo receptor que el DTE origen
    $phrases,
    $items,                         // Items a acreditar (montos positivos)
    $totals
);
$invoice->setReferenceNote($reference);

$reference = new FelReferenceNote(
    '1364585227',        // Número de resolución de autorización
    '2018-05-20',
    'Anulación parcial',
    '5AAE0F7A',          // Serie del documento origen
    '1364585227',        // Número del documento origen
    oldRegime: true      // Emite RegimenAntiguo="Antiguo"
);

use Schoolaid\Fel\Enums\DocumentTypeEnum;
use Schoolaid\Fel\Enums\IVAAffiliationTypeEnum;

// Emisor pequeño contribuyente
$issuer = new FelIssuer(
    '[email protected]',
    '1',
    '12345678',
    'Pequeño Negocio',
    IVAAffiliationTypeEnum::PEQ,  // Pequeño contribuyente
    'Mi Pequeño Negocio',
    $issuerAddress
);

// Frases para pequeño contribuyente
$phrases = new FelPhrases([
    new FelPhrase(4, 1)  // Frase específica para pequeños contribuyentes
]);

$invoice = new Invoice(
    DocumentTypeEnum::SMALL_TAXPAYER_INVOICE,  // FPEQ
    now()->format('Y-m-d\TH:i:s'),
    CurrencyEnum::QUETZAL,
    $issuer,
    $receiver,
    $phrases,
    $items,
    $totals
);

use Schoolaid\Fel\Enums\DocumentTypeEnum;

$phrases = new FelPhrases([
    new FelPhrase(3, 1)  // Frase para donaciones
]);

$invoice = new Invoice(
    DocumentTypeEnum::DONATION_RECEIPT,  // RDON
    now()->format('Y-m-d\TH:i:s'),
    CurrencyEnum::QUETZAL,
    $issuer,
    $receiver,
    $phrases,
    $items,
    $totals
);

use Schoolaid\Fel\Actions\FelCancel;
use Schoolaid\Fel\Models\Cancellation;

$cancellation = new Cancellation(
    uuid: '12345678-1234-1234-1234-123456789012',  // UUID de la factura a cancelar
    nitIssuer: '12345678',  // NIT del emisor
    idReceiver: '87654321',  // NIT o CF del receptor
    reason: 'Anulación por devolución de mercadería',
    dateTime: now()->format('Y-m-d\TH:i:s')
);

$config = FelConfig::fromConfig();
$cancelAction = new FelCancel($cancellation, $config);
$response = $cancelAction->execute();

if ($response->isSuccessful()) {
    echo "Factura cancelada exitosamente";
} else {
    echo "Error: " . implode(', ', $response->getErrors());
}

use Schoolaid\Fel\Actions\FelGenerate;

$generate = new FelGenerate($invoice);
$xml = $generate->generateXml();

// Guardar el XML o procesarlo según necesites
file_put_contents('factura.xml', $xml);

use Schoolaid\Fel\Certification\Exceptions\CertificationException;
use Schoolaid\Fel\Certification\Exceptions\AuthenticationException;

try {
    $certify = new FelCertify($invoice, $config);
    $response = $certify->execute();

    if ($response->isSuccessful()) {
        // Éxito
        $uuid = $response->getUuid();
    } else {
        // Errores de validación de INFILE
        foreach ($response->getErrors() as $error) {
            \Log::error("Error FEL: {$error}");
        }
    }
} catch (AuthenticationException $e) {
    // Error de autenticación con INFILE
    \Log::error("Error de autenticación: " . $e->getMessage());
} catch (CertificationException $e) {
    // Otros errores de certificación
    \Log::error("Error de certificación: " . $e->getMessage());
} catch (\Exception $e) {
    // Errores generales
    \Log::error("Error general: " . $e->getMessage());
}

// Frase 1: Sujeto a pagos trimestrales de IVA
new FelPhrase(1, 1)

// Frase 2: Exento de IVA (exportaciones)
new FelPhrase(2, 1)

// Frase 3: Donaciones
new FelPhrase(3, 1)

// Frase 4: Pequeño contribuyente
new FelPhrase(4, 1)
bash
php artisan vendor:publish --provider="Schoolaid\Fel\FelServiceProvider"